为什么JavaScript中会有Symbol类型,具体又是如何应用它的?

2026-03-31 14:151阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2820个文字,预计阅读时间需要12分钟。

为什么JavaScript中会有Symbol类型,具体又是如何应用它的?

什么是Symbol?为什么会有这样的东西?

Symbol(符号)是JavaScript中的一种特殊的数据类型,用于创建唯一且不可变的标识符。它主要用于解决对象属性名冲突的问题。

下面本篇章节将为大家介绍JavaScript中的Symbol类型,讨论其使用方法,希望对大家有所帮助!

Symbol(符号)是什么?

Symbol是JavaScript中的一种基本数据类型,类似于String、Number和Boolean。它用于创建一个唯一的标识符,这个标识符在JavaScript中是唯一的,不会与其他任何属性名冲突。

Symbol的创建方式:

javascriptlet sym=Symbol();

Symbol的属性:

1. Symbol是唯一的:每个Symbol值都是唯一的,即使使用相同的参数创建多个Symbol,它们也不会相等。

2.Symbol是不可变的:一旦创建,Symbol的值不能被改变。

3.Symbol不能被转换为原始值:Symbol不能被转换为String或Number。

Symbol的为什么?

1. 解决属性名冲突:在对象中,如果多个属性名相同,它们会被覆盖。使用Symbol可以创建唯一的属性名,避免属性名冲突。

2.私有属性:Symbol可以用于创建私有属性,使得属性只能在对象内部访问,外部无法访问。

3.避免属性名枚举:使用Symbol作为属性名,可以避免在枚举对象属性时将其包含在内。

Symbol的使用方法:

1. 创建Symbol:

javascript

let sym=Symbol();

2. 使用Symbol作为属性名:

javascriptlet obj={};obj[sym]='value';

3. 获取Symbol的描述:

javascriptlet symDesc=Object.getOwnPropertyDescriptor(obj, sym);console.log(symDesc.value); // 输出:value

4. 判断属性是否为Symbol:

javascriptlet symKey=Symbol();let obj={ [symKey]: 'value' };console.log(Object.prototype.hasOwnProperty.call(obj, symKey)); // 输出:trueconsole.log(Object.prototype.hasOwnProperty.call(obj, 'symKey')); // 输出:false

希望以上内容对大家有所帮助!如有疑问,请随时提问。

什么是 Symbol?为什么会有这么个东西?下面本篇文章给大家介绍一下JavaScript中的Symbol类型,聊聊使用方法,希望对大家有所帮助!

什么是 Symbol?为什么会有这么个东西?

Symbol(符号)是 ES6 新增的数据类型。Symbol 是原始值(基础数据类型),且 Symbol 实例是唯一、不可变的。它的产生是因为要用来唯一的标记,进而用作非字符串形式的对象属性,是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

用法1. 基本用法

符号需要使用 Symbol()函数初始化。因为符号本身是原始类型,所以 typeof 操作符对符号返回 symbol。

let sym = Symbol(); console.log(typeof sym); // symbol

Symbol()函数可以接收一个字符串参数用来描述,后,后续可以通过这个字符串来调试代码。但值得注意的是,多个 Symbol()函数即使接受的参数是一样的,他们的值也是不相等的。

let genericSymbol = Symbol(); let otherGenericSymbol = Symbol(); let fooSymbol = Symbol("foo"); let otherFooSymbol = Symbol("foo"); console.log(genericSymbol == otherGenericSymbol); // false console.log(fooSymbol == otherFooSymbol); // false2. 使用全局符号注册表

如果在代码中有多个地方需要使用同一个 Symbol 实例的时候,可以传入一个字符串,然后使用 Symbol.for()方法来创建一个可以复用的 Symbol,类似于单例模式,在第一次使用 Symbol.for()的时候,它会根据传入的参数会全局的去寻找是否使用 Symbol.for()创建过同样的实例,如果有,则复用,如果没有,则新建

