和 Symbol 沾点边的东西

Reflect 和 Object 的一些静态方法

Object.preventExtensions() 和 Reflect.preventExtensions()

new properties cannot be added, existing properties can be removed Object.preventExtensions() 与 Reflect.preventExtensions() 的不同点
如果 Reflect.preventExtensions() 的 target 参数不是一个对象(是原始值),那么将造成一个 TypeError 异常。 对于 Object.preventExtensions() 方法, 非对象的 target 参数将被强制转换为对象。

Object.freeze()

Freezing an object prevents extensions and makes existing properties non-writable and non-configurable.
A frozen object can no longer be changed: new properties cannot be added, existing properties cannot be removed, their enumerability, configurability, writability, or value cannot be changed, and the object's prototype cannot be re-assigned. freeze() returns the same object that was passed in.

Object.seal()

Sealing an object prevents extensions and makes existing properties non-configurable. A sealed object has a fixed set of properties: new properties cannot be added, existing properties cannot be removed, their enumerability and configurability cannot be changed, and it's prototype cannot be re-assigned. Values of existing properties can still be changed as long as they are writable. seal() returns the same object that was passed in.

Reflect 的静态方法

  • Reflect.deleteProperty()
    The static Reflect.deleteProperty() method allows to delete properties. It is like the delete operator as a function.
  • Reflect.defineProperty()
    The static Reflect.defineProperty() method is like Object.defineProperty() but returns a Boolean.
  • Reflect.getOwnPropertyDescriptor()
    The static Reflect.getOwnPropertyDescriptor() method is similar to Object.getOwnPropertyDescriptor(). It returns a property descriptor of the given property if it exists on the object, undefined otherwise.
  • Reflect.getPrototypeOf()
    The static Reflect.getPrototypeOf() method is almost the same method as Object.getPrototypeOf(). It returns the prototype (i.e. the value of the internal [[Prototype]] property) of the specified object.
  • Reflect.has()
    The static Reflect.has() method works like the in operator as a function.
  • Reflect.ownKeys()
    The static Reflect.ownKeys() method returns an array of the target object's own property keys.
  • Reflect.setPrototypeOf()
    The static Reflect.setPrototypeOf() method is the same method as Object.setPrototypeOf(), except for it's return type. It sets the prototype (i.e., the internal [[Prototype]] property) of a specified object to another object or to null, and returns true if the operation was successful, or false otherwise.

Reflect.setPrototypeOf() and Object.setPrototypeOf()

Reflect.setPrototypeOf() is same as Object.setPrototypeOf().

typescript
1
2
3
4
5
6
7
8
9
10
class Human {}
class SuperHero {}

// Set the instance properties
Object.setPrototypeOf(SuperHero.prototype, Human.prototype);

// Hook up the static properties
Object.setPrototypeOf(SuperHero, Human);

const superMan = new SuperHero();

Reflect.ownKeys() and Object.keys()

  • The Reflect.ownKeys method returns an array of the target object's own property keys. It's return value is equivalent to Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)).
    • Object.getOwnPropertyNames() returns an array whose elements are strings corresponding to the enumerable and non-enumerable properties found directly in a given object obj. The ordering of the enumerable properties in the array is consistent with the ordering exposed by a for...in loop (or by Object.keys()) over the properties of the object. According to ES6, the non-negative integer keys of the object (both enumerable and non-enumerable) are added in ascending order to the array first, followed by the string keys in the order of insertion.
    • Object.getOwnPropertySymbols() is similar to Object.getOwnPropertyNames(), you can get all symbol properties of a given object as an array of symbols. Note that Object.getOwnPropertyNames() itself does not contain the symbol properties of an object and only the string properties.
  • Object.keys() returns an array whose elements are strings corresponding to the enumerable properties found directly upon object. The ordering of the properties is the same as that given by looping over the properties of the object manually.

Symbol

symbol 是唯一标识符的基本类型

  • symbol 属性不参与 for..in 循环,Object.keys(obj) 也会忽略它们。
  • 相反,Object.assign 会同时复制字符串和 symbol 属性,由于指向同一片内存当然会复制。

全局 Symbol

该调用会检查全局注册表,如果有一个描述为 key 的 symbol,则返回该 symbol,否则将创建一个新 symbol(Symbol(key)),并通过给定的 key 将其存储在全局注册表中,key 只能为 string。

js
1
2
3
4
5
6
7
8
// 从全局注册表中读取
let id = Symbol.for("id"); // 如果该 symbol 不存在,则创建它

// 再次读取(可能是在代码中的另一个位置)
let idAgain = Symbol.for("id");

// 相同的 symbol
alert( id === idAgain ); // true

对于全局 symbol,Symbol.for(key) 按名字返回一个 symbol。相反,通过全局 symbol 返回一个名字,我们可以使用 Symbol.keyFor(sym)

js
1
2
3
4
5
6
7
// 通过 name 获取 symbol
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");

// 通过 symbol 获取 name
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id

Symbol.keyFor 内部使用全局 symbol 注册表来查找 symbol 的键。所以它不适用于非全局 symbol。如果 symbol 不是全局的,它将无法找到它并返回 undefined

隐式转换 implicit conversion

为了进行转换,JavaScript 尝试查找并调用三个对象方法:

  1. 调用 obj[Symbol.toPrimitive](hint) —— 带有 symbol 键 Symbol.toPrimitive(系统 symbol)的方法,如果这个方法存在的话,
  2. 否则,如果 hint 是 "string" —— 尝试调用 obj.toString()obj.valueOf(),无论哪个存在。
  3. 否则,如果 hint 是 "number""default" —— 尝试调用 obj.valueOf()obj.toString(),无论哪个存在。

关于所有原始转换方法,有一个重要的点需要知道,就是它们不一定会返回 “hint” 的原始值。
没有限制 toString() 是否返回字符串,或 Symbol.toPrimitive 方法是否为 "number" hint 返回数字。
唯一强制性的事情是:这些方法必须返回一个原始值,而不是对象。
由于历史原因,如果 toStringvalueOf 返回一个对象,则不会出现 error,但是这种值会被忽略(就像这种方法根本不存在)。这是因为在 JavaScript 语言发展初期,没有很好的 “error” 的概念。
相反,Symbol.toPrimitive 更严格,它 必须 返回一个原始值,否则就会出现 error。


hint 可以理解为根据场景自动判断更倾向于转换成 string 还是 number,如果无法判断倾向于转化哪一个,就是 default。下面我们来模拟一下转化过程中获取 hint 值的过程

js
1
2
3
4
5
6
7
8
const obj = { 
    [Symbol.toPrimitive](hint) { 
        console.log(`hint: ${hint}`); 
    } 
}; 
const arr = []
arr[obj]
// hint: string
js
1
2
3
4
5
6
7
const obj = { 
    [Symbol.toPrimitive](hint) { 
        console.log(`hint: ${hint}`); 
    } 
}; 
-obj
// hint: number
js
1
2
3
4
5
6
const a = [1, 2, 3]
a[Symbol.toPrimitive] = function (hint) {
    console.log(`hint: ${hint}`);
}
'' + a
// hint: default