在 ECMAScript 2015 中,我们认识了一种新的基本数据类型 Symbol(符号) 。在 ECMAScript 2019 中,Symbol 得到了更多的新概念、新特性以及更广泛的应用场景。本文主要介绍 ECMAScript 2019 中关于 Symbol 的新概念与用法,旨在帮助前端开发者更好地理解 Symbol,掌握它的使用方法,提高开发效率和代码质量。
Symbol 的定义和基本用法
Symbol 是 ECMAScript 6 中引入的一种新的基本数据类型,表示一个独一无二的值。它通过 Symbol() 函数创建一个独一无二的值,在使用时可以通过变量名来访问。
let sym1 = Symbol(); let sym2 = Symbol(); console.log(sym1 === sym2); // false let obj = { [sym1]: "value" }; console.log(obj[sym2]); // undefined console.log(obj[sym1]); // "value"
在上面的代码中,我们创建了两个 Symbol 类型的变量 sym1 和 sym2,由于每个 Symbol 的值都是独一无二的,因此两个变量的值也不相等。使用 Symbol() 函数创建的值是不能通过文字或表达式来创建的,只能通过函数来创建。
Symbol 的新概念
在 ECMAScript 2019 中,Symbol 得到了更多的新概念,包括如下几种。
Symbol.hasInstance
Symbol.hasInstance 属性是一个函数,用来判断一个对象是否为另一个对象的实例。
class Foo { static [Symbol.hasInstance](obj) { return obj instanceof Array; } } console.log([] instanceof Foo); // true let obj = {}; console.log(obj instanceof Foo); // false
在上面的代码中,我们创建了一个类 Foo,并定义了 Symbol.hasInstance 属性,通过该属性让 JavaScript 引擎在检查 obj instanceof Foo 时使用该函数进行判断。由于 [] 是一个数组,因此它是 Foo 的一个实例,而 obj 不是一个数组,因此它不是 Foo 的实例。
Symbol.asyncIterator
Symbol.asyncIterator 属性是一个函数,表示一个异步迭代器接口,用于定义异步遍历的行为。
-- -------------------- ---- ------- ----- --- - --- -- --- ----- -------- ------ - --- ----- ------ ----- -- ---- - ------------------- - - -------
在上面的代码中,我们通过 for await...of 循环遍历一个数组,并使用 Symbol.asyncIterator 属性来表示该数组是一个异步迭代器。
Symbol.match
Symbol.match 属性是一个函数,用于确定一个对象是否具有正则表达式的匹配能力。
class Foo { [Symbol.match](str) { return str.indexOf("foo") !== -1; } } console.log("foobar".match(new Foo())); // true console.log("bar".match(new Foo())); // false
在上面的代码中,我们创建了一个类 Foo,并定义了 Symbol.match 属性,通过该属性让 JavaScript 引擎在进行正则表达式匹配时使用该函数进行判断。由于 "foobar" 中包含 "foo",因此它符合 Foo 的匹配规则,而 "bar" 不包含 "foo",因此不符合匹配规则。
Symbol.replace
Symbol.replace 属性与 Symbol.match 属性相对应,用于将一个字符串中符合某种条件的文本替换成另一个文本。
class Foo { [Symbol.replace](str, replacement) { return str.replace(/foo/g, replacement); } } console.log("foobar".replace(new Foo(), "hello")); // "helloBar"
在上面的代码中,我们创建了一个类 Foo,并定义了 Symbol.replace 属性,通过该属性让 JavaScript 引擎在进行字符串替换时使用该函数进行判断。由于 "foobar" 中包含 "foo",因此它符合 Foo 的替换规则,替换成了 "hello",最终输出 "helloBar"。
Symbol.search
Symbol.search 属性用于确定一个对象是否可以被用作字符串匹配的目标。
class Foo { [Symbol.search](str) { return str.indexOf("foo"); } } console.log("foobar".search(new Foo())); // 0 console.log("bar".search(new Foo())); // -1
在上面的代码中,我们创建了一个类 Foo,并定义了 Symbol.search 属性,通过该属性让 JavaScript 引擎在进行字符串搜索时使用该函数进行判断。由于 "foobar" 中包含 "foo",因此它符合 Foo 的匹配规则,搜索到了 "foo" 的位置 0,而 "bar" 不包含 "foo",因此没有搜索到。
Symbol.species
Symbol.species 属性是一个函数,用于创建导致派生对象的默认构造函数。它在 Array.prototype.map()、Array.prototype.filter()、Array.prototype.slice() 等方法里应用非常广泛。通过 Symbol.species 属性,我们可以指定一个函数,用于在方法调用时创建一个新的派生对象。
-- -------------------- ---- ------- ----- ------- ------- ----- - ------ --- ------------------ - ------ ------ - - ----- ------- - --- ---------- -- --- ----- -------- - ----------------- -- ----- - --- -------------------- ---------- --------- -- ----- -------------------- ---------- ------- -- ----
在上面的代码中,我们创建了一个 MyArray 类,继承了 Array 类,并定义了 Symbol.species 属性。Map 方法在对输出值类型的计算时,将使用这个符号属性来决定返回值的类型。而在MyArray 类中,我们通过 Symbol.species 属性指定了 Array 构造函数,因此 map 的返回值为一个新的 Array 对象。
Symbol 的应用场景
Symbol 的应用非常广泛,可以用于对象属性名、私有属性(使用 Symbol 的结果会使这个变量更像私有变量)、类方法及原型方法的定义等多个方面。下面列举一些常见的应用场景。
防止属性名冲突
在 JavaScript 中,对象的属性名都是字符串。如果多个对象的属性名相同,可能会导致冲突。而使用 Symbol,则可以保证属性名不会重复。
const obj1 = { [Symbol("prop")]: "value" }; const obj2 = { [Symbol("prop")]: "value" }; console.log(obj1 === obj2); // false
自定义迭代器
使用 Symbol.iterator 属性可以自定义迭代器。
-- -------------------- ---- ------- ----- ---------- - - ------------------- - --- - - -- ------ - ------ - -- -- - -- - ------ - ------ ---- ----- ----- -- - ---- - ------ - ------ ---------- ----- ---- -- - - -- - -- --- ------ ----- -- ----------- - ------------------- -
在上面的代码中,我们定义了一个 myIterable 对象,并在该对象上添加 Symbol.iterator 属性,表示该对象可以被迭代。使用 for...of 循环遍历该对象时,会逐个输出该对象的值。
私有属性实现
使用 Symbol 可以实现类似于私有属性的功能,因为使用 Symbol 的结果会使这个变量更像私有变量,无法被直接访问。
-- -------------------- ---- ------- ----- ---- - --------------- ----- ------ - ------------- - ---------- - ------ - --------- - ------ ----------- - - ----- ------ - --- --------- ------------------------------ -- --- ------------------------- -- ---------
在上面的代码中,我们通过 Symbol 键定义了一个私有属性,只能通过类中的方法来访问。
总结
在 ECMAScript 2019 中,Symbol 收到了更广泛的应用,如 Symbol.hasInstance、Symbol.asyncIterator、Symbol.match 等等。通过本文的讲解,了解了 Symbol 的新概念、用法和应用场景,相信对前端开发者来说都是非常有意义的。在实际开发中,合理使用 Symbol,可以帮助我们提高代码的可读性和可维护性。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64fe6bd095b1f8cacdd3225d