ES6躬行记(6)——Symbol

  本节将会重点分析ES6引入的第6种基本类型:Symbol(符号)。符号可以像字符串那样作为对象的属性名,只是它有唯一性的特点,可以避免属性名之间的冲突。

一、创建

  符号没有字面量形式,只能通过Symbol()函数创建。该函数有一个可选的参数,只是用来描述当前符号,除了便于阅读之外,没有其他用途。由此可知,即使两个符号的描述相同,它们还是不能画等号。注意,Symbol()不是构造函数,因此不能和new运算符组合使用,否则会抛出类型错误。下面用一个例子展示符号的创建。

var sym1 = Symbol(),
  sym2 = Symbol("name"),
  sym3 = Symbol("name"),
  sym4 = new Symbol();         //抛出类型错误
console.log(sym2 === sym3);    //false

  如果要识别一个变量是否为符号,可以用typeof运算符。ES6扩展了它,当检测到符号时,能返回一个新的类型字符串“symbol”,具体如下所示。

typeof sym1;        //"symbol"
typeof sym2;        //"symbol"

二、类型转换

  符号在类型转换时表现得并不灵活,它无法与数字或字符串进行运算,也无法显式的转换成数字。如下所示,后面四条语句在执行时都会报错。

var sym = Symbol("age");
Number(sym);
parseInt(sym);
1 + sym;
"" + sym;

  不过,符号可以显式的转换成字符串或布尔值,具体如下所示。

Boolean(sym);      //true
!sym;              //false
sym.toString();     //"Symbol(age)"
String(sym);        //"Symbol(age)"

三、全局共享

  ES6会在内部维护一张全局符号注册表,通过Symbol.for()方法,可以登记指定符号,使其变成一个全局有效地符号,从而达到全局共享。该方法只接收一个参数,这个参数既是注册表中的键值,同时也是此符号的描述。下面的代码调用了两次Symbol.for()方法,传递了相同的参数,返回的却是同一个全局符号。

var sym1 = Symbol.for("name"),
  sym2 = Symbol.for("name");
console.log(sym1 === sym2);        //true

  在上面的代码中,第一次调用Symbol.for()方法时,会在注册表中搜索键值为“name”的符号,由于没有找到,所以就会创建一个新的符号。而在第二次调用Symbol.for()方法时,由于传递的键值与前一次相同,因此会返回刚刚的那个符号。从而可知,对变量sym1和sym2进行全等比较,返回的结果将是true。

  如果要获取某个全局符号所对应的键值(即它的描述),那么可以通过Symbol.keyFor()实现,具体操作如下所示。

Symbol.keyFor(sym1);    //"name"
Symbol.keyFor(sym2);    //"name"

四、属性名

  本节开头曾提到过对象的属性名可以用符号表示,而这类属性可以有三种赋值方式。第一种是用方括号,注意,不能用点号,因为点号后面的标识符会被识别成字符串而不是符号。下面代码分别用方括号和点号为obj对象的sym属性赋值,前者被识别成了符号属性,而后者却被识别成了字符串属性。

var sym = Symbol("name"),
  obj = {};
obj[sym] = "strick";
obj.sym = "strick";
console.log(obj);        //{Symbol(name): "strick", sym: "strick"}

  第二种是在创建对象字面量时,用计算属性名的方式(即属性名被方括号包裹)为其赋值,如下所示。

obj = {
  [sym]: "freedom"
};

  第三种是调用Object.defineProperty()或Object.defineProperties()方法来为符号属性赋值,如下所示。

Object.defineProperty(obj, sym, { value: "justice" });

  注意,符号属性是不可枚举的,既不能被for-in等循环遍历到,也不能被Object.keys()、Object.getOwnPropertyNames()等方法读取到。但可以通过Object.getOwnPropertySymbols()方法获得对象的符号属性,具体如下所示。

obj = {
  [sym]: "freedom",
  age: 28
};
Object.keys(obj);                   //["age"]
Object.getOwnPropertyNames(obj);      //["age"]
Object.getOwnPropertySymbols(obj);    //[Symbol(name)]

五、内置符号

  ES6提供了一些内置符号,也叫做知名符号(Well-Known Symbol)。它们暴露了语言的内部逻辑,允许开发人员修改或拓展规范所描述的对象特征或行为。每一个内置符号对应Symbol对象的一个属性,例如Symbol.hasInstance、Symbol.iterator等,表1列出了11个内置符号。

表1  内置符号

属性名称值类型描述
hasInstance方法当使用instanceof运算符时会调用该方法
isConcatSpreadable布尔值当对象作为Array.prototype.concat()方法的参数时,控制该对象是否被展开
iterator方法返回一个迭代器,用于定义一个可迭代的对象
match方法当对象作为String.prototype.match()方法的参数时,该方法会被调用
replace方法当对象作为String.prototype.replace()方法的参数时,该方法会被调用
search方法当对象作为String.prototype.search()方法的参数时,该方法会被调用
split方法当对象作为String.prototype.split()方法的参数时,该方法会被调用
species方法创建派生类的构造函数
toPrimitive方法当对象需要转换成原始值(即执行ToPrimitive抽象操作)时,该方法会被调用
toStringTag字符串指定对象的类型,可在调用Object.prototype.toString()方法的时候返回
unscopables对象保存在这个对象中的属性将不能被with语句所引用

  下面会给出4个内置符号的示例,分别是hasInstance、isConcatSpreadable、match和toStringTag。

let digit = {
  [Symbol.hasInstance](number) {
    return !(number % 2);        //判断数字是否为偶数
  }
};
1 instanceof digit;    //false
2 instanceof digit;    //true

let arr1 = [3, 4];
[1, 2].concat(arr1);             //[1, 2, 3, 4]
let arr2 = [3, 4];
arr2[Symbol.isConcatSpreadable] = false;    //禁止展开
[1, 2].concat(arr2);             //[1, 2, [3, 4]]

let regex = {
    [Symbol.match](str) {
      return str.substr(0, 10);
    }
  },
  message = "My name is strick";
message.match(regex);          //"My name is"

let people = {
  [Symbol.toStringTag]: "People"
};
people.toString();             //"[object People]"

 

转载于:https://www.cnblogs.com/strick/p/10173774.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值