目录
5.Symbol.match/replace/search/species/split
Symbol 类型
symbol的实例是唯一的不可变的, 用于确保对象的属性不重复
使用方式 : 调用 Symbol( 标识 ) 函数 返回一个符号
const a = Symbol('a');
const obj = {
[a]: 1
};
console.log(obj); // {Symbol(a): 1}
Symbol() 函数不可以 new
符号代表唯一的值, 但是我就想用一个符号呢 ?
通过 Symbol.for () 创建一个全局符号
const a = Symbol('a');
const b = Symbol.for('b');
const b2 = Symbol.for('b');
const obj = {
[a]: 1,
[b]: 2,
[b2]: 3
};
console.log(b === b2); // true
console.log(obj); // {Symbol(a): 1, Symbol(b): 3}
// key通过覆盖替换 不必疑惑
// b 和 b2 指向同一个符号
通过 Symbol.keyFor () 查看全局符号 参数是全局符号 返回符号的描述
如果传的不是全局符号 返回undefined
如果传的不是符号 报错
const b = Symbol.for('b123');
console.log(Symbol.keyFor(b)); // b123
常用的内置符号 Symbol的工厂函数
1.Symbol.asyncIterator
此方法返回对象的默认的异步迭代器, 可由 for - await - of 使用
// for await of 会调用对象以[Symbol.asyncIterator]为键的函数 该函数返回异步的generator
class Foo {
constructor(n) {
this.n = n;
this.i = 0;
}
async *[Symbol.asyncIterator]() {
while (this.i < this.n) {
yield Promise.resolve(this.i++);
}
}
}
async function fn() {
const p = new Foo(5);
for await (const x of p) {
console.log(x);
}
}
fn() // 0 1 2 3 4
2.Symbol.hasInstance
该方法判断一个对象是不是构造函数的实例 , 由 instanceof 使用 , 也就是说 instanceof 检测数据类型时的原理就是调用了 Symbol.hasInstance
这个属性被定义在 Function 上 所以函数和类都有 Symbol.hasInstance 方法
class Foo {
static [Symbol.hasInstance]() {
return false
}
}
const demo = new Foo()
console.log(Foo[Symbol.hasInstance](demo)); // false
console.log(demo instanceof Foo); // false
// 之所以会使false 是因为我重新定义了 [Symbol.hasInstance]() 函数 默认情况下是true
3.Symbol.isConcatSpreadable
这个符号作为属性返回一个布尔值 , 决定拼接数组时是否展开 , 默认为true
let arr = [0];
const arr1 = [1];
arr1[Symbol.isConcatSpreadable] = true;
const arr2 = [2];
arr2[Symbol.isConcatSpreadable] = false;
arr = arr.concat(arr1, arr2)
console.log(arr); // [0, 1, Array(1)]
4.Symbol.iterator
此方法返回对象默认的迭代器 for of 循环时就会调用对象的 Symbol.iterator函数(返回一个迭代器对象)
class Foo {
constructor(n) {
this.n = n;
this.i = 0;
}
*[Symbol.iterator]() {
while (this.i < this.n) {
yield this.i++;
}
}
}
async function fn() {
const p = new Foo(5);
for (const x of p) {
console.log(x);
}
}
fn()
5.Symbol.match/replace/search/species/split
Symbol允许我们自定义某些函数, 以改变函数原本的行为 .
接下来简单说明一下
match
match 方法返回字符串匹配正则表达式的结果
即使参数传入的不是正则表达式 也会调用 new RegExp('xxx') 转为正则表达式
原始的match
const str = 'abcdefg';
const reg = /b/;
const res = str.match(reg);
console.log(res); // ['b', index: 1, input: 'abcdefg', groups: undefined]
自定义的match
const str = 'abcdefg';
const a = {
[Symbol.match](target) {
console.log(target);
return false
}
};
const res = str.match(a);
console.log(res); // false
replace
replace 方法传入的参数替换字符串匹配正则表达式的结果返回新的字符串
即使参数传入的不是正则表达式 也会调用 new RegExp('xxx') 转为正则表达式
原始的replace
let str = 'abcdefg';
str = str.replace('abc', '111') // 等价于 str = str.replace(/abc/, '111')
console.log(str); // '111defg'
自定义的replace
// 自定义 replace
let str1 = '123';
const target = [];
target[Symbol.replace] = (...rest) => {
return `${rest[1]}${rest[0]}`
}
console.dir(target);
str1 = str1.replace(target, '333')
console.log(str1); // 333123
search
search 方法传入的参数为目标字符中出现的位置 会调用参数的 Symbol.search 进行求值
即使参数传入的不是正则表达式 也会调用 new RegExp('xxx') 转为正则表达式
let str = 'abcdefg';
const res = str.search('d');
console.log(res); // 3
// 自定义 search 方法
const demo = {};
demo[Symbol.search] = (target) => {
return 5
}
console.log(str.search(demo)); 5
// 执行原理
// 调用String.proptype.search()
// 该方法调用正则表达式的[Symbol.search]
console.log(String.prototype.search);
console.log(RegExp.prototype[Symbol.search]);
species
如果你定义一个扩展数组类 那么这个类的实例对象默认是同时指向父类和Array这个构造函数
如果你想只返回Array替换掉他的父类 那么可以使用静态方法
// static get [Symbol.species]() {
// return Array;
// }
class Arr extends Array { };
class Arr1 extends Array {
static get [Symbol.species]() {
return Array;
}
};
const d1 = new Arr()
console.log(d1.concat([]) instanceof Arr); // true
console.log(d1 instanceof Array); // true
const d2 = new Arr1()
console.log(d2.concat([]) instanceof Arr1); // false
console.log(d2 instanceof Array); // true
split
split() 方法以指定字符串分割调用对象, 在此方法调用时会使用以 Symbol.split 为键的函数
const str = 'abcd'
const res = str.split('') // 等价于 str.split(new RegExp(''))
console.log(res); // ['a', 'b', 'c', 'd']
// 自定义 split
const reg = new RegExp('')
reg[Symbol.split] = (target) => {
return 123
}
const res1 = str.split(reg)
console.log(res1); // 123
6.Symbol.toPrimitive
对象转成字符串会被 String 函数转化成 '[object Object]'
如果想对对象进行加减运算 , 你可以通过定义 Symbol.toPrimitive 来实现
const obj = {};
console.log(1 + obj); // 1[object Object]
console.log(1 - obj); // NaN
console.log(String(obj)); // [object Object]
obj[Symbol.toPrimitive] = (hint) => {
switch (hint) {
case 'string':
return '1';
case 'number':
return 0;
default:
return 'none'
}
}
console.log(1 + obj); // 1none
console.log(1 - obj); // 1
console.log(String(obj)); // 1
7.Symbol.toStringTag
对象使用 toString 方法时 会使用 Symbol.toStringTag 的返回值作为数组中的第二项
我的理解就是 Symbol.toStringTag 方法可以改变(实例对象)的 toString 输出指定的构造函数
const obj1 = {}
console.log(obj1.toString()); // [object Object]
class P { }
const obj2 = new P()
console.log(obj2.toString()); // [object Object]
obj2[Symbol.toStringTag] = 'P'
console.log(obj2.toString()); // [object P]
8.Symbol.unscopables
在 with 语句中 访问的变量默认都会访问器参数对象的属性
使用 Symbol.unscopables 可以指定对象哪些属性不在with环境下生效
// 例如
const obj = {
a: 'a',
b: 'b'
}
obj[Symbol.unscopables] = {
b: true
}
with (obj) {
console.log(a);
console.log(b); // 报错 b is not defined
}