Javascript Symbol 隐匿的未来之星
ES6中基礎類型增加到了7種,比上一個版本多了一個Symbol,貌似出現了很長時間,但卻因沒有使用場景,一直當作一個概念層來理解它,我想,用它的最好的方式,還是要主動的去深入了解它吧,所以我從基礎部分和總結的實用場景來分析這個特性。已經了解使用方法或者時間緊迫者可以從實用場景一節開始閱讀
base
首先,它給我的第一感覺就是ES6做出了很多釋放語言特性方面的改變,它能讓我們更加了解語言內部機制,Symbol以對象的鍵值定義,比如
let key = Symbol('test'); let obj = {};obj[key] = 'alone';obj[key]; // "alone"Symbol正如其名,表示一個唯一的標示,以屬性的方式存在于對象當中,它接收一個參數,沒有實質的作用,只是為了做一個描述。以上我們通過直接量的方式來定義它,并且取值時,也需要使用key進行讀取,如果出現跨作用域的情況,是不是就不能獲取了?
function sent(key){accept({[key]:"2018"}) } function accept(obj) {obj[???] //我怎么拌? }以上兩個作用域中,如果不把key傳遞過來,是無法讀取的,一個屬性還好,但是如果多了,那么靠參數傳遞key是不現實的. 在這種情況下,我們可以使用 Symbol.for 來為它再添加一個標示,它接受一個參數String{key}。通常,它做為一個偏功能性的標記來表示,在全劇中它是唯一的。
function sent(key){return accept({[key]:"2018"},key) } function accept(obj,key) {console.log(Symbol.keyFor(key)) //CURRENT_YEARreturn obj[Symbol.for(Symbol.keyFor(key))] //CURRENT_YEAR } sent(Symbol.for('CURRENT_YEAR'))并且使用 Symbol.for 來生成,會在存入當前全局上下文中一個<List>結構中,我們稱它為GlobalSymbolRegistry , 顧名思義,它是全局的,所以使用key時我們需要謹慎,尤其是在大型項目中。
需要還注意以下幾點:
在使用 Symbol 做key值時,它經歷了以下步驟
所以這樣寫也是可以的,但是貌似沒有什么意義
var n = 1; var key = Symbol('numer') n[key] = ‘Symbol Number’n[key]的時候把n隱式轉換成封裝對象,并為他添加Symbol,但并沒有辦法去通過封裝對象回訪這個Symbol
除了單純的用key以外,在Symbol類下還有一些有意思的方法,following :
iterator
為指向對象添加 iterator 接口,比如使用數組解構或者使用for of,它接受一個generator函數
class IteratorExec {constructor(){ this.count = 1 }*[Symbol.iterator] = function* (){yield this.count++;yield this.count++;yield this.count++;}} let obj = new IteratorExec() [...obj] //[1,2,3]通過添加iterator使用數據解構,還可以使用for of
let values = [];for (let value of obj) { values.push(value) }values; //[1,2,3]注:ES6中Map,Set,數組和添加了Iterator接口的對象,擁有Iterator接口.
asyncIterator
這不是ES6中的特性,貌似放到了ES7中,可以提前意淫一下如下代碼:
for await (const line of readLines(filePath)) {console.log(line); }toPrimitive
在對對象類型進行轉換時,會進行一次 toPrimitive,利用這個Symbol可以改變目標對象的轉換規則,改變了以前的 "[object Object]"的固定形式
let obj = {[Symbol.toPrimitive](hint){switch(hint){case 'number': return 5;case 'string': return 'string';case 'default': return 'default' }} } obj+11 // 'default11' obj*2 // 10這里需要注意+ Number操作是不屬于 'number' 的,其他正常,這樣就可以定義轉對象類型的轉換規則了。
toStringTag
在javascript一切皆為對象,而在每個對象中,都會有一個內部屬性[[Class]]表示其對象類型,這在Symbol.toStringTag,中是可以修改的,也就是說 '[object Object]' 后邊的字符串是可自定義的
let obj = {[Symbol.toStringTag]:'custom' } Object.prototype.toString(obj); // [object Object] obj.toString(); //[object custom]通常我們使用Object.prototype.toString讀取對象屬性,正是因為向后兼容,規范在對象自身的toString上實現了這種特性,而老式方法依舊使用。但是我們可以使用以下方式:
obj = {[Symbol.toStringTag]:'custom'get [Symbol.toStringTag](){return 'custom'} } Object.prototype.toString.call(obj)我們把obj傳入執行toString,可以達到這種效果,可以預想es6中,Object.toString是受到上下文的影響的. 顯然,我們上面的兩個例子都是獲取的Object.prototype.toString 兩者有很大區別,只有它才能準確轉換,如果你的toString不全等于它,那是無法轉換的,比如
var n = new Number();n[Symbol.toStringTag] = 123;n.toString(); // “0”太幼稚了,太無聊了?,Number私有的toString是直接把[[PrimitiveValue]]轉換成了字符串,這里大家要千萬留心,不要誤認為所有的對象添加了Symbol.toStringTag都可以改變,如果當前對象不是純對象,那么你可以為此對象添加一個 getter 返回對應的類型,這樣外部在使用Object...call的時,會獲取自定的類型。所以,這需要外部配合使用,你添加getter,人家不call你也是沒辦法的。
另外Symbol暴露了幾種為原生對象定義了一些類型,比如
Math.toString(); //[object Math]其他類型有 JSON, Promise, Map, TypedArray, DataView, ArrayBuffer, Genterator等等
unscopeables
const object1 = {property1: 42 };object1[Symbol.unscopables] = {property1: true };with (object1) {console.log(property1); }這個功能我感覺可用性為0,基本不用,with就是據對禁止的.
hasInstance
對于 instance運算符,為此操作添加一個鉤子,第一參數是instance的左值,我們可以返回true|false來定義運算符的返回值
var obj1 = {[Symbol.hasInstance](instance){return Array.isArray(Array)} } class Array1 {static [Symbol.hasInstance](instance) {return Array.isArray(instance);} } [] instance obj1 //true console.log([] instanceof Array1); //trueisConcatSpreadable
表示[].concat是否可以展開,默認是true.
let arr = [1,2]; arr.concat([3,4],5) //[1,2,3,4,5]arr[Symbol.isConcatSpreadable] = false; arr.concat([3,4],5) //[[1,2],3,4,5]// 也可以把[3,4]提出來處理 let arr2 = [3,4] arr2[Symbol.isConcatSpreadable] = false; arr.concat(arr2,5); //[[1,2],[3,4],5]只有在數組中這個symbol屬性為false,concat操作時,就不會去解構。那么是不是意味著屬性設置為ture,沒有意義了?對于數組來說是的,因為它默認就是true,可是對于類數組對象,它還有一個小功能:
// (續) arr.concat({length:2,0:3,1:4,[Symbol.isConcatSpreadable]:true}) //[1,2,3,4]match & replace & split & search
一些字符串的操作方法,一起都說了,大概都一個意思,就是接受一個對象,然后實現一個鉤子處理的函數,并返回其處理結果,它們都是可以接收正則的方法,在ES6之前,如果我們需要對字符串有比較復雜的操作基本上都是在方法外部的,必
class MyMatch {[Symbol.match](string){return string.indexOf('world') } }'hello world'.match(new MyMatch()); //6class MyReplace{[Symbol.replace](string) {return 'def'} }'abcdef'.replace(new MyReplace(),'xxx'); //'abcxxx'class mySplit {[Symbol.split](val){return val.split('-');} }"123-123-123".split(new mySplit()); //['123','123','123']class MySearch {constructor(value) {this.value = value;}[Symbol.search](string) {return string.indexOf(this.value);} } var fooSearch = 'foobar'.search(new MySearch('foo')); //0 var barSearch = 'foobar'.search(new MySearch('bar')); //3 var bazSearch = 'foobar'.search(new MySearch('baz')); //-1practice
總結
Symbol更多的是在使用和語言本身層面暴露更多的使用方式和特性(on Object type),是的,它只以key的方式存在Object當中,在一切皆為對象中,它為 Next ECMScript Standard 提供了更多的可能性擴展性,這也是ES6中做的最大改變方面之一,雖不常用但我們還是要總結學習一下,以便在極端情況下應變自如,如果有什么文章中沒有涉及到的點,歡迎補充! 注: 尤其是使用場景方面
總結
以上是生活随笔為你收集整理的Javascript Symbol 隐匿的未来之星的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C99灵活数组
- 下一篇: java基础-迭代器(Iterator)