javascript基础系列之Symbol

javscript基础系列之symbol

常见用法

1
2
3
4
5
6
7
8
9
const sym = Symbol("hello");
console.log(typeof sym); // symbol
console.log(sym.toString()); // Symbol(hello)
console.log(sym == 2); // false
console.log(Symbol('world') === Symbol("world")); // false
console.log(typeof Symbol() === "symbol"); //typeof类型判断
console.log(typeof Symbol("hello") === "symbol"); //typeof类型判断
console.log(typeof Symbol.iterator === "symbol"); //typeof类型判断
console.log(JSON.stringify({[Symbol("world")]: "world"})) // JSON.stringify会自动过滤Symbol键值对
1
2
3
4
5
6
7
8
// const sym1 = new Symbol();  // Uncaught TypeError: Symbol is not a constructor
// Object.getOwnPropertySymbols
const obj = {}; // 不能在字面量中直接使用Symbol作为键名
const a = Symbol("a"); // 局部Symbol
const b = Symbol.for("b"); // 全局Symbol
obj[a] = "local Symbol";
obj[b] = "global Symbol";
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(a), Symbol(b)], 查找对象的Symbol属性

静态属性

Symbol.length

1
2
// Symbol.length 长度属性 0 ,具体用途不太清楚
console.log(Symbol.length);

Symbol函数对象属性

Symbol.iterator

1
2
3
4
5
6
7
8
9
10
11
12
13
// Symbol.iterator  返回对象默认迭代器的方法,可以被for...of遍历到
// 创建自定义的迭代器
const myIterator = {};
myIterator[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
}
console.log([...myIterator]); // [1,2,3]

for (const iterator of myIterator) {
console.log(iterator); // 1 2 3
}

Symbol.asyncIterator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Symbol.asyncIterator  // 异步可迭代对象
const asyncIterable = {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
if(this.i < 3) {
return Promise.resolve({ value: this.i++, done: false });
}
return Promise.resolve({done: true});
}
}
}
};
(async function() {
for await (const num of asyncIterable) {
console.log(num); // 0 1 2
}
})();

Symbol.match

1
2
3
4
5
// Symbol.match 是否为正则对象(配置项)
const reg = /hello/;
reg[Symbol.match] = false; // 设置后会认为reg是字符串,不是正则对象,不设置对只支持字符串的方法会报错
console.log("/hello/".startsWith(reg)); // true, startsWith参数只支持字符串,如果是正则表达式会报错。
console.log("/world/".endsWith(reg)); // false

Symbol[replace|search|split]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Symbol.replace 替换所匹配字符串时所调用的方法
// Symbol.search 检索所匹配字符串时所调用的方法
// Symbol.split 拆分所匹配字符串时所调用的方法
const obj = {
value: "world",
splitVal: " "
}
obj[Symbol.replace] = function(str) { // 调用Object.prototype.replace方法会执行此方法
return "hello " + str;
}
obj[Symbol.search] = function(str) { // 调用Object.prototype.search方法会执行此方法
return str.toLowerCase().indexOf(this.value);
}
obj[Symbol.split] = function(str) { // 调用Object.prototype.split方法会执行此方法
const index = str.toLowerCase().indexOf(this.value);
return [str.substring(0, index-1), str.substring(index)];
}
// console.log('alan'.replace(obj)); // hello alan
// console.log('Hello World'.search(obj)); // 6
console.log('Hello World'.split(obj)); // ["hello", "world"]

Symbol.hasInstance

1
2
3
4
5
6
// Symbol.hasInstance 确定对象是否是构造函数的实例
const MyArray = {}
MyArray[Symbol.hasInstance] = function(val){
return Array.isArray(val);
}
console.log([] instanceof MyArray); // true

Symbol.isConcatSpreadable

1
2
3
4
5
6
// Symbol.isConcatSpreadable 配置Array.prototype.concat是否展开数组元素
const arr1 = [1, 2, 3];
const arr2 = ['a', 'b', 'c'];
console.log(arr1.concat(arr2)); // [1,2,3,'a','b','c']
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr1.concat(arr2)); // [1,2,3, ['a', 'b', 'c']]

Symbol.unscopables

1
2
3
4
5
6
7
8
9
10
// Symbol.unscopables 从with环境中排除属性
const obj = {
habit: "apple"
}
obj[Symbol.unscopables] = { // 排除with中对habit属性的访问
habit: true
}
with(obj) {
console.log(habit); // Uncaught ReferenceError: habit is not defined
}

Symbol.species

1
2
3
4
5
6
7
8
9
10
// Symbol.species 允许子类覆盖对象的默认构造函数
class MyArray extends Array {
static get [Symbol.species]() { // 覆盖species到父级的Array构造函数
return Array;
}
}
const arr = new MyArray(1,2,3);
const mapped = arr.map(x => x * 2);
console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true

Symbol.toPrimitive

1
2
3
4
5
6
7
8
9
// Symbol.toPrimitive 对象转换为对应的原始值时,会调用此函数
const obj = {};
obj[Symbol.toPrimitive] = function(val) { // 转换为原始值时,类似钩子
if(val === "number") {
return 10;
}
return null;
}
console.log(+obj); // 10

Symbol.toStringTag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Symbol.toStringTag 对象默认描述的字符串值
const { toString } = Object.prototype;
console.log(toString.call("hello")); // [object String]
console.log(toString.call(null)); // [object Null]
console.log(toString.call(new Map())); // [object Map]

// 自定义的class
class ValidatorClass {}
console.log(toString.call(new ValidatorClass())); // [object Object]

class ValidatorClass2 {
get [Symbol.toStringTag]() {
return "Validator";
}
}
console.log(toString.call(new ValidatorClass2())); // [object Validator]

静态方法

Symbol.for

1
2
3
4
5
6
7
8
9
10
11
 // Symbol.for(key)  根据给定的key, 从运行时的symbol注册表中找到对应的symbol,找到了就返回,找不到就创建,并放入到全局Symbol注册表中
console.log(Symbol.for("hello")); // Symbol(hello) 创建一个Symbol并放入symbol注册表中
console.log(Symbol.for('hello') === Symbol.for('hello')); // true
console.log(Symbol('hello') === Symbol('hello')); // false

// Symbol.keyFor 查询全局Symbol,查到返回key值,否则返回undefined
const globalSymbol = Symbol.for("hello");
console.log(Symbol.keyFor(globalSymbol)); // 'hello'

const localSymbol = Symbol("world");
console.log(Symbol.keyFor(localSymbol)); // undefined

实例属性

Symbol.prototype.description

1
2
3
// Symbol.prototype.description  对该Symbol对象的描述
console.log(Symbol("hello").description); // hello
console.log(Symbol.iterator.description); // Symbol.iterator

实例方法

Symbol.prototype[toSource|toString]

1
2
3
4
// Symbol.prototype.toSource  兼容性很差,废弃
// Symbol.prototype.toString 返回Symbol对象的字符串表示
const sym = Symbol("hello");
console.log(sym.toString()); // Symbol(hello) 字符串

Symbol.prototype.valueOf

1
2
3
4
// Symbol.prototype.valueOf 返回Symbol对象
const sym1 = Symbol("world");
console.log(sym1.valueOf()); // Symbol(world) Symbol类型的值

Symbol.prototype[@@toPrimitive]

1
// Symbol.prototype[@@toPrimitive] 返回Symbol原始值,参考Symbol.prototype.toPrimitive
参考文档

Symbol