前言
元编程是指编写代码来操作自身行为的编程范式。在 JavaScript 中,Symbol 是元编程的一个关键组成部分。在 ES6 中,引入了 Symbol 类型。ES12 中,Symbol 的改进使得元编程变得更加细颗粒度,提供了更多的自定义机会。本文将介绍在 ES12 中 Symbol 的改进。
Symbol 的概述
Symbol 是一种原始数据类型,表示不可变的、唯一的值。相同参数的 Symbol 函数调用会返回一个新的、不同的 Symbol 值。因为 Symbol 值不会与其他 Symbol 值相等,所以它们可以作为对象属性的唯一标识符。
只有通过 Symbol 函数创建的值才是真正的 Symbol 值,其他参数类型的值都不是。
const sym1 = Symbol('foo'); typeof sym1 // "symbol" const sym2 = Symbol('foo'); sym1 === sym2 // false
Symbol 值可以用作对象属性的键名。
const obj = {}; const a = Symbol('a'); const b = Symbol('b'); obj[a] = 'Hello'; obj[b] = 'World'; obj // { [Symbol(a)]: 'Hello', [Symbol(b)]: 'World' }
Symbol 的改进
Symbol.hasInstance
在 ES12 中,Symbol.hasInstance 属性可以对 instanceof 进行自定义行为。Symbol.hasInstance 属性是一个 Symbol 值,用于定义对象的一个方法,当该对象被 instanceof 运算符调用时会被执行。
class MyNumber { static [Symbol.hasInstance](value) { return typeof value === 'number'; } } console.log(1 instanceof MyNumber); // true console.log('a' instanceof MyNumber); // false
这里定义的 MyNumber 类,当执行 value instanceof MyNumber
时,会执行类中的 [Symbol.hasInstance]
方法,如果该方法返回 true,则 1 instanceof MyNumber 的结果为 true,否则结果为 false。
Symbol.matchAll
Symbol.matchAll 用于全局匹配字符串。它是 RegExp.prototype.matchAll 方法生成的迭代器对象的属性。
const str = 'hello world, hello everyone'; const re = /hello/g; for (const match of str.matchAll(re)) { console.log(match); }
输出结果为:
["hello", index: 0, input: "hello world, hello everyone", groups: undefined] ["hello", index: 13, input: "hello world, hello everyone", groups: undefined]
Symbol.replace
Symbol.replace 属性是一个 Symbol 值,用于定义对象的一个方法,当该对象被 String.prototype.replace 方法调用时会被执行。Symbol.replace 方法接收两个参数,第一个参数是被匹配到的子字符串,第二个参数是将被替换的字符串。
const myReplacer = { [Symbol.replace](str, replaceStr) { return str.replace(/hello/g, replaceStr); } }; const str = 'hello world, hello everyone'; console.log(str.replace(myReplacer, 'hi'));
输出结果为:
hi world, hi everyone
这里定义了 myReplacer 对象,在对象中定义了 [Symbol.replace]
方法,该方法会将字符串中的 "hello" 替换为传入的第二个参数。
Symbol.search
Symbol.search 属性是一个 Symbol 值,用于定义对象的一个方法,当该对象被 String.prototype.search 方法调用时会被执行。Symbol.search 方法接收一个参数,即需要被搜索的字符串。
const mySearcher = { [Symbol.search](str) { return str.indexOf('world'); } }; const str = 'hello world, hello everyone'; console.log(str.search(mySearcher)); // 6
这里定义了 mySearcher 对象,在对象中定义了 [Symbol.search]
方法,该方法用于搜索字符串中包含 "world" 的位置。
Symbol.species
Symbol.species 属性是一个 Symbol 值,用于在派生构造函数中覆盖基类构造函数。Symbol.species 可以提供一个函数,该函数用于创建派生对象。
-- -------------------- ---- ------- ----- ------- ------- ----- - ------ --- ------------------ - ------ ------ - - ----- --- - --- ---------- -- --- ----- -------- - ------------ --------------- ---------- --------- -- ---- -------------------- ---------- --------- -- -----
这里定义了一个 MyArray 类,当调用 slice 方法时,会使用到 Symbol.species。在上面的例子中,cloneArr 不是 MyArray 类型,而是 Array 类型,因为 MyArray 重写了 Symbol.species,返回的是 Array。
Symbol.split
Symbol.split 属性是一个 Symbol 值,用于定义对象的一个方法,当该对象被 String.prototype.split 方法调用时会被执行。Symbol.split 方法接收两个参数,第一个参数是要被分割的字符串,第二个参数是分割次数的上限。
const mySplitter = { [Symbol.split](str, limit) { return str.split('o', limit); } }; const str = 'hello world, hello everyone'; console.log(str.split(mySplitter, 2)); // [ 'hell', ' w' ]
这里定义了 mySplitter 对象,在对象中定义了 [Symbol.search]
方法,该方法用于将字符串按照 "o" 分割成数组,最多分割两次。
结论
在 ES12 中,Symbol 的改进使得元编程变得更加细颗粒度,提供了更多的自定义机会。通过 Symbol.hasInstance、Symbol.matchAll、Symbol.replace、Symbol.search、Symbol.species 和 Symbol.split 属性,我们可以实现更加灵活的元编程。希望本文能够对你理解元编程有所帮助。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/66ff901e1b0bf82c71cc012b