JS基本类型-Symbol

129 阅读4分钟

基本类型

在 JavaScript中,基本类型(基本数值、基本数据类型)是一种既非对象也无方法或属性的数据。有 7 种基本数据类型:

  • string
  • number
  • boolean
  • symbol
  • undefined
  • null
  • bigint

最新的 ECMAScript 标准定义了 8 种数据类型,除了上述的七种基本数据类型,还包括对象(Object)。

Symbol

symbol 是一种基本数据类型(primitive data type)。Symbol() 函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。

每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数据类型仅有的目的。

全局共享的 Symbol

使用 Symbol.for() 方法和 Symbol.keyFor() 方法从全局的 symbol 注册表设置和取得 symbol。

Symbol.for()

Symbol.for(key)  方法会根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。

示例:

Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol


Symbol.for("bar") === Symbol.for("bar"); // true,证明了上面说的
Symbol("bar") === Symbol("bar"); // false,Symbol() 函数每次都会返回新的一个 symbol


var sym = Symbol.for("mario");
sym.toString();
// "Symbol(mario)",mario 既是该 symbol 在 symbol 注册表中的键名,又是该 symbol 自身的描述字符串

为了防止冲突,最好给你要放入 symbol 注册表中的 symbol 带上键前缀。

Symbol.for("mdn.foo");
Symbol.for("mdn.bar");

Symbol.keyFor()

Symbol.keyFor(sym)  方法用来获取全局 symbol 注册表中与某个 symbol 关联的键。

// 创建一个全局 Symbol
var globalSym = Symbol.for("foo");
Symbol.keyFor(globalSym); // "foo"

创建一个可迭代的对象

Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被 for...of 循环使用。

class Foo {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
}

console.log(...new Foo()); // 1, 2, 3

const someObj = {
  test: '123',
  *[Symbol.iterator]() {
    yield "a";
    yield "b";
  },
};

for(let a of someObj) {
  console.log(a)
} 
// a
// b


for(let a in someObj) {
  console.log(a)
}
// test

console.log(...someObj) // a b

for...in定义:迭代一个对象上的除Symbol以外的可枚举属性,包括继承的可枚举属性。

使 String.prototype.startsWith() 可使用正则表达式

str.startsWith(searchString[, position])

startsWith 的第一个参数为字符串,如何使用正则表达式呢?

var re = /foo/;
console.log("/foo/".startsWith(re));

会报错,如下

console.log("/foo/".startsWith(re)); // true
                    ^

TypeError: First argument to String.prototype.startsWith must not be a regular expression
    at String.startsWith (<anonymous>)
    at Object.<anonymous> (d:\vuespace\blog\test.js:2:21)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:75:12)
    at internal/main/run_main_module.js:17:47

TypeError: String.prototype.startsWith的第一个参数不能为正则表达式。

利用Symbol.match,并将它设置为false,使用 match 属性的表达式检查会认为该对象不是正则表达式对象,startsWith就不会报错。

var re = /foo/;
re[Symbol.match] = false;
console.log("/foo/".startsWith(re)); // true
console.log("/baz/".endsWith(re));   // false

Symbol.replace

定义一个类(MyReplace)的replace方法,可达到复用的目的。

class MyReplace {
  constructor(value) {
    this.value = value;
  }
  [Symbol.replace](string) {
    return string.replace(/[o]/g, this.value);
  }
}

console.log('foo'.replace(new MyReplace('bar'))); // fbarbar

console.log('foo'.replace(/o/g, 'bar')) // fbarbar

Symbol 类型转换

当使用 symbol 值进行类型转换时需要注意一些事情:

  • 尝试将一个 symbol 值转换为一个 number 值时,会抛出一个 TypeError 错误 (e.g. +sym or sym | 0).
  • 使用宽松相等时,Object(sym) == sym returns true.
  • 这会阻止你从一个 symbol 值隐式地创建一个新的 string 类型的属性名。例如,Symbol("foo") + "bar" 将抛出一个 TypeError (can't convert symbol to string).
  • "safer" String(sym) conversion的作用会像 symbol 类型调用 Symbol.prototype.toString() 一样,但是注意 new String(sym) 将抛出异常。

Symbols 与 for...in 迭代

Symbols 在 for...in迭代中不可枚举。另外,Object.getOwnPropertyNames() 不会返回 symbol 对象的属性,但是你能使用 Object.getOwnPropertySymbols() 得到它们。

var obj = {};

obj[Symbol("a")] = "a";
obj[Symbol.for("b")] = "b";
obj["c"] = "c";
obj.d = "d";

for (var i in obj) {
   console.log(i); // logs "c" and "d"
}

console.log(obj)
// { c: 'c', d: 'd', [Symbol(a)]: 'a', [Symbol(b)]: 'b' }

Symbols 与 JSON.stringify()

当使用 JSON.stringify() 时,以 symbol 值作为键的属性会被完全忽略:

console.log(JSON.stringify(obj))
// {"c":"c","d":"d"}

Symbol 包装器对象作为属性的键

当一个 Symbol 包装器对象作为一个属性的键时,这个对象将被强制转换为它包装过的 symbol 值:

var sym = Symbol("foo");
var obj = {[sym]: 1};
obj[sym];            // 1
obj[Object(sym)];    // still 1

使用场景

  1. 在不关心它具体的值是什么,又需要具备唯一性的时候。

    let arr = [{
      name: 'wq'
    }, {
      name: 'wq'
    }]
    
    arr.forEach(item=> {
      item.id = Symbol()
    })
    
  2. 可复用代码,参照上面的 Symbol.replace

其他属性及方法请参考

Symbol - JavaScript | MDN (mozilla.org)