let fooGlobalSymbol = Symbol.for("foo"); // 创建新符号 let otherFooGlobalSymbol = Symbol.for("foo"); // 重用已有符号 console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true

Symbol.for()创建的实例和 Symbol()创建的实例区别: Symbol()创建的实例永远都是唯一的,不会因为你传入的参数相同而跟其他的实例相等,但是 Symbol.for()创建的实例如果参数相同的话他们是会相等的,因为他们会公用同一个 Symbol 实例

let fooSymbol = Symbol("foo"); let otherFooSymbol = Symbol("foo"); console.log(fooSymbol == otherFooSymbol); // false let fooGlobalSymbol = Symbol.for("foo"); // 创建新符号 let otherFooGlobalSymbol = Symbol.for("foo"); // 重用已有符号 console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true3. 使用符号作为属性

对象中的属性一般都是字符串的形式,但其实也是可以使用 Symbol 实例来作为属性的,这样的好处就是你新增的属性不会覆盖掉以前的任何属性

let s1 = Symbol("foo"), s2 = Symbol("bar"), s3 = Symbol("baz"), s4 = Symbol("qux"); let o = { [s1]: "foo val", }; // 这样也可以:o[s1] = 'foo val'; console.log(o); // {Symbol(foo): foo val} Object.defineProperty(o, s2, { value: "bar val" }); console.log(o); // {Symbol(foo): foo val, Symbol(bar): bar val} Object.defineProperties(o, { [s3]: { value: "baz val" }, [s4]: { value: "qux val" }, }); console.log(o); // {Symbol(foo): foo val, Symbol(bar): bar val, // Symbol(baz): baz val, Symbol(qux): qux val}

注意: 创建 Symbol 实例作为对象属性的时候,如果改 symbol 一开始没有声明一个变量进行接收的话,后续就必须遍历对象的所有符号属性才能找到相应的属性键:

为什么JavaScript中会有Symbol类型,具体又是如何应用它的?

let o = { [Symbol("foo")]: "foo val", [Symbol("bar")]: "bar val", }; console.log(o); // {Symbol(foo): "foo val", Symbol(bar): "bar val"} let barSymbol = Object.getOwnPropertySymbols(o).find(symbol => symbol.toString().match(/bar/)); console.log(barSymbol); // Symbol(bar)4. 常用内置符号

ES6 也引入了一批常用内置符号(well-known symbol),用于暴露语言内部行为,开发者可以直接访问、重写或模拟这些行为。如果对这些默认的属性进行了修改的话,是可以改变一些操作最后执行的结果的。比如 for-of 循环会在相关对象上使用 Symbol.iterator 属性,那么就可以通过在自定义对象上重新定义 Symbol.iterator 的值,来改变 for-of 在迭代该对象时的行为。

5. Symbol.asyncIterator

其实就是一个返回 Promise 的 Generator,一般配合 for await of 使用

6. Symbol.hasInstance

根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的 AsyncIterator。 由 for-await-of 语句使用”。换句话说,这个符号表示实现异步迭代器 API 的函数。

这个属性定义在 Function 的原型上。都知道 instanceof 操作符可以用来确定一个对象实例是否属于某个构造函数。其原理就是 instanceof 操作符会使用 Symbol.hasInstance 函数来确定关系

function Foo() {} let f = new Foo(); console.log(f instanceof Foo); // true class Bar {} let b = new Bar(); console.log(b instanceof Bar); // true

如果你重新定义一个函数的 Symbol.hasInstance 属性,你就可以让 instanceof 方法返回一些意料之外的东西

class Bar {} class Baz extends Bar { static [Symbol.hasInstance]() { return false; } } let b = new Baz(); console.log(Bar[Symbol.hasInstance](b)); // true console.log(b instanceof Bar); // true console.log(Baz[Symbol.hasInstance](b)); // false console.log(b instanceof Baz); // falseSymbol.isConcatSpreadabl

这个属性定义在 Array 的原型上

