理解ES6中的Symbol

Posted jyybeam

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解ES6中的Symbol相关的知识,希望对你有一定的参考价值。

一、为什么ES6引入Symbol

  有时候我们在项目开发的过程中可能会遇到这样的问题,我写了一个对象,而另外的同时则在这个对象里面添加了一个属性或是方法,倘若添加的这个属性或是方法是原本的对象中本来就有的,那么这个时候势必会造成冲突,那么为了防止这种冲突,ES6

就引入了Symbol

二、Symbol使用方法

  Symbol是一个新的数据类型,所以不要因为Symbol的使用方法的特殊而认为它只是es6中的新的方法。它所代表的是独一无二的,即便其参数一致。

  1.使用方法

    Symbol的使用方法是Symbol(描述信息),其中描述信息可以为任意类型,但若是引用类型则会调用其toString方法;若为undefined,则为Symbol(),相当于不设置描述信息;若为null则为Symbol(null)。

let name = Symbol("jyy");
let name1 = Symbol("jyy");
console.log(typeof name);  // "symbol" console.log(name);  // Symbol(jyy)
console.log(name === name1);  // 两个Symbol不相等,false

let obj = {
toString : function(){
return "jyy"
}
};
let obj_name = Symbol(obj);
console.log(obj_name); // Symbol(jyy)  调用引用类型的toString

let unf_name = Symbol(undefined);
console.log(unf_name); // Symbol()  相当于没有设置描述信息

let null_name = Symbol(null);
console.log(null_name); // Symbol(null)
 

    从打印出的信息,我们可以看到,其类型是symbol。而且即便在Symbol中的描述字符串设置为相同的内容,最后二者依旧是不相等的。

  2.和其他数据类型的关系

    我们知道数据类型之间是可以互相转化的,那么Symbol和其他的数据类型之间是如何转化的?

    a. 转换为字符串

1 let name = Symbol("jyy");
2 console.log(String(name));  // "Symbol(jyy)"
3 console.log(name.toString()); // "Symbol(jyy)"
4 console.log("" + name); // 报错TypeError: Cannot convert a Symbol value to a string

      可以看出String()和toString()方法是可以转为字符串的,但是不可使用“”+Symbol()转化,回报类型错误

    b.转换为布尔值

let name = Symbol("jyy");
console.log(Boolean(name)); // true
if(name){ 
  console.log(true);  // 为true
}else{
  console.log(false);
}

      Symbol类型只要赋值,就一定为true

  3.将对象的属性设置为Symbol类型

let name = Symbol("jyy");
let obj = {
  name : "beijing",
  [name] : "hebei"
};
console.log(obj); // { name: ‘beijing‘, [Symbol(jyy)]: ‘hebei‘ }
console.log(obj.name);  // beijing
console.log(obj["name"]); //beijing
console.log(obj[name]); //hebei

    若对象的属性为Symbol类型,则Symbol值必须放在[]中,看代码可以体会一下

  4.对还有属性为Symbol的对象进行遍历

    若属性为Symbol类型,则该属性不会出现在for...in、for...of中,也不会通过使用Object.keys()、Object.getOwnPropertyName()得到,若想要得到,则需要使用Object.getOwnPropertySymbol()

let name = Symbol("jyy");
let obj = {
  name : "beijing",
  [name] : "hebei"
};
for(let item in obj){
  console.log(item);  // name 不能遍历Symbol类型属性
}
console.log(Object.keys(obj));  // [ ‘name‘ ] 不能得到Symbol类型属性
console.log(Object.getOwnPropertyNames(obj)); // [ ‘name‘ ] 不能得到Symbol类型属性
console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(jyy) ]  只得到Symbol类型属性

    从上面的代码我们可以看到有些方法可以的得到Symbol类型,有些不行,但是却没有能够完全得到所有类型的。es6中有一个新的方法是可以得到的,如下

let name = Symbol("jyy");
let obj = {
  name : "beijing",
  [name] : "hebei"
};
console.log(Reflect.ownKeys(obj));  // [ ‘name‘, Symbol(jyy) ]

 

三、Symbol中的方法

  1.Symbol.for()

    我们知道Symbo()创建的两个变量永远不会是相同的。那么如果我们需要重新使用同一个Symbol怎么办,总不能需要挨个去进行比较吧。还好,es6为我们提供了Symbol.for()方法。 

    参数是symbol类型的描述信息,不同于Symbol(),这个而参数只能是字符串或者是undefined,若已经创建了则返回这个symbol,否则就进行创建并将这个新的symbol返回,代码如下

