symbol的来头
symbol是第六种基础类型,厌倦了只能用String做key,现在可以用Symbol。
a = Symbol('a')
p = {[a]: 'a'}
p[a] // "a"
Symbol共享体系
Symbol.for('xxx')首先在全局Symbol注册表中查找是否存在xxx,有则返回现有的,没有新创建并返回。Symbol.keyFor方法在Symbol全局注册表中检测与Symbol有关的键。
uid = Symbol.for('uid')
uid2 = Symbol.for('uid')
uid3 = Symbol('uid')
uid === uid2 // true
uid === uid3 // false
Symbol.keyFor(uid) // "uid"
Symbol.keyFor(uid2) // "uid"
Symbol.keyFor(uid3)// undefined
内置Symbol
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
Symbol.hasInstance
当我们使用obj instanceof Array
等价于执行 Array[Symbol.hasInstance](obj)
,我们可以改变instanceof的运行方式。
对象的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
a = new MyClass()
// 原来的instanceof也有用
a instanceof MyClass // true
Symbol.isConcatSpreadable
对象的Symbol.isConcatSpreadable
属性等于一个布尔值,表示该对象用于Array.prototype.concat()
时,是否可以展开。数组的默认行为是可以展开,Symbol.isConcatSpreadable默认等于undefined。该属性等于true时,也有展开的效果。类似数组的对象正好相反,默认不展开。
let arr1 = ['c', 'd'];
['a', 'b'].concat(arr1, 'e') //(5) ["a", "b", "c", "d", "e"]
arr1[Symbol.isConcatSpreadable] // undefined
arr1[Symbol.isConcatSpreadable] = false
['a', 'b'].concat(arr1, 'e') // (4) ["a", "b", Array(2), "e"]
// 设为undefined也可以
arr1[Symbol.isConcatSpreadable] = undefined
['a', 'b'].concat(arr1, 'e') // (5) ["a", "b", "c", "d", "e"]
//对象
let obj = {length: 2, 0: 'c', 1: 'd'};
['a', 'b'].concat(obj, 'e') // (4) ["a", "b", {…}, "e"]
obj[Symbol.isConcatSpreadable] = true
['a', 'b'].concat(obj, 'e') // (5) ["a", "b", "c", "d", "e"]
class MyArray extends Array {
}
const a = new MyArray(1, 2, 3);
const b = a.map(x => x);
const c = a.filter(x => x > 1);
b instanceof MyArray // true
c instanceof MyArray // true
true
b instanceof MyArray // true
true
c instanceof MyArray // true
true
c instanceof Array // true
true
Symbol.species
对象的Symbol.species属性,指向一个构造函数。创建衍生对象时,会使用该属性。
class MyArray extends Array {
}
const a = new MyArray(1, 2, 3);
const b = a.map(x => x);
const c = a.filter(x => x > 1);
console.log(b instanceof MyArray) // true
console.log(c instanceof MyArray) // true
undefined
class MyArray1 extends Array {
static get [Symbol.species]() { return Array; }
}
const a1 = new MyArray1(1, 2, 3);
const b1 = a1.map(x => x);
const c1 = a1.filter(x => x > 1);
console.log(b1 instanceof MyArray) // false
console.log(c1 instanceof MyArray) // false
Symbol.toPrimitive
当执行特定操作时(如:==)经常会将对象转换成原始值然后再比较,es6之前是由内部操作决定的,es6则可通过Symbol.toPrimitive方法可以更改那个暴露出来的值。每当执行原始值转换是,总会调用Symbol.toPrimitive方法并传入一个参数:类型提示hint(js 引擎注入)。有三种选择'number', 'string', 'default'(==,+ , Date()构造函数传参)。
1 number:
调用valueof 如果为原始值则返回 --> 调用toString(),如果为原始值则返回
--> 如果再无可选值则报错
2 string
调用toString(),如果为原始值则返回 --> 调用valueof 如果为原始值则返回
--> 如果再无可选值则报错
在大多数情况下,标准对象按默认按数字模式处理。
我们可以自定义:
function Temperature (d) {
this.degrees = d
}
Temperature.prototype[Symbol.toPrimitive] = function (hint) {
switch(hint) {
case 'string':
return this.degrees + '\u00b0'
case 'number':
return this.degrees
case 'default':
return this.degrees + '度'
}
}
var f = new Temperature(62)
f+'!' // "62度!"
f /2 // 31
String(f) // "62°"
一般的做法是将default设为和string或number保持一致。
Symbol.toStringTag
es5我们一般根据
function type (val, xxx) {
return Object.prototype.toString.call(val) === `[object ${xxx}]`
}
来判断数据的类型,es6中定义了toString返回的值储存在对象的Symbol.toStringTag属性中,同样的你也可以改变它。
function type (val) {
return Object.prototype.toString.call(val)
}
let son = {}
type(son) // "[object Object]"
son[Symbole.toStringTag] = 'your father is zlj'
type(son) // "[object your father is zlj]"
请看下面这段代码:
p = {}
p[Symbol.toStringTag] = 'zlj'
function type (val) {
return Object.prototype.toString.call(val)
}
type(p) // "[object zlj]"
p.toString() // "[object zlj]"
p.toString = function () {
return 'haha'
}
p.toString() // "haha"
type(p) // "[object zlj]"
toString的行为默认和Symbol.toStringTag一致,但是你可以改变toString方法,Symbol.toStringTag表现不变。
Symbol.unscopables(非作用域的)
在非严格模式下可以使用with语句:
var values = [1,2,3]
colors = ['red', 'green', 'blue']
color = 'black'
with(colors){
push(color)
push(...values)
}
colors // (7) ["red", "green", "blue", "black", 1, 2, 3]
再看一个例子
var values = [1,2,3]
colors = ['red', 'green', 'blue']
color = 'black'
length = 100
with(colors){
push(color)
push(...values)
push(length)
}
colors // (8) ["red", "green", "blue", "black", 1, 2, 3, 7]
举例:我想要的是length用外面的而不是里面的(该数组的),Symbol.unscopables可以帮助你,Symbol.unscopables: 指用于指定对象值,其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称。
Array.prototype[Symbol.unscopables].length = true
var values = [1,2,3]
colors = ['red', 'green', 'blue']
color = 'black'
length = 100
with(colors){
push(color)
push(...values)
push(length)
}
colors // (8) ["red", "green", "blue", "black", 1, 2, 3, 100]
总结
以上这些行为有危险性,毕竟改变了语言本身的执行方式,除非你知道你自己在干嘛!
网友评论