Symbol的介绍
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是Javascript语言的第七种数据类型。
ES5对象属性名都是字符串,这容易造成属性名的冲突。为了解决这个问题引入了Symbol类型来解决命名冲突的问题。
Symbol值通过Symbol函数生成,也就是说,对象的属性名现在可以有两种形类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。
Symbol的特点
- Symbol的值是唯一的,用来解决命名冲突的问题
- Symbol值不能与其他数据进行运算
- Symbol定义的对象属性不能使用for.in循环遍历,但是可以使用Reflect.ownkeys来获取对象的所有键名
认识Symbol
Symobol函数可以接受一个字符串作为参数,用来区分或者描述Symbol实例。
// 创建Symbol
let x = Symbol('abc')
let y = Symbol('123')
console.log(x, y); // Symbol(abc) Symbol(123)
如果Symbol的参数传递的是一个对象,那么它内部会调用toString方法将对象转为字符串,在生成一个Symbol值。
// 参数传递对象
let a = Symbol({a: "a"})
console.log(a); // Symbol([object Object])
相同参数创建出来的Symbol值是不相等的
// 相同参数不相等
let b = Symbol("1");
let c = Symbol("1");
console.log(b === c) // false
let d = Symbol();
let e = Symbol();
console.log(d === e) // false
Symbol的值不能与其他类型的值进行运算,如果进行运算会报错。
// 不能与其他类型的值进行运算
let n = Symbol("hello")
console.log(n + "world"); // Uncaught TypeError: Cannot convert a Symbol value to a string
Symbol可以显示转为字符串。
// Symbol显示转为字符串
let f = Symbol("hello wrold")
console.log(f.toString()); // Symbol(hello wrold)
console.log(typeof f.toString()); // string
Symbol也可以转为Boolean值
// Symbol也可以转为Boolean值
let g = Symbol("hello wrold")
console.log(Boolean(g)); // true
Symbol作为对象的属性名
前面我们说到每一个Symbol的值都是不相等的,所以我们可以将Symbol的值作为对象的属性名来使用,这样就能保证不会出现同名属性,防止属性被覆盖或者改写。
// 将Symbol作为对象的属性名使用
let sym = Symbol('sym'),
sym1 = Symbol('sym1'),
sym2 = Symbol('sym2');
const obj = {
[sym]: "hello",
}
obj[sym1] = "world"
Object.defineProperty(obj, sym2, {value: "!!!!"})
console.log(obj); // {Symbol(sym): 'hello', Symbol(sym1): 'world', Symbol(sym2): '!!!!'}
console.log(obj[sym], obj[sym1] ,obj[sym2]); // hello world !!!!
const fun = Symbol('fun');
const obj1 = {
[fun] () {
console.log('hello world!');
}
}
obj1[fun]() //hello world!
Symbol属性名的遍历
Symbol 作为属性名,该属性不会出现在for in、for of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()返回。但是它也不是私有属性,有一个Object.getOwnPropertySymbols()方法可以获指定对象的所有Symbol属性名。
Object.getOwnPropertySymbols()方法返回的是一个数组,成员是当前对象所有用作属性名的Symbol值
Reflect.ownKeys()方法返回所有类型的键名,包括常规键名和Symbol键名。
const a = Symbol('a')
const b = Symbol('b')
const obj = {
a: 'a',
b: 'b',
[a]: 'a',
[b]: 'b'
}
// // 返回当前对象所有用作属性名的Symbol值
const objSymbols = Object.getOwnPropertySymbols(obj)
console.log(objSymbols); // [Symbol(a), Symbol(b)]
// 返回对象内所有类型的键名
const objSymbols1 = Reflect.ownKeys(obj)
console.log(objSymbols1); // ['a', 'b', Symbol(a), Symbol(b)]
Symbol.for() 、Symbol.keyFor()
有时候我们想要使用同一个Symbol值,Symbol.keyFor()方法可以做到这一点。它接收一个字符串参数,让后搜索有没有以该参数作为名称的Symbol值,如果有,就返回这个Symbol值,否则就新建一个以该字符串命名的Symbol值。
// 同样参数使用Symbol.for方法生成的值相同
let sym = Symbol.for('a');
let sym1 = Symbol.for('a');
console.log(sym === sym1); // true
Symbol.for()和Symbol()这两种写法都会生成新的Symbol,它们的区别是,前者会被登记在全局环境中供搜索,而后者不会Symbol.for(),不会在每次调用时都返回一个新的Symbol类型的值,而是先检查给定的key是否已经存在,如果不存在就会返回一个新值。
Symbol.keyFor()方法返回一个已登记的Symbol类型值的key
let sym = Symbol.for('a');
let sym1 = Symbol('a');
console.log(Symbol.keyFor(sym)); // 'a'
console.log(Symbol.keyFor(sym1)); // undefined
sym1属于为登记的Symbol值,所以返回undefined
注意:Symbol.for()方法为Symbol方法登记的名字是全局的。
内置的Symbol
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
以下属性不一一举例说明了,如果需要详细示例请看ECMAScript 6 标准入门https://es6.ruanyifeng.com/#docs/symbol
Symbol.hasInstance
对象的Symbol.hasInstance
属性,指向一个内部方法。当其他对象使用instanceof
运算符,判断是否为该对象的实例时,会调用这个方法。比如,foo instanceof Foo
在语言内部,实际调用的是Foo[Symbol.hasInstance](foo)
。
class MyClass {
[Symbol.hasInstance](foo) {
return foo instanceof Array;
}
}
[1, 2, 3] instanceof new MyClass() // true
上面代码中,MyClass
是一个类,new MyClass()
会返回一个实例。该实例的Symbol.hasInstance
方法,会在进行instanceof
运算时自动调用,判断左侧的运算子是否为Array
的实例。
Symbol.isConcatSpreadable
对象的Symbol.isConcatSpreadable
属性等于一个布尔值,表示该对象用于Array.prototype.concat()
时,是否可以展开。
Symbol.species
对象的Symbol.species
属性,指向一个构造函数。创建造衍生对象时,会使用该属性。
Symbol.match
对象的Symbol.match
属性,指向一个函数。当执行str.match(myObject)
时,如果该属性存在,会调用它,返回该方法的返回值。
Symbol.replace
对象的Symbol.replace
属性,指向一个方法,当该对象被String.prototype.replace
方法调用时,会返回该方法的返回值。
Symbol.search
对象的Symbol.search
属性,指向一个方法,当该对象被String.prototype.search
方法调用时,会返回该方法的返回值。
Symbol.split
对象的Symbol.split
属性,指向一个方法,当该对象被String.prototype.split
方法调用时,会返回该方法的返回值。
Symbol.iterator
对象的Symbol.iterator
属性,指向该对象的默认遍历器方法。
Symbol.toPrimitive
对象的Symbol.toPrimitive
属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
Symbol.toPrimitive
被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。
- Number:该场合需要转成数值
- String:该场合需要转成字符串
- Default:该场合可以转成数值,也可以转成字符串
Symbol.toStringTag
对象的Symbol.toStringTag
属性,指向一个方法。在该对象上面调用Object.prototype.toString
方法时,如果这个属性存在,它的返回值会出现在toString
方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object]
或[object Array]
中object
后面的那个字符串。
Symbol.unscopables
对象的Symbol.unscopables
属性,指向一个对象。该对象指定了使用with
关键字时,哪些属性会被with
环境排除。