根据 ECMAScript 规范,这个符号作为一个属性表示“一个布尔值,如果是 true,则意味着对象应该用 Array.prototype.concat()打平其数组元素”。ES6 中的 Array.prototype.concat()方法会 根据接收到的对象类型选择如何将一个类数组(伪数组)对象拼接成数组实例。所以修改 Symbol.isConcatSpreadable 的值可以修改这个行为。

Symbol.isConcatSpreadable 对应的效果

false: 将一整个对象添加进数组true: 将一整个对打平添加进数组

let initial = ["foo"]; let array = ["bar"]; console.log(array[Symbol.isConcatSpreadable]); // undefined console.log(initial.concat(array)); // ['foo', 'bar'] array[Symbol.isConcatSpreadable] = false; console.log(initial.concat(array)); // ['foo', Array(1)] let arrayLikeObject = { length: 1, 0: "baz" }; console.log(arrayLikeObject[Symbol.isConcatSpreadable]); // undefined console.log(initial.concat(arrayLikeObject)); // ['foo', {...}] arrayLikeObject[Symbol.isConcatSpreadable] = true; console.log(initial.concat(arrayLikeObject)); // ['foo', 'baz'] let otherObject = new Set().add("qux"); console.log(otherObject[Symbol.isConcatSpreadable]); // undefined console.log(initial.concat(otherObject)); // ['foo', Set(1)] otherObject[Symbol.isConcatSpreadable] = true; console.log(initial.concat(otherObject)); // ['foo']8. Symbol.iterator

根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的迭代器。由 for-of 语句使用”

该属性会返回一个 Generator 函数,for of 就会依次的去调用 next()方法,这就是为什么 for of 可以使用在某些对象身上。

class Emitter { constructor(max) { this.max = max; this.idx = 0; } *[Symbol.iterator]() { while (this.idx < this.max) { yield this.idx++; } } } function count() { let emitter = new Emitter(5); for (const x of emitter) { console.log(x); } } count(); // 0 // 1 // 2 // 3 // 49. Symbol.match

根据 ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法用正则表达式去匹配字符串。由 String.prototype.match()方法使用”。

String.prototype.match()方法会使用以 Symbol.match 为键的函数来对正则表达式求值。所以更改一个正则表达式的 Symbol.match 属性,可以让 String.prototype.match()得到你想要的值

console.log(RegExp.prototype[Symbol.match]); // ƒ [Symbol.match]() { [native code] } console.log("foobar".match(/bar/)); // ["bar", index: 3, input: "foobar", groups: undefined] class FooMatcher { static [Symbol.match](target) { return target.includes("foo"); } } console.log("foobar".match(FooMatcher)); // true console.log("barbaz".match(FooMatcher)); // false class StringMatcher { constructor(str) { this.str = str; } [Symbol.match](target) { return target.includes(this.str); } } console.log("foobar".match(new StringMatcher("foo"))); // true console.log("barbaz".match(new StringMatcher("qux"))); // false11. Symbol.search

这个符号作为一个属性表示“一个正则表达式方法,该方法返回字符串中 匹配正则表达式的索引。由 String.prototype.search()方法使用”

12. Symbol.species

这个符号作为一个属性表示“一个函数值,该函数作为创建派生对象的构 造函数”。

13. Symbol.split

这个符号作为一个属性表示“一个正则表达式方法,该方法在匹配正则表 达式的索引位置拆分字符串。由 String.prototype.split()方法使用”。

14. Symbol.toPrimitive

这个符号作为一个属性表示“一个方法,该方法将对象转换为相应的原始 值。由 ToPrimitive 抽象操作使用”

15. Symbol.toStringTag

这个符号作为一个属性表示“一个字符串,该字符串用于创建对象的默认 字符串描述。由内置方法 Object.prototype.toString()使用”

16. Symbol.unscopables

这个符号作为一个属性表示“一个对象,该对象所有的以及继承的属性, 都会从关联对象的 with 环境绑定中排除

