es6-Symbol

 

es5定義屬性名只能使用字符串,這就可能致使一個問題,就是你定義的屬性名跟別人定義的屬性名衝突。爲了解決這個問題,es6引入了一個新的原始數據類型Symbol。Symbol的值是經過Symbol函數生成的,如今的屬性名能夠有兩種類型,一種是字符串,另外一種就是Symbol類型,凡是Symbol類型的屬性名就是獨一無二的,不會與其它任何屬性名衝入。html

 

Symbol概述es6

定義一個Symbol類型的變量看看編程

let s = Symbol();
typeof s;    // "symbol"

嗯,變量s果真是Symbol類型,不是字符串了。數組

Symbol是一種原始數據類型,不是對象,因此Symbol不能new(會報錯),Symbol不能添加屬性和方法。基本上,它是一種相似於字符串的數據類型。promise

Symbol函數能夠接受一個字符串做爲參數,表示對 Symbol 實例的描述。
  let s1 = Symbol('foo');函數

let s2 = Symbol('bar'); flex

  s1;    //Symbol(foo)this

s2;    //Symbol(bar) es5

let s3 = Symbol('foo');spa

s1 和 s3是不相等的兩個值。

Symbol還有如下一些特徵,Symbol能夠顯示轉爲字符串 

s1.toString();    // "Symbol(foo)"

若是 Symbol 的參數是一個對象,就會調用該對象的toString方法,將其轉爲字符串,而後才生成一個 Symbol 值。

var a = {};

var b = Symbol(a);

  b;   // "Symbol([object Object])"

a['toString'] = function () { return 'a symbol' } 

  var c = Symbol(a);

  c;  // "Symbol(a symbol)"

也能夠轉爲布爾類型

 Boolean(s1);    // true

可是不能轉爲數字,也不能與其它數據類型參與計算,會報錯。

 

獲取Symbol的描述,能夠經過 Symbol.prototype提供的description屬性獲取

s1.description    //"foo"

------------------------------------

做爲屬性名的Symbol 

經過方括號結構和Object.defineProperty將對象的屬性名指定爲一個Symbol值。

var mySymbol = Symbol(); 

//第一種寫法

var a = {};

a[mySymbol] = 'hello'; 

//第二種寫法

var a = {

[mySymbol]: 'hello'

};

//第三種寫法

var a = {};

 Object.defineProperty(a, mySymbol, {value: 'hello'});

//注意,Symbol值做爲對象屬性名時不能用點運算符。

使用Symbol做爲屬性名不會出現同名的屬性

 1.  let obj = { 

  type: 'PC',        //小A定義的

type: 'NEW'      //小B定義的,把小A的覆蓋了

 

2.     const type = 'TYPE';    //小A定義的

const type1 = 'TYPE';    //小B定義的

  let obj = { 

  [type]: 'PC',       

[type1]: 'NEW'     //把小A的覆蓋了

}

 

3.     使用Symbol不會被覆蓋

const type = Symbol('type');    //小A定義的

const type1 =  Symbol('type');    //小B定義的

  let obj = { 

  [type]: 'PC',       

[type1]: 'NEW'     

}

------------------------------------

 消除魔術字符串

魔術字符串指的是,在代碼之中屢次出現、與代碼造成強耦合的某一個具體的字符串或者數值。風格良好的代碼,應該儘可能消除魔術字符串,改由含義清晰的變量代替。

function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case 'Triangle': // 魔術字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }
  return area;
}
getArea('Triangle', { width: 100, height: 100 }); // 魔術字符串


'Triangle'就是一個魔術字符串,它出現屢次,不利於維護,因此咱們要把他定義成一個變量或者屬性,消除強耦合。


const shapeType = {

triangle:Symbol('triangle')            //這裏若是triangle的值是什麼並不重要,只是一個標識,不會與業務耦合

  //triangle: 'Triangle'                       //若是有的同窗不一樣話,沒有這樣調用getArea(shapeType.triangle, { width: 100, height: 100 });   而是getArea(''Triangle'', { width: 100, height: 100 }); 這樣仍是存在很差維護的問題
};
function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}
getArea(shapeType.triangle, { width: 100, height: 100 });

------------------------------------

屬性名的遍歷

在a.js文件中定義一個對象testSymbol,並將它對外暴露

let t2 = Symbol('t2');

export let testSymbol = {
t1: 't1value',

[t2]: 't2value' 

};

在b.js中引入a.js,只能訪問到屬性t1, 沒法訪問到t2屬性

import { testSymbol } from '/a.js'

console.log(testSymbol['t1']);  //t1value

  console.log(testSymbol[t2]);  //Uncaught (in promise) ReferenceError: t2 is not defined

