基本用法

  • Symbol为原始类型,可以使用typeof操作符进行检测
1
2
let mes = Symbol();
console.log(typeof mes)//Symbol
  • Symbol(name)
    • name作为对符号的描述
    • 与符号定义或标识完全无关
1
2
3
let sys1 = Symbol('foo');
let sys2 = Symbol('foo');
console.log(sys1 == sys2);//false
  • 符号没有字面量语法
  • 创建Symbol()实例并将其用作对象的新属性,就可以保证它不会覆盖已有的对象属性
  • Symbol()不能与new关键字一起作为构造函数使用
    • 为了避免创建符号包装对象
  • 可以使用Object()函数创建符号包装对象
    1
    2
    3
    4
    5
    6
    let str = new String();
    console.log(typeof str);//object
    let sys = new Symbol();// TypeError: Symbol is not a constructor
    let mySymbol = Symbol();
    let objsys = Object(mySymbol);
    console.log(typeof objsys);//object

全局符号注册表

  • Symbol.for()
  • 第一次使用౼个字符串调用时,它会检查全局运行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。
  • 即使使用相同的符号描述,在全局注册表中定义的符号跟使用 Symbol()定义的符号也并不等同
  • 全局注册表中的符号必须使用字符串键来创建
    • 作为参数传给 Symbol.for()的任何值都会被转换为字符串
    • 注册表中使用的键同时也会被用作符号描述
1
2
3
4
5
6
7
let sys1 = Symbol.for('foo');
let sys2 = Symbol.for('foo');
let sys3 = Symbol('foo');
let sys4 = Symbol.for();
console.log(sys1 == sys2);//true
console.log(sys1 == sys3);//false
console.log(sys4);//Symbol(underfined)
  • Symbol.keyFor() 来查询全局注册表
  • 如果查询的不是全局符号,则返回 undefined
  • 如果传给 Symbol.keyFor()的不是符号,则该方法抛出 TypeError
1
2
3
4
5
let sys1 = Symbol.for('foo');
let sys3 = Symbol('foo');
console.log(Symbol.keyFor(sys1));//foo
console.log(Symbol.keyFor(sys3));//underfined
Symbol.for(123);//TypeError:123 is not a symbol

符号作为属性

  • 凡是可以使用字符串或数值作为属性的地方,都可以使用符号
  • Object.defineProperty(obj,prop,descriptor)
    • obj:被操作的对象
    • prop:目标对象需要定义或修改的属性的名称
    • descriptor:将被定义或修改的属性描述符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let s1 = Symbol('foo');
s2 = Symbol('bar');
s3 = Symbol('baz');
s4 = Symbol('qux');
let o = {
[s1]:'foo value'
};
console.log(o);
//{Symbol(foo):foo value}
Object.defineProperty(o,s2,{value:'bar value'});
console.log(o);
//{Symbol(foo):foo value,Symbol(bar):bar value}
Object.defineProperties(o,{
[s3]:{value:'baz value'},
[s4]:{value:'qux value'}
});
console.log(o);
//{Symbol(foo):foo value,Symbol(bar): bar value,
//Symbol(baz):baz value,Symbol(qux):qux value}

属性

Symbol.asyncIterator()

  • 一个方法,该方法返回对象默认的 AsyncIterator。 由 for-await-of 语句使用
  • 这个符号表示实现异步迭代器 API 的函数。
  • for-await-of 循环会利用这个函数执行异步迭代操作。
  • 循环时,它们会调用以 Symbol.asyncIterator为键的函数,并期望这个函数会返回一个实现迭代器 API 的对象。
  • 个由 Symbol.asyncIterator 函数生成的对象应该通过其 next()方法陆续返回 Promise 实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Emitter{
constructor(max){
this.max = max;
this.asyncIdx = 0;
}
async *[Symbol.asyncIterator](){
while(this.asyncIdx < this.max){
yield new Promise((resolve)=>resolve(this.asyncIdex++))
}
}
}
async function asyncCount(){
let emitter = new Emitter(5);
for await(const x of emitter){
console.log(x);
}
}
asyncCount();
//0
//1
//2
//3
//4