let name = Symbol.for("jyy");
let name1 = Symbol.for("jyy");
console.log(name === name1);  // true

    请注意,我们在使用创建描述信息为jyy的变量的时候,使用的是for,而不是Symbol(),倘若使用Symbol()进行首次创建,for会再次创建一次,二者不会相等,代码如下:

let name = Symbol("jyy");
let name1 = Symbol.for("jyy");
console.log(name === name1);  // false

    原因在于Symbol.for()会有一个登记机制,使用for只会对通过for创建的symbol进行检查,不会对Symbol()创建的进行检查。

  2. Symbol.keyFor()

    这个方法参数是一个通过Symbol.for()创建的symbol类型变量,返回这个symbol变量的描述信息。

let name = Symbol.for("jyy");
console.log(Symbol.keyFor(name)); // "jyy"
let name1 = Symbol("jyy");
console.log(Symbol.keyFor(name1)); // undefined 不能查找Symbol()创建的变量

  

四、内置的Symbol属性

  1.Symbol.hasInstance

    这个属性只想一个内部方法,当该对象使用instanceof时,会调用这个方法,代码如下:

let Animal = {
[Symbol.hasInstance](foo){
return true
}
}
let obj = {};
console.log(obj instanceof Animal); // true

    另外写下对于原始instaceoOf的原理:

function instanceOf(L,R){
  var R_temp = R.prototype; // 获取右侧对象的原型对象 
  L = L.__proto__;  // 获取左侧对象的原型对象
  while(true){
    if(L == null){// 若左侧对象的原型对象是空,则返回false
      return false;
    }
    if(L === R_temp){ // 若二者的原型对象相等,则说明L是由R直接或间接创建的
      return true;
    }
    L = L.__proto__;  // 通过原型链不断向上原型对象
  }
}

  2.Symbol.isConcatSpreadable

    这个属性为一个布尔值,用来表示该对象是否在Array.prototype.concat()时可以展开,干说不懂,代码如下:

let arr = [1,2,3];
let arr1 = [4,5];
console.log(arr1[Symbol.isConcatSpreadable]); // undefined
console.log(arr.concat(arr1));  // [ 1, 2, 3, 4, 5 ], 数组的Symbol.isConcatSpreadable默认为undefined,可以展开(true更可以)

arr1[Symbol.isConcatSpreadable] = false; 
console.log(arr.concat(arr1));  // [ 1, 2, 3, [ 4, 5, [Symbol(Symbol.isConcatSpreadable)]: false ] ]  设置为false后不可展开

  3.Symbol.match

    这个属性指向一个方法,当执行str.match(obj)时,会调用这个函数并返回其返回值,代码如下:

class Person{
  [Symbol.match](num){
    return parseInt(num) > 100; // 自定义方法内的逻辑
  }
}
console.log("123".match(new Person())); // true
console.log("12".match(new Person()));  // false

  4.Symbol.replace

    这个属性指向一个方法,当执行str.replace(obj)时,会调用这个方法并返回其返回值,代码如下:

class Person{
  [Symbol.replace](obj,rst){  // 两个参数,第一个是对象,第二个是返回的结果
    console.log(rst); // wss
    return "hello"; // 自定义返回值
  }
}

console.log("jyy".replace(new Person(), "wss"));  // hello

  5.Symbol.toPrimitive
    这个属性指向一个方法,当对象被转为原始类型时,会调用这个方法,代码如下

let Person = {
  [Symbol.toPrimitive](type){ // 参数为三种: string,number,default
    switch(type){
      case "string":
        return "jyy";
      case "number":
        return 28;
      default:
        return "Beijing"
    }
  }
};
console.log("my name: " + String(Person));  //my name: jyy
console.log("my age: " + Number(Person));   // my age: 28
console.log("my address is: " + Person);    // my address is: Beijing

  6.Symbol.toStringTag

    个人感觉这个方法还是比较实用的,最早看到这这个属性的使用是在webpack3打包后的js代码中,用来自定义一个方法的toString()值。我们知道当我们在aler一个对象的时候,会显示[object Object],这个属性指向的方法就是可以将Object换为自定义。代码如下:

let person = {
  [Symbol.toStringTag] : "Person"
}
console.log(person.toString()); // [object Person]

 

    

 

以上是关于理解ES6中的Symbol的主要内容,如果未能解决你的问题,请参考以下文章

《深入理解ES6》之Symbol

es6 Symbole入门理解

ES6 第十二节 Symbol在对象中的作用

ES6-Symbol的用法 ,symbol在对象中的应用,改变值

ES6中的新数据类型——Symbol

ES6中的Symbol类型