更多编程相关知识,请访问:编程视频!!

本文共计2820个文字,预计阅读时间需要12分钟。

为什么JavaScript中会有Symbol类型,具体又是如何应用它的?

什么是Symbol?为什么会有这样的东西?

Symbol(符号)是JavaScript中的一种特殊的数据类型,用于创建唯一且不可变的标识符。它主要用于解决对象属性名冲突的问题。

下面本篇章节将为大家介绍JavaScript中的Symbol类型,讨论其使用方法,希望对大家有所帮助!

Symbol(符号)是什么?

Symbol是JavaScript中的一种基本数据类型,类似于String、Number和Boolean。它用于创建一个唯一的标识符,这个标识符在JavaScript中是唯一的,不会与其他任何属性名冲突。

Symbol的创建方式:

javascriptlet sym=Symbol();

Symbol的属性:

1. Symbol是唯一的:每个Symbol值都是唯一的,即使使用相同的参数创建多个Symbol,它们也不会相等。

2.Symbol是不可变的:一旦创建,Symbol的值不能被改变。

3.Symbol不能被转换为原始值:Symbol不能被转换为String或Number。

Symbol的为什么?

1. 解决属性名冲突:在对象中,如果多个属性名相同,它们会被覆盖。使用Symbol可以创建唯一的属性名,避免属性名冲突。

2.私有属性:Symbol可以用于创建私有属性,使得属性只能在对象内部访问,外部无法访问。

3.避免属性名枚举:使用Symbol作为属性名,可以避免在枚举对象属性时将其包含在内。

Symbol的使用方法:

1. 创建Symbol:

javascript

let sym=Symbol();

2. 使用Symbol作为属性名:

javascriptlet obj={};obj[sym]='value';

3. 获取Symbol的描述:

javascriptlet symDesc=Object.getOwnPropertyDescriptor(obj, sym);console.log(symDesc.value); // 输出:value

4. 判断属性是否为Symbol:

javascriptlet symKey=Symbol();let obj={ [symKey]: 'value' };console.log(Object.prototype.hasOwnProperty.call(obj, symKey)); // 输出:trueconsole.log(Object.prototype.hasOwnProperty.call(obj, 'symKey')); // 输出:false

希望以上内容对大家有所帮助!如有疑问,请随时提问。

什么是 Symbol?为什么会有这么个东西?下面本篇文章给大家介绍一下JavaScript中的Symbol类型,聊聊使用方法,希望对大家有所帮助!

什么是 Symbol?为什么会有这么个东西?

Symbol(符号)是 ES6 新增的数据类型。Symbol 是原始值(基础数据类型),且 Symbol 实例是唯一、不可变的。它的产生是因为要用来唯一的标记,进而用作非字符串形式的对象属性,是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

用法1. 基本用法

符号需要使用 Symbol()函数初始化。因为符号本身是原始类型,所以 typeof 操作符对符号返回 symbol。

let sym = Symbol(); console.log(typeof sym); // symbol

Symbol()函数可以接收一个字符串参数用来描述,后,后续可以通过这个字符串来调试代码。但值得注意的是,多个 Symbol()函数即使接受的参数是一样的,他们的值也是不相等的。

let genericSymbol = Symbol(); let otherGenericSymbol = Symbol(); let fooSymbol = Symbol("foo"); let otherFooSymbol = Symbol("foo"); console.log(genericSymbol == otherGenericSymbol); // false console.log(fooSymbol == otherFooSymbol); // false2. 使用全局符号注册表

如果在代码中有多个地方需要使用同一个 Symbol 实例的时候,可以传入一个字符串,然后使用 Symbol.for()方法来创建一个可以复用的 Symbol,类似于单例模式,在第一次使用 Symbol.for()的时候,它会根据传入的参数会全局的去寻找是否使用 Symbol.for()创建过同样的实例,如果有,则复用,如果没有,则新建

