除了自己创建的 symbol,JavaScript 还内建了一些在 ECMAScript 5 之前没有暴露给开发者的 symbol,它们代表了内部语言行为。ES5的一个中心主旨时将JavaScript中的一些神奇的部分暴露出来,并详细定义了这些开发者们当初模拟不了的功能。ES6延续了这个传统,新标准主要通过在原型链上定义与Symbol相关的属性来暴露更多的语言内部逻辑。
ES6开放了之前JavaScript中常见的内部操作,并通过预定义一些well-known Symbol来表示。每一个这类Symbol都是Symbol对象的一个属性,例如Symbol.match.
All static properties of the Symbol constructor are Symbols themselves, whose values are constant across realms. They are known as
well-known Symbols, and their purpose is to serve as “protocols” for certainbuilt-in JavaScript operations, allowing users to customize the language’s behavior. For example, if a constructor function has a method withSymbol.hasInstanceas its name, this method will encode its behavior with theinstanceofoperator.
Prior to well-known Symbols, JavaScript used normal properties to implement certain built-in operations. For example, the
JSON.stringifyfunction will attempt to call each object’stoJSON()method, and theStringfunction will call the object’stoString()andvalueOf()methods. However, as more operations are added to the language, designating each operation a “magic property” can break backward compatibility and make the language’s behavior harder to reason with. Well-known Symbols allow the customizations to be “invisible” from normal code, which typically only read string properties.
The static properties are all well-known Symbols. In these Symbols’ descriptions, we will use language like “Symbol.hasInstance is a method determining…”, but bear in mind that this is referring to the semantic of an object’s method having this Symbol as the method name (because well-known Symbols act as “protocols”), not describing the value of the Symbol itself.
The Symbol.hasInstance well-known symbol is used to determine if a constructor object recognizes an object as its instance. The instanceof operator’s behavior can be customized by this symbol.
一个在执行instanceof时调用的内部方法,用于检测对象的继承信息。每一个函数中都有一个Symbol.hasInstance方法,用于确定对象是否为函数的示例。该方法在Function.prototype中定义,所以所有函数都继承了instance of属性的默认行为。为了确保Symbol.hasInstance不会被意外重写,该方法被定义为不可写、不可配置并且不可枚举。Symbol.hasInstance方法只接受一个参数,即要检查的值。如果传入的值时函数的实例,则返回true。
Array[Symbol.hasInstance](obj)
类似于
obj instanceof Array
比如
class Array1 {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
// true
console.log([] instanceof Array1);
本质上,ES6只是将instanceof操作符重新定义为此方法的简写语法。现在引入方法调用后,就可以随意改变instanceof 的运行方式了。比如
function MyObject() {
}
let obj = new MyObject();
// true
console.log(obj instanceof MyObject);
// 只有通过Object.defineProperty方法才能修改一个不可写属性
// 修改Symbol.hasInstance,为其定义一个总是返回false的新函数
Object.defineProperty(MyObject, Symbol.hasInstance, {
value: function (v) {
return false;
}
})
obj = new MyObject();
// false
console.log(obj instanceof MyObject);
只有通过Object.defineProperty方法才能修改一个不可写属性。 上面的示例调用这个方法来改写了Symbol.hasInstance,为其定义了一个总是返回false的函数,即使obj实际上确实是MyObject类的实例,在调用defineProperty之后,instanceof运算符也是返回false.
当然也可以基于任意条件,通过值检测来确定被检测的是否为实例。举个例子,可以将1~100的数字定义为一个特殊数字类型的实例。
function SpecialNumber(){
}
Object.defineProperty(SpecialNumber,Symbol.hasInstance,{
value: function(v){
return (v instanceof Number) && (v >= 1 && v <= 100);
}
});
var two = new Number(2),
zero = new Number(0);
// true
console.log(two instanceof SpecialNumber);
// false
console.log(zero instanceof SpecialNumber);