Open JingshunYang opened 5 years ago
symbol是js中7种基础数据类型之一(基础数据类型包括:number, string, boolean, null, undefined, bigint, symbol),symbol是在ECMAScript 2015 / ES6中新加入的。在JavaScript运行环境中,调用函数Symbol()可以动态地返回一个匿名且唯一的symbol类型的值。接下来,这篇文章会简单介绍一下Symbol构造函数,well-known symbols,全局symbol注册表这三部分内容。
symbol
Symbol()
Symbol()函数:构造一个symbol类型值并返回,该值唯一。 返回值:symbol类型值 它类似于内置的Object类,但不完全是一个构造器,因为它不支持new Symbol()这样的语法。 每一个从Symbol()函数返回的symbol值都是独一无二的,因为其唯一性,适合用作对象属性的标识符,使用Object.getOwnPropertySymbols() 以symbol数组的形式返回某个对象的所有symbol属性。
new Symbol()
Object.getOwnPropertySymbols()
// Symbol()函数使用 const symbol1 = Symbol(); const symbol2 = Symbol(42); const symbol3 = Symbol('foo'); console.log(typeof symbol1); // expected output: "symbol" console.log(symbol3.toString()); // expected output: "Symbol(foo)" // 唯一性 console.log(Symbol('foo') === Symbol('foo')); // expected output: false const object1 = {}; // symbol用作对象属性的标识 object1[symbol1] = 'this is symbol1.'; object1[symbol2] = 'this is symbol2.'; object1[symbol3] = 'this is symbol3.'; // 获取某对象中所有的symbol属性 const objectSymbols = Object.getOwnPropertySymbols(object1); console.log(objectSymbols.length); // expected output: 3
即内置symbol,用来表示内部语言行为,分为3种:迭代symbol,正则symbol,其他symbol。
Symbol.iterator
@@iterator
for...of
of
String
Array
TypedArray
Map
Set
arguments
Object
const iterable1 = new Object();
iterable1[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; };
console.log([...iterable1]); // [1, 2, 3]
- `Symbol.asyncIterator`是内置的迭代symbol,用来访问对象的`@@asyncIterator`方法,该方法实现的是对象默认异步迭代器的方法,可用于`for await...of`循环。 #### Regular Expression Symbols - `Symbol.match`是内置的正则symbol,用于访问对象的`@@match`方法,该方法用来判断给定的值是否和某个字符串匹配,在使用`String.prototype.match()`时被调用。也就是说我们可以用`@@match`方法来实现自定义的匹配方法。 ```js class MyMatcher { constructor(value) { this.value = value; } [Symbol.match](string) { var index = string.indexOf(this.value); if (index === -1) { return null; } return [this.value]; } } var fooMatcher = 'foobar'.match(new MyMatcher('foo')); // ['foo'] var barMatcher = 'foobar'.match(new MyMatcher('bar')); // ['bar']
Symbol.matchAll
@@matchAll
String.prototype.matchAll()
Symbol.replace
@@replace
String.prototype.replace()
class MyReplacer { constructor(value) { this.value = value; } [Symbol.replace](string, replacer) { var index = string.indexOf(this.value); if (index === -1) { return string; } if (typeof replacer === 'function') { replacer = replacer.call(undefined, this.value, string); } return `${string.slice(0, index)}${replacer}${string.slice(index + this.value.length)}`; } } var fooReplaced = 'foobar'.replace(new MyReplacer('foo'), 'baz'); // 'bazbar' var barMatcher = 'foobar'.replace(new MyReplacer('bar'), function () { return 'baz' }); // 'foobaz'
Symbol.search
@@search
String.prototype.search()
Symbol.split
@@split
String.prototype.split()
Symbol.hasInstance
instanceof
class Array1 { static [Symbol.hasInstance](instance) { return Array.isArray(instance); } }
console.log([] instanceof Array1); // expected output: true
- `Symbol.isConcatSpreadable`用来访问对象的`@@isConcatSpreadable`属性,该属性为布尔值,用来配置一个数组对象在作为`Array.prototype.concat()`方法的参数时是否应该展开它的元素。 ```js var alpha = ['a', 'b', 'c'], numeric = [1, 2, 3]; var alphaNumeric = alpha.concat(numeric); console.log(alphaNumeric); // 结果: ['a', 'b', 'c', 1, 2, 3] numeric[Symbol.isConcatSpreadable] = false; alphaNumeric = alpha.concat(numeric); console.log(alphaNumeric); // 结果: ['a', 'b', 'c', [1, 2, 3] ]
Symbol.unscopables
@@unscopables
with
const object1 = { property1: 42 };
object1[Symbol.unscopables] = { property1: true };
with (object1) { console.log(property1); // expected output: Error: property1 is not defined }
- `Symbol.species`用来访问对象的`@@species`方法,该属性值为函数,该函数会在创建派生对象 (derived object) 时作为其构造函数。派生对象是相对于原始对象而言的,原始对象在某些具体的操作(如`map`)之后得到的对象被称为派生对象。一般情况下,派生对象和原始对象有着相同的构造器。而如果我们想为派生对象自定义一个构造器时,`@@species`方法就派上用场了。举个例子,我希望给`Array`类添加一些其他的方法,那么我可以基于父类`Array`继承得到子类`MyArray`。之后,`MyArray`的某个实例使用了`map()`方法,如果我希望`map()`方法返回的新对象是`Array`类的实例而不是`MyArray`类的实例,通过实现`MyArray`类的`@@species`方法可以达到目的。 ```js class MyArray extends Array { isEmpty() { return this.length === 0; } static get [Symbol.species]() { return Array; } } let array = new MyArray(3, 5, 4); array.isEmpty(); // => false let odds = array.filter(item => item % 2 === 1); odds instanceof Array; // => true odds instanceof MyArray; // => false
Symbol.toPrimitive
@@toPrimitive
+obj
obj[Symbol.toPrimitive]('number')
'obj'
obj[Symbol.toPrimitive]('string')
var obj = { [Symbol.toPrimitive](hint) { if (hint == 'number') { return 10; } if (hint == 'string') { return 'hello'; } return true; } }; console.log(+obj); // 10 -- hint is "number" console.log(`${obj}`); // "hello" -- hint is "string" console.log(obj + ''); // "true" -- hint is "default"
Symbol.toStringTag
@@toStringTag
Object.prototype.toString()
class Collection { get [Symbol.toStringTag]() { return 'Collection'; } } var x = new Collection(); Object.prototype.toString.call(x) === '[object Collection]' // true
即全局symbol注册表,表中的每一项称之为Record,每个record包含两个字段:key(字符串类型,用作symbol的标识), symbol(symbol类型,存储的symbol值)。在全局symbol注册表中的symbol可在全局访问到。
Symbol.for(key)
Symbol.keyFor(sym)
undefined
symbol的浏览器兼容性
symbol是ES6中新引入的基本数据类型,可通过Symbol构造函数创建得到唯一的symbol值,well-known symbols可以让开发人员自定义类及对象的行为,如迭代、字符串匹配查找等。
Symbol数据类型
symbol
是js中7种基础数据类型之一(基础数据类型包括:number, string, boolean, null, undefined, bigint, symbol),symbol是在ECMAScript 2015 / ES6中新加入的。在JavaScript运行环境中,调用函数Symbol()
可以动态地返回一个匿名且唯一的symbol
类型的值。接下来,这篇文章会简单介绍一下Symbol构造函数,well-known symbols,全局symbol注册表这三部分内容。Symbol构造函数
Symbol()
函数:构造一个symbol类型值并返回,该值唯一。 返回值:symbol类型值 它类似于内置的Object类,但不完全是一个构造器,因为它不支持new Symbol()
这样的语法。 每一个从Symbol()
函数返回的symbol值都是独一无二的,因为其唯一性,适合用作对象属性的标识符,使用Object.getOwnPropertySymbols()
以symbol数组的形式返回某个对象的所有symbol属性。well-known symbols
即内置symbol,用来表示内部语言行为,分为3种:迭代symbol,正则symbol,其他symbol。
Iteration Symbols
Symbol.iterator
是内置的迭代symbol,用来访问对象的@@iterator
方法,该方法是对象默认迭代器的方法,可用于for...of
循环,也可以说@@iterator
为我们提供了重载of
的方法。可迭代对象(String
,Array
,TypedArray
,Map
,Set
,arguments
对象等)拥有默认的@@iterator
方法。对于不可迭代的Object
,通过实现Object
的@@iterator
方法,就可以把不可迭代的对象转变为可迭代的对象。iterable1[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; };
console.log([...iterable1]); // [1, 2, 3]
Symbol.matchAll
是内置的正则symbol,用于访问对象的@@matchAll
方法,该方法返回一个包括了所有匹配值的迭代器,在使用String.prototype.matchAll()
时被调用。Symbol.replace
是内置的正则symbol,用于访问对象的@@replace
方法,该方法用来替换和某个字符串的子串匹配的部分,在使用String.prototype.replace()
时被调用。Symbol.search
用于访问对象的@@search
方法,在使用String.prototype.search()
时被调用。Symbol.split
用于访问对象的@@split
方法,在使用String.prototype.split()
时被调用。 正则symbol允许我们自定义类似于正则表达式的类。Other Symbols
Symbol.hasInstance
用于判断某个对象是否为某个构造器的实例,通过instanceof
被调用。console.log([] instanceof Array1); // expected output: true
Symbol.unscopables
用来访问对象的@@unscopables
属性,该属性为对象值,通过设置某个对象的这个属性,就可以把对象的自有属性及继承属性排除在with
作用域之外。object1[Symbol.unscopables] = { property1: true };
with (object1) { console.log(property1); // expected output: Error: property1 is not defined }
Symbol.toPrimitive
用来访问对象的@@toPrimitive
方法,当对象需要转化为基本数据类型的时候就会调用该方法。举个例子,在做+obj
运算时js会调用obj[Symbol.toPrimitive]('number')
;在做'obj'
运算时js会调用obj[Symbol.toPrimitive]('string')
。Symbol.toStringTag
用来访问对象的@@toStringTag
属性,该属性返回一个字符串,用于设置对象默认的字符串描述。在使用Object.prototype.toString()
方法时,Object.prototype.toString()
方法首先会检查作为参数传入的对象是否存在@@toStringTag
属性,如果有则用于最后返回的字符串中。global symbol registry
即全局symbol注册表,表中的每一项称之为Record,每个record包含两个字段:key(字符串类型,用作symbol的标识), symbol(symbol类型,存储的symbol值)。在全局symbol注册表中的symbol可在全局访问到。
Symbol.for(key)
函数,在全局symbol注册表中根据key值查询对应的symbol并返回;查询不到则创建一个可在全局symbol注册表中访问到的symbol。Symbol.keyFor(sym)
函数,返回symbol在全局symbol注册表中对应的key值;没有对应的key值则返回undefined
。浏览器兼容性
symbol的浏览器兼容性
总结
symbol是ES6中新引入的基本数据类型,可通过Symbol构造函数创建得到唯一的symbol值,well-known symbols可以让开发人员自定义类及对象的行为,如迭代、字符串匹配查找等。