Symbol 类型 的简单理解和应用
Symbol 的概念
symbol?是一種基本數據類型 (primitive data type)。Symbol()函數會返回symbol類型的值,該類型具有靜態屬性和靜態方法。它的靜態屬性會暴露幾個內建的成員對象;它的靜態方法會暴露全局的symbol注冊,且類似于內建對象類,但作為構造函數來說它并不完整,因為它不支持語法:"new Symbol()"。
1? 每個從Symbol()返回的symbol值都是唯一的。一個symbol值能作為對象屬性的標識符;這是該數據類型僅有的目的
? ?也就是說每一個Symbol的值都是不同的?
?
const symbol1 = Symbol(); const symbol2 = Symbol('foo'); const symbol3 = Symbol('foo');console.log(typeof symbol1); // expected output: "symbol"console.log(symbol2 === 42); // expected output: falseconsole.log(symbol3.toString()); // expected output: "Symbol(foo)"console.log(Symbol('foo') === Symbol('foo')); // expected output: falselet copyValue1 = 2 let copyValue2 = 2 console.log(copyValue1,copyValue2,Symbol('foo'),Symbol('foo'),copyValue1==copyValue2,symbol2==symbol3) //2 2 Symbol(foo) Symbol(foo) true false上面的代碼創建了三個新的symbol類型。 注意,Symbol("foo")?不會強制將字符串 “foo” 轉換成symbol類型。它每次都會創建一個新的 symbol類型:
?
全局共享的 Symbol
上面使用Symbol()?函數的語法,不會在你的整個代碼庫中創建一個可用的全局的symbol類型。 要創建跨文件可用的symbol,甚至跨域(每個都有它自己的全局作用域)?, 使用?Symbol.for()?方法和 ?Symbol.keyFor()?方法從全局的symbol注冊表設置和取得symbol。
在對象中查找 Symbol 屬性
Object.getOwnPropertySymbols()?方法讓你在查找一個給定對象的符號屬性時返回一個symbol類型的數組。注意,每個初始化的對象都是沒有自己的symbol屬性的,因此這個數組可能為空,除非你已經在對象上設置了symbol屬性。
Symbol 包裝器對象作為屬性的鍵
當一個 Symbol 包裝器對象作為一個屬性的鍵時,這個對象將被強制轉換為它包裝過的 symbol 值:
var sym = Symbol("foo"); var obj = {[sym]: 1}; obj[sym]; // 1 obj[Object(sym)]; // still 1?
對 symbol 使用 typeof 運算符
?typeof運算符能幫助你識別 symbol 類型
typeof Symbol() === 'symbol' typeof Symbol('foo') === 'symbol' typeof Symbol.iterator === 'symbol'Symbol 類型轉換
當使用 symbol 值進行類型轉換時需要注意一些事情:
- 嘗試將一個 symbol 值轉換為一個 number 值時,會拋出一個?TypeError?錯誤? (e.g.?+sym?or?sym | 0).
- 使用寬松相等時,?Object(sym) == sym?returns?true.
- 這會阻止你從一個 symbol 值隱式地創建一個新的 string 類型的屬性名。例如,Symbol("foo") + "bar" 將拋出一個?TypeError?(can't convert symbol to string).
- "safer"?String(sym)?conversion?的作用會像symbol類型調用?Symbol.prototype.toString()?一樣,但是注意?new String(sym)?將拋出異常。
Symbols 與?for...in?迭代
Symbols 在?for...in?迭代中不可枚舉。另外,Object.getOwnPropertyNames()?不會返回 symbol 對象的屬性,但是你能使用?Object.getOwnPropertySymbols()?得到它們。
var obj = {};obj[Symbol("a")] = "a"; obj[Symbol.for("b")] = "b"; obj["c"] = "c"; obj.d = "d";for (var i in obj) {console.log(i); // logs "c" and "d" }Symbols 與?JSON.stringify()
當使用 JSON.stringify() 時,以 symbol 值作為鍵的屬性會被完全忽略:
JSON.stringify({[Symbol("foo")]: "foo"}); // '{}'更多細節,請看?JSON.stringify()。
?
應用場景1:使用Symbol來作為對象屬性名(key)
在這之前,我們通常定義或訪問對象的屬性時都是使用字符串,比如下面的代碼:
let obj = {abc: 123,"hello": "world" }obj["abc"] // 123 obj["hello"] // 'world'而現在,Symbol可同樣用于對象屬性的定義和訪問:
? const PROP_NAME = Symbol() const PROP_AGE = Symbol()let obj = {[PROP_NAME]: "一斤代碼" } obj[PROP_AGE] = 18obj[PROP_NAME] // '一斤代碼' obj[PROP_AGE] // 18隨之而來的是另一個非常值得注意的問題:就是當使用了Symbol作為對象的屬性key后,在對該對象進行key的枚舉時,會有什么不同?在實際應用中,我們經常會需要使用Object.keys()或者for...in來枚舉對象的屬性名,那在這方面,Symbol類型的key表現的會有什么不同之處呢?來看以下示例代碼:
? let obj = {[Symbol('name')]: '一斤代碼',age: 18,title: 'Engineer' }Object.keys(obj) // ['age', 'title']for (let p in obj) {console.log(p) // 分別會輸出:'age' 和 'title' }Object.getOwnPropertyNames(obj) // ['age', 'title']由上代碼可知,Symbol類型的key是不能通過Object.keys()或者for...in來枚舉的,它未被包含在對象自身的屬性名集合(property names)之中。所以,利用該特性,我們可以把一些不需要對外操作和訪問的屬性使用Symbol來定義。
也正因為這樣一個特性,當使用JSON.stringify()將對象轉換成JSON字符串的時候,Symbol屬性也會被排除在輸出內容之外:
JSON.stringify(obj) // {"age":18,"title":"Engineer"}我們可以利用這一特點來更好的設計我們的數據對象,讓“對內操作”和“對外選擇性輸出”變得更加優雅。
然而,這樣的話,我們就沒辦法獲取以Symbol方式定義的對象屬性了么?非也。還是會有一些專門針對Symbol的API,比如:
? // 使用Object的API Object.getOwnPropertySymbols(obj) // [Symbol(name)]// 使用新增的反射API Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']應用場景2:使用Symbol來替代常量
先來看一下下面的代碼,是不是在你的代碼里經常會出現?
? const TYPE_AUDIO = 'AUDIO' const TYPE_VIDEO = 'VIDEO' const TYPE_IMAGE = 'IMAGE'function handleFileResource(resource) {switch(resource.type) {case TYPE_AUDIO:playAudio(resource)breakcase TYPE_VIDEO:playVideo(resource)breakcase TYPE_IMAGE:previewImage(resource)breakdefault:throw new Error('Unknown type of resource')} }如上面的代碼中那樣,我們經常定義一組常量來代表一種業務邏輯下的幾個不同類型,我們通常希望這幾個常量之間是唯一的關系,為了保證這一點,我們需要為常量賦一個唯一的值(比如這里的'AUDIO'、'VIDEO'、 'IMAGE'),常量少的時候還算好,但是常量一多,你可能還得花點腦子好好為他們取個好點的名字。
現在有了Symbol,我們大可不必這么麻煩了:
?const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()
這樣定義,直接就保證了三個常量的值是唯一的了!是不是挺方便的呢。
應用場景3:使用Symbol定義類的私有屬性/方法
我們知道在JavaScript中,是沒有如Java等面向對象語言的訪問控制關鍵字private的,類上所有定義的屬性或方法都是可公開訪問的。因此這對我們進行API的設計時造成了一些困擾。
而有了Symbol以及模塊化機制,類的私有屬性和方法才變成可能。例如:
- 在文件 a.js中
- 在文件 b.js 中
由于Symbol常量PASSWORD被定義在a.js所在的模塊中,外面的模塊獲取不到這個Symbol,也不可能再創建一個一模一樣的Symbol出來(因為Symbol是唯一的),因此這個PASSWORD的Symbol只能被限制在a.js內部使用,所以使用它來定義的類屬性是沒有辦法被模塊外訪問到的,達到了一個私有化的效果。
?
?
參考文檔
【1】https://blog.csdn.net/weixin_33711641/article/details/89659385?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-5.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-5.control
【2】https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol#規范?
總結
以上是生活随笔為你收集整理的Symbol 类型 的简单理解和应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 湖中剑 GitHub周刊 #10 | 开
- 下一篇: 软件开发工具【四】 之 软件开发工具的技