Symbol.hasInstance

  • 一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例。由 instanceof 操作符使用
  • 在 ES6 中,instanceof 操作符会使用Symbol.hasInstance 函数来确定关系
  • 这个属性定义在 Function 的原型上,因此默认在所有函数和类上都可以调用
  • instanceof操作符会在原型链上寻找这个属性定义,就跟在原型链上寻找其他属性一样,因此可以在继承的类上通过静态方法重新定义这个函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Foo(){}
let f = new Foo();
console.log(f instanceof Foo);//true
console.log(Foo[Symbol.hasInstance](f));//true

class Bar{}
class Baz extends Bar{
static [Symbol.hasInstance](){
return false;
}
}
let b = new Baz();
console.log(Bar[Symbol.hasInstance](b));//true
console.log(b instanceof Bar);//true
console.log(Baz[Symbol.hasInstance][b]);//false
console.log(b instanceof Baz);//false

Symbol.isConcatSpreadable

  • 一个布尔值,如果是 true,则意味着对象应该用 Array.prototype.concat()打平其数组元素
  • ES6 中的 Array.prototype.concat()方法会根据接收到的对象类型选择如何将一个类数组对象拼接成数组实例
  • 数组对象默认情况下会被打平到已有的数组,false 或假值会导致整个对象被追加到数组末尾
  • 类数组对象默认情况下会被追加到数组末尾,true 或真值会导致这个类数组对象被打平到数组实例。
  • 其他不是类数组对象的对象在 Symbol.isConcatSpreadable 被设置为 true 的情况下将被忽略。
  • 类数组对象是指:property(属性名)为正整数的对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let arr1 = ['foo'];
    let arr2 = ['bar'];
    console.log(arr1.concat(arr2));//['foo','bar']
    arr2[Symbol.isConcatSpreadable] = false;
    console.log(arr1.concat(arr2));//['foo',['bar']]

    let obj = {length:1,0:'baz'};
    console.log(arr1.concat(obj));//['foo',{length:1,0:'baz'}]
    obj[Symbol.isConcatSpreadable] = true;
    console.log(arr1.concat(obj));//['foo','baz']

    let set = new Set().add('qux');
    console.log(arr1.concat(set));//['foo',Set(1)]

Symbol.iterator

  • 一个方法,该方法ᤄ回对象默认的迭代器。由 for-of 语句使用
  • for-of 循环这样的语言结构会利用这个函数执行迭代操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Emitter{
constructor(max){
this.max = max;
this.asyncIdx = 0;
}
*[Symbol.interctor](){
while(this.asyncIdx < this.max){
yield this.idx++;
}
}
}
function Count(){
let emitter = new Emitter(5);
for await(const x of emitter){
console.log(x);
}
}
Count();
//0
//1
//2
//3
//4

Symbol.match

  • 一个正则表达式方法,该方法用正则表达式去匹配字符串。由 String.prototype.match()方法使用
  • 给这个方法传入非正则表达式值会导致该值被转换为 RegExp 对象。
  • String.prototype.match()方法会使用以 Symbol.match 为键的函数来对正则表达式求值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class FooMatcher{
static [Symbol.match](target){
return target.includes('foo');
}
}
console.log('foobar'.match(FooMatcher));//true
console.log('barbaz'.match(Foomatcher));//false

class StringMatcher{
constructor(str){
this.str = str;
}
[Symbol.match](target){
return target.includes(this.str);
}
}
console.log('foobar'.match(new StringMatcher('foo')));//true
console.log('barbaz'.match(new StringMatcher('quz')));//false

Symbol.replace

  • 一个正则表达式方法,该方法替换一个字符串中匹配的子串。由 String.prototype.replace()方法使用
  • String.prototype.replace()方法会使用以 Symbol.replace 为键的函数来对正则表达式求值。
  • 给这个方法传入非正则表达式值会导致该值被转换为 RegExp 对象。