console.log(Object.keys(testSymbol));                                  //["t1"]
    console.log(Object.getOwnPropertyNames(testSymbol));        //["t1"]
    console.log(Reflect.ownKeys(testSymbol));                            //["t1", Symbol(t2)]
    console.log(Object.getOwnPropertySymbols(testSymbol));      //[Symbol(t2)]

for(let i in testSymbol) { console.log(i); }                             //"t1" 

因此能夠利用Symbol定義私有屬性

-----------------------------------

Symbol.for()    Symbol.keyFor()

  Symbol()和Symbol.for()這兩個方法都會生產Symbol對象,Symbol.for()與Symbol()的不一樣是它生成的Symbol對象會被註冊到全局環境中。Symbol.for()不會每次調用就返回一個新的 Symbol 類型的值,而是會先檢查給定的key是否已經存在,若是不存在纔會新建一個值。

let s1 = Symbol.for('foo');

let s2 = Symbol.for('foo');

console.log(s1 === s2);       // true

         Symbol.for('foo')先檢查全局的註冊表中有沒有key是'foo'的Symbol對象,若是存在就返回這個對象,如不不存在就從新生成。

 Symbol.keyFor方法返回一個已登記的 Symbol 類型值的key。
 Symbol.keyFor(s1) // "foo"

------------------------------------

內置的Symbol值

es6提供了11個內置的Symbol值,指向了語言內部使用的方法,實現元編程(對js默認的行爲作操做)。

  1. Symbol.hasInstance  

 對象的Symbol.hasInstance指向內部方法。當對其它對象(a)使用instanceof操做符判斷是否是該對象(b)的實例時,會調用該對象(b)的[Symbol.hasInstance
]方法。好比a instanceof b,在方法內部實際調用的是b[Symbol.hasInstance](a)。

let a = [1,2,3], b;
class MyArray {
[Symbol.hasInstance] (val) {
return val instanceof Array;
}
}
b = new MyArray();
console.log(a instanceof b); //true

  2.Symbol.isConcatSpreadable

對象的Symbol.isConcatSpreadable屬性是一個布爾值,它表示對該對象使用Array.prototype.concat()方法時,該對象是否能夠展開。

當對象是數組時,Symbol.isConcatSpreadable的默認值是undefined,默認展開。設置爲false不能夠展開,設置爲true能夠展開。

當對象是一個相似數組的對象時,Symbol.isConcatSpreadable的默認值是undefined,默認不展開。設置爲false不能夠展開,設置爲true能夠展開。

  默認狀況:

let a1 = [3, 4];
let a2 = {0: 3, 1: 4, length: 2};
console.log([1].concat(a1, [5, 6])); // [1, 3, 4, 5, 6]
console.log([1].concat(a2, [5, 6])); // [1, {0: 3, 1: 4, length: 2}, 5, 6]

  設置一下:

a1[Symbol.isConcatSpreadable] = false;
a2[Symbol.isConcatSpreadable] = true;
console.log([1].concat(a1, [5, 6])); // [1, 3, [4, 5], 6]
console.log([1].concat(a2, [5, 6])); // [1, 3, 4, 5, 6]

  Symbol.isConcatSpreadable還能夠定義在類中

class A1 extends Array {
constructor (args) {
super(args);
this[Symbol.isConcatSpreadable] = true;
}
}
class A2 extends Array {
constructor (args) {
super(args);
}
get [Symbol.isConcatSpreadable] () {
return false;
}
}
let a1Obj = new A1();
let a2Obj = new A2();
a1Obj[0] = 3;
a1Obj[1] = 4;
a2Obj[0] = 5;
a2Obj[1] = 6;
console.log([1, 2].concat(a1Obj, a2Obj)); //[1, 2, 3, 4, [5, 6]]

A1是定義在實例上,A2是定義在類自己。

  3. Symbol.species

  對象的Symbol.species屬性,指向構造函數。當建立衍生對象時,會調用這個屬性。

class MyArray extends Array {};
let ma1 = new MyArray(1, 2, 3);
let ma2 = ma1.map(item => item * 2);
let ma3 = ma1.filter(item => item > 1);
console.log(ma2 instanceof MyArray); //true
console.log(ma2 instanceof Array); //true
console.log(ma3 instanceof MyArray); //true
console.log(ma3 instanceof Array); //true

從上面例子看,類MyArray繼承Array,ma1是MyArray的實例,ma2和ma3是ma1的衍生對象。ma2和ma3都是經過數組的方法生成出來的對象,也就是說他們應該是Array的實例,可是他們並非MyAarry的實例,因此上面獲得的結果並非咱們想要的。那麼經過設置Symbol.species屬性就能夠解決這個問題,咱們要設置MyArray的Symbol.species屬性。