let fooGlobalSymbol = Symbol.for("foo"); // 创建新符号 let otherFooGlobalSymbol = Symbol.for("foo"); // 重用已有符号 console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true

Symbol.for()创建的实例和 Symbol()创建的实例区别: Symbol()创建的实例永远都是唯一的,不会因为你传入的参数相同而跟其他的实例相等,但是 Symbol.for()创建的实例如果参数相同的话他们是会相等的,因为他们会公用同一个 Symbol 实例

let fooSymbol = Symbol("foo"); let otherFooSymbol = Symbol("foo"); console.log(fooSymbol == otherFooSymbol); // false let fooGlobalSymbol = Symbol.for("foo"); // 创建新符号 let otherFooGlobalSymbol = Symbol.for("foo"); // 重用已有符号 console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true3. 使用符号作为属性

对象中的属性一般都是字符串的形式,但其实也是可以使用 Symbol 实例来作为属性的,这样的好处就是你新增的属性不会覆盖掉以前的任何属性

let s1 = Symbol("foo"), s2 = Symbol("bar"), s3 = Symbol("baz"), s4 = Symbol("qux"); let o = { [s1]: "foo val", }; // 这样也可以:o[s1] = 'foo val'; console.log(o); // {Symbol(foo): foo val} Object.defineProperty(o, s2, { value: "bar val" }); console.log(o); // {Symbol(foo): foo val, Symbol(bar): bar val} Object.defineProperties(o, { [s3]: { value: "baz val" }, [s4]: { value: "qux val" }, }); console.log(o); // {Symbol(foo): foo val, Symbol(bar): bar val, // Symbol(baz): baz val, Symbol(qux): qux val}

注意: 创建 Symbol 实例作为对象属性的时候,如果改 symbol 一开始没有声明一个变量进行接收的话,后续就必须遍历对象的所有符号属性才能找到相应的属性键:

为什么JavaScript中会有Symbol类型,具体又是如何应用它的?

let o = { [Symbol("foo")]: "foo val", [Symbol("bar")]: "bar val", }; console.log(o); // {Symbol(foo): "foo val", Symbol(bar): "bar val"} let barSymbol = Object.getOwnPropertySymbols(o).find(symbol => symbol.toString().match(/bar/)); console.log(barSymbol); // Symbol(bar)4. 常用内置符号

ES6 也引入了一批常用内置符号(well-known symbol),用于暴露语言内部行为,开发者可以直接访问、重写或模拟这些行为。如果对这些默认的属性进行了修改的话,是可以改变一些操作最后执行的结果的。比如 for-of 循环会在相关对象上使用 Symbol.iterator 属性,那么就可以通过在自定义对象上重新定义 Symbol.iterator 的值,来改变 for-of 在迭代该对象时的行为。

5. Symbol.asyncIterator

其实就是一个返回 Promise 的 Generator,一般配合 for await of 使用

6. Symbol.hasInstance

根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的 AsyncIterator。 由 for-await-of 语句使用”。换句话说,这个符号表示实现异步迭代器 API 的函数。

这个属性定义在 Function 的原型上。都知道 instanceof 操作符可以用来确定一个对象实例是否属于某个构造函数。其原理就是 instanceof 操作符会使用 Symbol.hasInstance 函数来确定关系

function Foo() {} let f = new Foo(); console.log(f instanceof Foo); // true class Bar {} let b = new Bar(); console.log(b instanceof Bar); // true

如果你重新定义一个函数的 Symbol.hasInstance 属性,你就可以让 instanceof 方法返回一些意料之外的东西

class Bar {} class Baz extends Bar { static [Symbol.hasInstance]() { return false; } } let b = new Baz(); console.log(Bar[Symbol.hasInstance](b)); // true console.log(b instanceof Bar); // true console.log(Baz[Symbol.hasInstance](b)); // false console.log(b instanceof Baz); // falseSymbol.isConcatSpreadabl

这个属性定义在 Array 的原型上