1
2
3
4
5
6
7
8
9
class Replace{
constructor(str){
this.str = str;
}
[Symbol.replace](target,replacment){
return target.split(this.str).join(replacement);
}
}
console.log('barfoobaz'.replace(new Replace('foo'),'qux'));
  • 一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引。由 String.prototype.search()方法使用
  • String.prototype.search()方法会使用以 Symbol.search 为键的函数来对正则表达式求值
  • 给这个方法传入非正则表达式值会导致该值被转换为 RegExp 对象。
1
2
3
4
5
6
7
8
9
10
11
class Search {
constructor(str){
this.str = str;
}
[Symbol.search](target){
return target.indexOf(this.str);
}
}
console.log('foobar'.search(new Search('foo')));//0
console,log('barfoo'.search(new Search('foo')));//3
console.log('barbaz'.search(new Search('qux'))); // -1

Symbol.species

  • 一个函数值,该函数作为创建派生对象的构造函数
  • 用于对内置类型实例方法的返回值暴露实例化派生对象的方法
  • 用 Symbol.species 定义静态的获取器(getter)方法,可以覆盖新创建实例的原型定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Bar extends Array{}
class Baz extends Array{
static get [Symbol.species](){
return Array;
}
}
let bar = new Bar();
console.log(bar instanceof Array);//true
console.log(bar instanceof Bar);//true
bar = bar.concat('bar');
console.log(bar instanceof Array);//true
console.log(bar instanceof Bar);//true
let baz = new Baz();
console.log(baz instanceof Array);//true
console.log(baz instanceof Baz);//true
bar = bar.concat('baz');
console.log(baz instanceof Array);//true
console.log(baz instanceof Baz);//false

Symbol.split

  • 一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串。由 String.prototype.split()方法使用
  • String.prototype.split()方法会使用以 Symbol.split 为键的函数来对正则表达式求值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Spilter{
static [Symbol.split](target){
return target.split('foo');
}
}
console.log('barfoobaz'.split(Spliter));
//['bar','baz']
class Split{
constructor(str){
this.str = str;
}
[Symbol.split](target){
return target.split(this.str)
}
}
console.log('barfoobaz'.split(new Split('foo')));
//['bar','baz']

Symbol.toPrimitive

  • 一个方法,该方法将对象转换为相应的原始值。由 ToPrimitive 抽象操作使用
  • 对于一个自定义对象实例,通过在这个实例的 Symbol.toPrimitive 属性上定义一个函数可以改变默认行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Foo{}
let foo = new Foo();

console.log(3+foo);//"3[object Object]"
console.log(3-foo);//"NaN"
console.log(String(foo));//"[object Object]"

class Bar{
constructor(){
this[Symbol.toPrimitive] = function(hint){
switch (hint){
case 'number':
return 3;
case 'string':
return 'string bar';
case 'default':
default:
return 'default bar';
}
}
}
}
let bar = new Bar();
console.log(3+bar);//"3default bar"
console.log(3-bar);//0
console.log(String(bar));//""string bar"

Symbol.toStringTag

  • 一个字符串,该字符串用于创建对象的默认字符串描述。由内置方法 Object.prototype.toString()使用
  • 通过 toString()方法获取对象标识时,会检索由 Symbol.toStringTag 指定的实例标识符,默认为”Object”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let s = new Set();

console.log(s);//Set(0){}
console.log(s.toString());//[object Object]
console.log(s[Symbol.toStringTag]);//Set

class Foo{}
let foo = new Foo();
console.log(foo);//Foo{}
console.log(foo.toString());//[object Object]
console.log(foo[Symbol.toStringTag]);//underfined

class Bar{
constructor(){
this[Symbol.toStringTag] = 'Bar';
}
}
let bar = new Bar();
console.log(bar);//Bar()
console.log(bar.toString());//[object Bar]
console.log(bar[Symbol.toStringTag]);//Bar

Symbol.unscopables

  • 一个对象,该对象所有的以及继承的属性,都会从关联对象的 with 环境绑定中排除
  • 不推荐使用 with,因此也不推荐使用 Symbol.unscopables