基本类型
在 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.+symorsym | 0). - 使用宽松相等时,
Object(sym) == symreturnstrue. - 这会阻止你从一个 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
使用场景
-
在不关心它具体的值是什么,又需要具备唯一性的时候。
let arr = [{ name: 'wq' }, { name: 'wq' }] arr.forEach(item=> { item.id = Symbol() }) -
可复用代码,参照上面的
Symbol.replace