Symbol
是 ECMAScript 6 新增的根本数据类型。Symbol
提供的实例是惟一、不可变的。它的用处能够确保对象属性应用惟一标识符,不会产生属性抵触的危险。
Symbol
提供 Symbol()
函数来返回 Symbol
类型的值。语法如下所示:
Symbol(desc)
desc
可选的字符串类型的参数。是用来形容Symbol
的。
通过 Symbol()
函数初始化,能够通过 typeof
运算符辨认 Symbol
类型,当然也能够传入参数对 Symbol
进行形容,但与其 Symbol
定义或标识齐全无关。
let s1 = Symbol();console.log(typeof s1); // symbollet s2 = Symbol('symbol instance');let s3 = Symbol('symbol instance');console.log(s1 == s2); // falseconsole.log(s2 == s3); // false
留神,Symbol
不能通过 new
关键字像构造函数一样创立实例,并抛出 TypeError
谬误。这样做是为了防止创立 Symbol
包装对象。
let s = new Symbol(); // TypeError: Symbol is not a constructor
然而,能够应用 Object()
函数创立一个 Symbol
包装器对象。
let s = Symbol("symbol instance");console.log(typeof s); // symbollet o = Object(s);console.log(typeof o); // object
全局共享的 Symbol
Symbol()
函数创立的 Symbol
实例不是全局可用的 Symbol
类型。如果须要全局可用的 Symbol
实例,能够应用 Symbol.for(key)
办法和 Symbol.keyFor(key)
办法。
Symbol.for()
办法创立的 Symbol
会被放入一个全局的 Symbol
注册表中。Symbol.for()
并不是每次都会创立一个新的 Symbol
实例,它会首先查看给定的 key
是否曾经在注册表中了。如果是,则会间接返回上次存储的Symbol
值。否则,它会在新键一个。
let gs1 = Symbol.for('gloal symbol'); // 创立新符号 let gs2 = Symbol.for('global symbol'); // 重用已有符号console.log(gs1 === gs2); // true
能够应用 Symbol.keyFor()
来查问全局注册表,如果查到与 key
对应的 Symbol
实例,则返回该 Symbol
实例的 key
值,否则返回 undefined
。
// 创立全局符号 let s = Symbol.for('foo'); console.log(Symbol.keyFor(s)); // foo // 创立一般符号 let s2 = Symbol('bar'); console.log(Symbol.keyFor(s2)); // undefined
如果传给 Symbol.keyFor()
的不是符号,则该办法抛出 TypeError
:
Symbol.keyFor(123); // TypeError: 123 is not a symbol
Symbol 作为属性
Symbol
能够作为属性来应用。如下所示:
let s1 = Symbol("property one");let o = { [s1]: 'symbol value one'};console.log(o); // { [Symbol(property one)]: 'symbol value one' }
Object.getOwnPropertySymbols()
办法会让你在查找给定对象的 Symbol
属性时返回一个 Symbol
类型的数组。
let s1 = Symbol("property one"), s2 = Symbol("property two");let o = { [s1]: 'symbol value one', [s2]: 'symbol value two', name: 'symbol property sample', index: 12};console.log(Object.getOwnPropertySymbols(o)); // [ Symbol(property one), Symbol(property two) ]
应用 Object.getOwnPropertyNames()
正好相同。如下所示:
console.log(Object.getOwnPropertyNames(o)); // [ 'name', 'index' ]
Symbol 原型
所有 Symbol
都继承自 Symbol.prototype
,而它也提供了 constructor
和 description
两个属性。constructor
属性会返回实例原型的函数,默认为 Symbol
函数;而 description
属性是一个只读字符串,返回 Symbol
对象的形容。
Symbol.prototype
也提供了 toString()
和 valueOf()
办法用于返回 Symbol
描述符的字符串办法和对象的原始值。
Symbol.prototype[@@toPrimitive]
会将 Symbol
对象转换为原始值。语法如下:
Symbol()[Symbol.toPrimitive](hint)
Symbol
的 [@@toPrimitive]()
办法返回该 Symbol
对象原始值作为 Symbol
数据模式。hint
参数未被应用。
JavaScript 调用 [@@toPrimitive]()
办法将一个对象转换为原始值示意。你不须要本人调用 [@@toPrimitive]()
办法;当对象须要被转换为原始值时,JavaScript 会主动地调用该办法。
内置的 Symbol 属性
除了本人创立的 Symbol
,JavaScript 还内建了一些在 ECMAScript 5 之前没有裸露给开发者的 Symbol
,它们代表了外部语言行为。这些 Symbol
最重要的用处之一是从新定义它们,从而扭转原生构造的行为。如从新定义 Symbol.iterator
属性的值,来扭转 for-of
在迭代对象时的行为。
留神 ,在提到 ECMAScript 标准时,常常会援用符号在标准中的名称,前缀为 @@
。比方,@@iterator
指的就是 Symbol.iterator
。
Symbol.iterator
该属性返回一个对象默认的迭代器,被 for-of
应用。上面自定义一个迭代器。
class List { constructor() { this.index = 0; this.data = arguments; } *[Symbol.iterator]() { while(this.index < this.data.length) { yield this.data[this.index++]; } }}function iter() { let list = new List("小玲", "小霞", "小星", "小民"); for (const v of list) { console.log(v); }}iter();// 小玲// 小霞// 小星// 小民
如上所示,通过 for-of
循环对一个对象进行迭代时,@@iterator
办法在不传参的状况下被调用,返回的迭代器用于获取要迭代的值。
Symbol.asyncIterator
一个返回对象默认的异步迭代器的办法。被 for await of
应用。
该办法返回一个对象默认的异步迭代器,由 for-await-of
语句应用。
class List { constructor() { this.index = 0; this.data = arguments; } async *[Symbol.asyncIterator]() { while(this.index < this.data.length) { yield new Promise((resolve) => resolve(this.data[this.index++])); } }}async function asyIter() { let list = new List("小玲", "小霞", "小星", "小民"); for await(const v of list) { console.log(v); }}asyIter();// 小玲// 小霞// 小星// 小民
如上所示,for-await-of
循环会利用 List
类中的函数执行异步迭代操作。
留神,Symbol.asyncIterator
是 ES2018
标准定义的,只有新版本的浏览器反对它。
Symbol.match
该办法指定了用正则表达式去匹配字符串。String.prototype.match()
办法会调用此函数。
console.log('It\'s a real horror story'.match(/horror/));/*[ 'horror', index: 12, input: "It's a real horror story", groups: undefined]*/
给这个办法传入非正则表达式值会导致该值被转换为RegExp对象。如果想扭转这种行为,让办法间接应用参数,则从新定义 Symbol.match
函数以取代默认对正则表达式求值的行为,从而让match()办法应用非正则表达式实例。Symbol.match 函数接管一个参数,就是调用 match()办法的字符串实例。返回的值没有限度:
class StringMatcher { static [Symbol.match](target) { return target.includes('horror'); } constructor(str) { this.str = str; } [Symbol.match](target) { return target.includes(this.str); }}console.log('It\'s a real horror story'.match(StringMatcher)); // trueconsole.log('It\'s a real relaxing story'.match(StringMatcher)); // falseconsole.log('It\'s a real horror story'.match(new StringMatcher('horror'))); // trueconsole.log('It\'s a real horror story'.match(new StringMatcher('relaxing'))); // false
Symbol.replace
该属性指定了当一个字符串替换所匹配字符串时所调用的办法。供 String.prototype.replace()
办法调用此办法,用于替换子字符串。
console.log('It\'s a real horror story'.replace(/horror/, 'relaxing'));// It's a real relaxing story
而正则表达式的原型上默认由 Symbol.replace
函数定义。给 String.prototype.replace
办法传入非正则表达式值会导致该值被转换为 RegExp
对象。
能够从新定义 Symbol.replace
函数,用来取代默认行为。两种定义 Symbol.replace
函数的形式如下所示:
class StringReplacer { static [Symbol.replace](target, replacement) { return target.split('horror').join(replacement); } constructor(str) { this.str = str; } [Symbol.replace](target, replacement) { return target.split(this.str).join(replacement); }}console.log('It\'s a real horror story'.replace(StringReplacer, 'qux')); // It's a real qux storyconsole.log('It\'s a real horror story'.replace(new StringReplacer('horror'), 'relaxing')); // It's a real relaxing story
Symbol.replace
函数接管两个参数,即调用 replace()
办法的字符串实例和替换字符串。
Symbol.search
该办法会返回正则表达式在字符串中匹配的索引。String.prototype.search()
办法会调用此办法,用于查找索引。
console.log('It\'s a real horror story'.search(/horror/)); // 12
能够从新定义 Symbol.search
函数,用来取代默认行为,从而让 search()
办法应用非正则表达式的实例。如下所示:
class StringSearcher { static [Symbol.search](target) { return target.indexOf('qux'); } constructor(str) { this.str = str; } [Symbol.search](target) { return target.indexOf(this.str); }}console.log('It\'s a real horror story'.search(StringSearcher)); // -1console.log('It\'s a real qux story'.search(StringSearcher)); // 12console.log('qux, It\'s a real horror story'.search(StringSearcher)); // 0console.log('It\'s a real horror story'.search(new StringSearcher('qux'))); // -1console.log('It\'s a real horror story'.search(new StringSearcher('horror'))); // 12console.log('It\'s a real horror story'.search(new StringSearcher('s'))); // 3
Symbol.search
函数接管一个参数,就是调用 search()
办法的字符串实例。
Symbol.split
该办法会通过一个正则表达式的索引,来分隔字符串。String.prototype.split()
办法会调用此办法。
console.log('It\'s a real horror story'.split(/ /)); // 正则匹配的是空格// [ "It's", 'a', 'real', 'horror', 'story' ]
能够从新定义 Symbol.split
函数,用来取代默认行为,从而让 split()
办法应用非正则表达式实例。
class StringSplitter { static [Symbol.split](target) { return target.split('qux'); } constructor(str) { this.str = str; } [Symbol.split](target) { return target.split(this.str); }}console.log('It\'s a real qux story'.split(StringSplitter)); // [ "It's a real ", ' story' ]console.log('It\'s a real horror story'.split(new StringSplitter(' '))); // [ "It's", 'a', 'real', 'horror', 'story' ]
Symbol.split
函数接管一个参数,就是调用 split()
办法的字符串实例。
Symbol.hasInstance
该属性用于判断某对象是否为某结构器对象的实例。能够用 Symbol.hasInstance
函数自定义 instanceof
在某个类上的行为。
instanceof
的场景如下:
class Instance {}let ist = new Instance();console.log(ist instanceof Instance); // true
而 ES6
中,instanceof
会应用 Symbol.hasInstance
属性来确定关系。
console.log(Instance[Symbol.hasInstance](ist)); // true
Symbol.hasInstance
属性定义在 Function
的原型上,默认所有函数和类都能够调用。因为 instanceof
会在原型链上寻找这个属性定义,就跟在原型链上寻找其余属性一样,因而能够在继承的类上通过静态方法从新定义这个属性:
class Instance {}class SubInstance extends Instance { static [Symbol.hasInstance]() { return false; }}let sist = new SubInstance();console.log(Instance[Symbol.hasInstance](sist)); // trueconsole.log(sist instanceof Instance); // trueconsole.log(SubInstance[Symbol.hasInstance](sist)); // falseconsole.log(sist instanceof SubInstance); // false
Symbol.isConcatSpreadable
Symbol.isConcatSpreadable
用于配置某对象作为 Array.prototype.concat()
办法的参数时是否开展其数组元素。
const arr1 = ['a', 'b', 'c'];const arr2 = [1, 2, 3];let arr3 = arr1.concat(arr2);console.log(arr3); // [ 'a', 'b', 'c', 1, 2, 3 ]
这是在 Symbol.isConcatSpreadable = true
时的情景,如果设置为 false
,就不会开展数组。
arr2[Symbol.isConcatSpreadable] = false;let arr4 = arr1.concat(arr2);console.log(arr4);/*[ 'a', 'b', 'c', [ 1, 2, 3, [Symbol(Symbol.isConcatSpreadable)]: false ]]*/
由上可知,对于数组对象,应用 concat
在默认状况下会将数组中元素开展进行连贯。重置 Symbol.isConcatSpreadable
能够扭转默认行为。
Symbol.unscopables
Symbol.unscopables
指定对象值,其对象所有的以及继承属性,都会从关联对象的 with
环境绑定中排除。设置 Symbol.unscopables
并让其映射对应属性的键值为 true
,就能够阻止该属性呈现在 with
环境绑定中。如下所示:
let o = { name: '小玲'};with (o) { console.log(name); // 小玲}o[Symbol.unscopables] = { name: true};with (o) { console.log(name); // ReferenceError: name is not defined}
留神,不举荐应用 with
,因而也不举荐应用 Symbol.unscopables
。
Symbol.species
Symbol.species
作为创立派生对象的构造函数。用 Symbol.species
定义动态的 getter
办法,能够笼罩新创建实例的原型定义:
class Array1 extends Array {}class Array2 extends Array { static get [Symbol.species]() { return Array; }}let a1 = new Array1();console.log(a1 instanceof Array); // trueconsole.log(a1 instanceof Array1); // truea1 = a1.concat('species');console.log(a1 instanceof Array); // trueconsole.log(a1 instanceof Array1); // truelet a2 = new Array2();console.log(a2 instanceof Array); // trueconsole.log(a2 instanceof Array2); // truea2 = a2.concat('species');console.log(a2 instanceof Array); // trueconsole.log(a2 instanceof Array2); // false
Symbol.toPrimitive
Symbol.toPrimitive
是一个内置的 Symbol
值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。
Symbol.toPrimitive
属性能够将对象转换为相应的原始值。很多内置操作都会尝试将值将对象转换为原始值,包含字符串、数值和未指定的原始类型。如下所示:
class Sample1 {}let s1 = new Sample1();console.log(+s1); // NaNconsole.log(`${s1}`); // [object Object]console.log(s1 + ""); // [object Object]class Sample2 { constructor() { this[Symbol.toPrimitive] = function(hint) { switch (hint) { case 'number': return 77; case 'string': return 'hello world!'; default: return true; } } }}let s2 = new Sample2();console.log(+s2); // 77console.log(`${s2}`); // hello world!console.log(s2 + ""); // true
Symbol.toStringTag
Symbol.toStringTag
用于创建对象的默认字符串形容。由 Object.prototype.toString()
调用。许多内置类型曾经指定了这个值,但自定义类实例能够明确定义:
// 没有定义 `Symbol.toStringTag` 时class StringTag { constructor() { }}let a = new StringTag();console.log(a); // StringTag {}console.log(a.toString()); // [object Object]// 定义 `Symbol.toStringTag` 时class StringTag { constructor() { this[Symbol.toStringTag] = 'StringTag'; }}let a = new StringTag();console.log(a); // StringTag { [Symbol(Symbol.toStringTag)]: 'StringTag' }console.log(a.toString()); // [object StringTag]
更多内容请关注公众号「海人为记」