阮老师的这一章我读了好几遍,感觉有些内容还是蛮“敷衍”的,有些例子感觉是在强行举例。
概述
Symbol
表示独一无二的值,可以用它来做的事情挺多。
Symbol
是JavaScript
除了undefined
、null
、布尔值(Boolean
)、字符串(String
)、数值(Number
)、对象(Object
)之外的第七种数据类型。
Symbol
不能使用new
命令
Symbol
的参数接受一个字符串,目的是为了容易区分,就算两个Symbol
接受同一个字符串,这两个Symbol
也不会相等
Symbol
不能和其他类型的值进行运算,Symbol
可以显式的转换为字符串(toString()
),Symbol
也可以转换为布尔值,但它不能转换为数值。
Symbol.prototype.description
ES2019
为Symbol
添加了属性,可以直接返回Symbol
参数中的那个用来做描述的字符串。
Symbol作为属性名
Symbol
作为对象的属性名的时候不能用点运算符,对象内部使用Symbol
定义属性时,Symbol
必须放在方括号中,这时因为避免将Symbol类型和字符串类型进行混淆。
Symbol
值作为属性名时,该属性还是公开属性,不是私有属性。
使用Symbol消除魔术字符串
感觉这个例子有些牵强?
const shapeType = {
triangle: Symbol() // 这里使用Symbol代表一个独一无二的值,可以避免使用魔术字符串
};
function getArea(shape, options) {
let area = 0;
switch (shape) {
case shapeType.triangle:
area = .5 * options.width * options.height;
break;
}
return area;
}
getArea(shapeType.triangle, { width: 100, height: 100 });
Symbol属性名的遍历
使用常规的遍历对象属性名的方式是无法遍历出Symbol
类型的属性名的。
Object.getOwnPropertySymbols()
和Reflect.ownKeys()
可以遍历出Symbol
属性名
利用这种无法被常规方法遍历到的特点可以为对象定义非私有的,但是只用于内部的方法,所以说可以用Symbol来模拟私有函数。
Symbol.for(),Symbol.keyFor()
Symbol.for()
方法可以传递一个字符串,然后根据这个字符串去查找全局有没有该Symbol
值,如果有就返回原来的,没有的话就会创建一个新的,并且登记到全局环境,注意无论在什么地方执行Symbol.for()
都会被登记到全局。
所以无论用同一个参数执行多少次Symbol.for()
都会返回同一个结果,但是使用Symbol()
每次都会返回一个新的。
Symbol.keyFor()
方法可以返回一个已经登记了的Symbol
的key
,Symbol.for()
用来在全局做登记,只有使用Symbol.for()
登记了的Symbol
才可以使用Symbol.keyFor()
方法获取到key
Symbol.for()
的这个全局登记特性,可以用在不同的 iframe
或 service worker
中取到同一个值。
iframe = document.createElement('iframe');
iframe.src = String(window.location);
document.body.appendChild(iframe);
iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo') // true
内置的一些Symbol的值
ES6
还提供了 11 个内置的 Symbol
值。
Symbol.hasInstance
var obj = {
[Symbol.hasInstance]: function(instance) { // 改写了instanceof关键字
console.dir(instance);
return true;
}
}
1 instanceof obj; // 1 true
Symbol.isConcatSpreadable
let arr2 = ['c', 'd'];
arr2[Symbol.isConcatSpreadable] = false; // 不允许arr2展开
['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']
Symbol.species
用来决定衍生对象的类型
Symbol.match
用来改写自定义对象的match方法
var obj = {
[Symbol.match]: function(str) {
return "改写了match方法" + str;
}
};
"xxx".match(obj); // "改写了match方法xxx"
Symbol.replace
用来改写自定义对象的replace方法
Symbol.search
用来改写自定义对象的search方法
Symbol.split
用来改写自定义对象的split方法
Symbol.iterator【重要】
对象的Symbol.iterator
属性,指向该对象的默认遍历器方法,对象进行for...of
循环时,会调用Symbol.iterator
方法,返回该对象的默认遍历器
var obj = {};
obj[Symbol.iterator] = function* () {
yield "来了";
yield "老弟";
};
for (var i of obj) {
console.dir(i)
}
// 来了
// 老弟
Symbol.toPrimitive
对象的Symbol.toPrimitive
属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
var obj = {
[Symbol.toPrimitive]: function(hint) {
console.dir(hint);
return 1000;
}
};
2 * obj // number 2000
3 + obj // default 1003
String(obj) // string "1000"
Symbol.toStringTag
对象的Symbol.toStringTag
属性,指向一个方法。在该对象上面调用Object.prototype.toString
方法时,如果这个属性存在,它的返回值会出现在toString
方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object]
或[object Array]
中object
后面的那个字符串。
var obj = {
[Symbol.toStringTag]: "自定义"
};
obj.toString() // "[object 自定义]"
Symbol.unscopables
对象的Symbol.unscopables
属性,指向一个对象。该对象指定了使用with
关键字时,哪些属性会被with
环境排除
var obj = {
foo: "bar",
[Symbol.unscopables]: {
foo: true // 排除foo属性
}
};
with(obj) {
console.dir(foo) // Uncaught ReferenceError: foo is not defined
}