前言:
JavaScript 中的数据类型一直是前端开发者的热门话题。因此,我们在日常开发中常常用到的数据类型,比如 String, Number, Boolean, Object 等等,都是我们耳熟能详的。不过,今天我们要来介绍一个相对新的数据类型:Symbol。
Symbol 是 ES6 中新引入的一种原始数据类型,用于表示唯一的标识,每个 Symbol 对象都是唯一的,这意味着它们可以用作对象属性的标识符,从而避免了属性名冲突的问题,同时也为数据加上了额外的安全性。
随着 ECMAScript 2019 规范的正式发布,Symbol 数据类型也得到了更加完善和稳定的支持。在这篇文章中,我们将深入讨论 Symbol 数据类型的定义、用法、特性、内置 Symbol 常量及其应用。
什么是 Symbol?
Symbol 是一种全新的基本数据类型,类似于 Number 和 String。它是一种特殊的、唯一的、不可变的数据类型,Symbol 数据类型的变量可以作为对象的属性进行设置,从而不会与其他属性重名发生冲突。
我们可以通过下面的代码来创建一个 Symbol 类型的变量:
const id1 = Symbol(); const id2 = Symbol(); console.log(id1 === id2); // false
这里的 id1
和 id2
都是 Symbol 类型的变量,它们彼此是完全不同且唯一的,即使它们没有任何区别,也无法相互比较,两个 Symbol 变量的值不相等。
我们还可以给 Symbol() 传递一个字符串作为参数,以便于在代码中更好地识别符号。这样做也可以创建具有不同标识符的 Symbol 变量,代码如下:
const id3 = Symbol("foo"); const id4 = Symbol("bar"); console.log(id3 === id4); // false
在上面的例子中,id3
和 id4
都有自己的描述字符串,并且它们是唯一的。
Symbol 类型的数据可以作为属性名用于 object object,因为 Symbol 类型的数据是唯一的,所以它们可以作为属性名来保证对象中的属性不会相互干扰。 我们可以使用这样的语法来定义 Symbol 类型的属性:
const syb = Symbol("hello"); const obj = { [syb]: "world", }; console.log(obj[syb]); // "world"
在上面的代码中,我们定义了一个名为 syb 的 Symbol 类型的变量,并将其作为 obj 对象的属性名。所以,当我们通过 obj[syb] 访问时,其值为 "world"。
Symbol 特性
唯一性
我们上面已经提到了,Symbol 类型的变量是唯一的,每个 Symbol 变量都是不同的,这是其最大的特性。
const id1 = Symbol(); const id2 = Symbol(); console.log(id1 === id2); // false
不可遍历性
Symbol 类型的属性是不可枚举的,不能使用 for..in 和 Object.keys() 来获取到,但可以使用 Object.getOwnPropertySymbols 方法来获取对象中的所有 Symbol 类型的属性,代码如下:
const mySymbol = Symbol("mySymbol"); const obj = { name: "bob", [mySymbol]: "hello", }; console.log(Object.getOwnPropertyNames(obj)); // ["name"] console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(mySymbol)]
可作为对象的属性名
因为 Symbol 类型的属性唯一性,可以避免多个属性名冲突的问题,从而为对象属性添加了额外的安全性。
const sym = Symbol("prop"); const obj = { [sym]: "value", }; console.log(obj[sym]); // "value"
作为常量
Symbol 类型的数据可以用作常量值,用于取代代码中的魔法数值:
// javascriptcn.com 代码示例 const MODE_READ = Symbol("mode_read"); const MODE_WRITE = Symbol("mode_write"); function getAccessType(mode) { switch (mode) { case MODE_READ: return "read access"; case MODE_WRITE: return "write access"; default: throw new Error("Unknown mode"); } } const access1 = getAccessType(MODE_READ); const access2 = getAccessType(MODE_WRITE); console.log(access1); // "read access" console.log(access2); // "write access"
内置 Symbol 常量
随着 ECMAScript 2019 规范的正式发布,Symbol 数据类型也得到了更加完善和稳定的支持。ES6 中提供了一些内置的 Symbol 常量,这些常量用于表示某些内部操作。
Symbol.iterator
这个常量表示对一个对象进行迭代时应使用的方法。对于许多 JavaScript 对象,你可以使用 for..of 循环来遍历它们,因为默认情况下它们都具有 Symbol.iterator 方法,用于返回迭代对象的迭代器。
const arr = ["dog", "cat", "bird"]; const itor = arr[Symbol.iterator](); console.log(itor.next()); // {value: "dog", done: false} console.log(itor.next()); // {value: "cat", done: false} console.log(itor.next()); // {value: "bird", done: false} console.log(itor.next()); // {value: undefined, done: true}
我们将数组 arr 的键名值传递给 Symbol.iterator()
方法返回一个迭代器,通过 next()
方法来迭代获取数组元素的值。
Symbol.hasInstance
这个常量表示一个函数是否可以被用来检查指定对象是否是一个构造器的实例。一般情况下,这个属性应该是一个函数。
class Foo {} console.log(Foo[Symbol.hasInstance]({})); // false console.log({} instanceof Foo); // false console.log(Foo[Symbol.hasInstance](new Foo())); // true console.log(new Foo() instanceof Foo); // true
在上面的代码中,我们定义了一个类 Foo,Foo[Symbol.hasInstance]
被用来检查指定的对象是否是 Foo 的实例,如果是则返回 true,否则返回 false。
Symbol.species
这个常量用于访问一个对象的构造函数,为构造函数创建派生对象时使用。
// javascriptcn.com 代码示例 class Bar extends Array { static get [Symbol.species]() { return Array; } } const bar = new Bar(1, 2, 3); const mapped = bar.map((x) => x * x); console.log(mapped instanceof Bar); // false console.log(mapped instanceof Array); // true
在这个例子中,我们定义了一个类 Bar,然后指定了 Symbol.species
常量为 Array,在调用 map() 方法时返回了一个新的数组,而这个数组的构造函数便是 Symbol.species
指定的 Array;最终返回的新数组是 Array 的实例,而不是 Bar 的实例。
Symbol.match,Symbol.replace 和 Symbol.search
这三个常量都是用在 RegExp 这个正则对象上的。它们分别表示匹配、替换和检索操作中可以使用的方法。
const regex = /javascript/; console.log(regex[Symbol.match]("javascript")); // ["javascript"] console.log(regex[Symbol.replace]("javascript", "js")); // "js" console.log(regex[Symbol.search]("javascript")); // 0 console.log("javascript".match(regex)); // ["javascript"] console.log("javascript".replace(regex, "js")); // "js" console.log("javascript".search(regex)); // 0
在上面的代码中,我们使用了 RegExp 对象上的三个 Symbol 常量,分别代表匹配、替换和检索操作。因此,我们可以将这些方法用作对象的属性来进行以上对应的操作。
Symbol.toPrimitive
这个常量是用来指定一个对象在转换为原始值时该采取的行为。它应该是一个函数。
const myObj = { [Symbol.toPrimitive]: function (hint) { return hint === "string" ? "hello world!" : 123; }, }; console.log(myObj + ""); // "hello world!" console.log(+myObj); // 123
在上面的例子中,我们定义了一个具有 Symbol.toPrimitive
常量的对象 myObj,我们在转换为字符串和 Number 类型时分别修改了其返回值;因此在 console 中我们可以看到输出的结果也分别与之对应。
总结
Symbol 类型是 ES6 中新引入的基本数据类型,是一种特殊的、唯一的、不可变的数据类型,它也为数据加上了额外的安全性。在 ECMAScript 2019 规范中,Symbol 数据类型也得到了越来越完善和稳定的支持,并新增了一些内置的 Symbol 常量,用来表示 JS 内部操作的标识。我们在实际开发中可以使用 Symbol 类型来保障对象属性名的唯一性,这样可以避免属性名冲突的问题,提高代码的稳定性和可读性。最后,Symbol 类型的特性和内置常量都需要深入了解和实践,在实际开发中灵活运用才能达到最好的效果。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652b32ce7d4982a6ebd35933