根据 ECMAScript 规范,这个符号作为一个属性表示“一个布尔值,如果是 true,则意味着对象应该用 Array.prototype.concat()打平其数组元素”。ES6 中的 Array.prototype.concat()方法会 根据接收到的对象类型选择如何将一个类数组(伪数组)对象拼接成数组实例。所以修改 Symbol.isConcatSpreadable 的值可以修改这个行为。

Symbol.isConcatSpreadable 对应的效果

false: 将一整个对象添加进数组true: 将一整个对打平添加进数组

let initial = ["foo"]; let array = ["bar"]; console.log(array[Symbol.isConcatSpreadable]); // undefined console.log(initial.concat(array)); // ['foo', 'bar'] array[Symbol.isConcatSpreadable] = false; console.log(initial.concat(array)); // ['foo', Array(1)] let arrayLikeObject = { length: 1, 0: "baz" }; console.log(arrayLikeObject[Symbol.isConcatSpreadable]); // undefined console.log(initial.concat(arrayLikeObject)); // ['foo', {...}] arrayLikeObject[Symbol.isConcatSpreadable] = true; console.log(initial.concat(arrayLikeObject)); // ['foo', 'baz'] let otherObject = new Set().add("qux"); console.log(otherObject[Symbol.isConcatSpreadable]); // undefined console.log(initial.concat(otherObject)); // ['foo', Set(1)] otherObject[Symbol.isConcatSpreadable] = true; console.log(initial.concat(otherObject)); // ['foo']8. Symbol.iterator

根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的迭代器。由 for-of 语句使用”

该属性会返回一个 Generator 函数,for of 就会依次的去调用 next()方法,这就是为什么 for of 可以使用在某些对象身上。

class Emitter { constructor(max) { this.max = max; this.idx = 0; } *[Symbol.iterator]() { while (this.idx < this.max) { yield this.idx++; } } } function count() { let emitter = new Emitter(5); for (const x of emitter) { console.log(x); } } count(); // 0 // 1 // 2 // 3 // 49. Symbol.match

根据 ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法用正则表达式去匹配字符串。由 String.prototype.match()方法使用”。

String.prototype.match()方法会使用以 Symbol.match 为键的函数来对正则表达式求值。所以更改一个正则表达式的 Symbol.match 属性,可以让 String.prototype.match()得到你想要的值

console.log(RegExp.prototype[Symbol.match]); // ƒ [Symbol.match]() { [native code] } console.log("foobar".match(/bar/)); // ["bar", index: 3, input: "foobar", groups: undefined] class FooMatcher { static [Symbol.match](target) { return target.includes("foo"); } } console.log("foobar".match(FooMatcher)); // true console.log("barbaz".match(FooMatcher)); // false class StringMatcher { constructor(str) { this.str = str; } [Symbol.match](target) { return target.includes(this.str); } } console.log("foobar".match(new StringMatcher("foo"))); // true console.log("barbaz".match(new StringMatcher("qux"))); // false11. Symbol.search

这个符号作为一个属性表示“一个正则表达式方法,该方法返回字符串中 匹配正则表达式的索引。由 String.prototype.search()方法使用”

12. Symbol.species

这个符号作为一个属性表示“一个函数值,该函数作为创建派生对象的构 造函数”。

13. Symbol.split

这个符号作为一个属性表示“一个正则表达式方法,该方法在匹配正则表 达式的索引位置拆分字符串。由 String.prototype.split()方法使用”。

14. Symbol.toPrimitive

这个符号作为一个属性表示“一个方法,该方法将对象转换为相应的原始 值。由 ToPrimitive 抽象操作使用”

15. Symbol.toStringTag

这个符号作为一个属性表示“一个字符串,该字符串用于创建对象的默认 字符串描述。由内置方法 Object.prototype.toString()使用”

16. Symbol.unscopables

这个符号作为一个属性表示“一个对象,该对象所有的以及继承的属性, 都会从关联对象的 with 环境绑定中排除

更多编程相关知识,请访问:编程视频!!