定義Symbol.species使用get取值器。Symbol.species是類的靜態屬性。Symbol.species內部會指向指定的構造函數(如今咱們須要指向Array)。

class MyArray extends Array {
static get [Symbol.species] () {
return Array;
}
};

經過上面的設置後,ma2和ma3只是Array的實例,並非MyArray的實例。

       Symbol.species主要的用途是,有些類庫是在基類的基礎上修改的,那麼子類使用繼承的方法時,做者可能但願返回基類的實例,而不是子類的實例。

  4.Symbol.match (Symbol.replace, Symbol.search,Symbol.split都是相似的)

對象的Symbol.match屬性,指向一個方法,當執行str.match(myObj)時,若是該屬性存在,會調用它,返回該方法的返回值。

class MyMatcher {
constructor (bstr) {
this.bstr = bstr;
}
[Symbol.match] (str) {
return str.indexOf(this.bstr);
}
}
'hello world'.match(new MyMatcher('e'));

  5. Symbol.replace

class MyReplace {
constructor (bstr) {
this.bstr = bstr;
}
[Symbol.replace](oldStr, newStr){
console.log('"'+ oldStr +'"被替換成了"'+ newStr +'"');
return this.bstr.replace(oldStr, newStr);
}
}
let res = '一'.replace(new MyReplace('我有一盆花'), '不少');
console.log(res);

   Symbol.replace方法會收到兩個參數,第一個參數是replace  方法正在做用的對象,上面例子第一個參數是「一」,第二個參數是替換後的值「不少」

6. Symbol.search 

class MySearch {
constructor(value) {
this.value = value;
}
[Symbol.search](string) {
return string.indexOf(this.value);
}
}
'foobar'.search(new MySearch('foo')) // 0

  7. Symbol.split

class MySplitter {
constructor(value) {
this.value = value;
}
[Symbol.split](string) {
let index = string.indexOf(this.value);
if (index === -1) {
return string;
}
return [
string.substr(0, index),
string.substr(index + this.value.length)
];
}
}
'foobar'.split(new MySplitter('foo')) // ['', 'bar']
'foobar'.split(new MySplitter('bar')) // ['foo', '']
'foobar'.split(new MySplitter('baz')) // 'foobar'

  8. Symbol.iterator

對象的Symbol.iterator屬性,指向該對象的默認遍歷器方法。對象執行for...of時,會調用該對象的Symbol.iterator方法,可參見Iterator 和 for...of 循環

9. Symbol.toPrimitive

  對象的Symbol.toPrimitive屬性,指向一個方法。把一個對象轉爲原始類型值時。會調用Symbol.toPrimitive方法,返回值是該對象的原始類型值。Symbol.toPrimitive接受一個參數,該參數表示當前運算的模式,一共有三種模式:

Number:該場合須要轉成數值

String:該場合須要轉成字符串

Default: 該場合能夠轉成數值,也能夠轉成字符串

let obj = {
[Symbol.toPrimitive] (hint) {
switch (hint) {
case 'number':
return 1;
case 'string':
return 's';
case 'default':
return 'default';
default:
return new Error();
}
}
}
console.log(obj * 2); //2
console.log(String(obj)); //s
console.log(obj + 'abc'); //defaultabc

10.  Symbol.toStringTag

對象的Symbol.toStringTag屬性,指向一個方法。咱們在獲取一個對象的類型時會調用Object.prototype.toString.call('abc')  返回的是[object String]。這裏,Symbol.toStringTag的返回值就會出如今toString方法的返回字符串中(就是結果中String的部分),看個例子。

class StringTag {
get [Symbol.toStringTag] () {
return 'arr';
}
}
Object.prototype.toString.call(new StringTag()); //"[object arr]"

11. Symbol.unscopables

  對象的Symbol.unscopables屬性,指向一個對象。指定了使用with關鍵字時,哪些屬性是被with環境排除的。

舉個例子,Array.prototype[Symbol.unscopables] 默認返回的是

  1. copyWithintrue
  2. entriestrue
  3. filltrue
  4. findtrue
  5. findIndextrue
  6. flattrue
  7. flatMaptrue
  8. includestrue
  9. keystrue
  10. valuestrue

  咱們來驗證一下

let a = [1, 2, 3]
with(a){console.log(indexOf)} //ƒ indexOf() { [native code] }
with(a){console.log(findIndex)} //VM15227:1 Uncaught ReferenceError: findIndex is not defined
相關文章
相關標籤/搜索