背景
在ES6(ECMAScript 2015)中,Symbol是一种新的原始数据类型,它是JavaScript语言的第七种数据类型(前六种是undefined、null、布尔值Boolean、字符串String、数值Number、对象Object)。
Symbol的主要目的是为了解决对象属性名可能产生的冲突问题,因为它确保每个由Symbol创建的值都是唯一的。
概念
Symbol是JavaScript中的一种基本数据类型,用于创建独一无二的键(key)。每个通过Symbol()函数创建的Symbol值都是唯一的,即使它们的描述符(即创建时传入的字符串参数,用于调试和显示目的)相同,它们也不相等。
基础知识与代码使用
基本使用
1、Symbol 类型提供了一种表示唯一值的方式
let s = Symbol();
console.log(s, typeof s); //Symbol() "symbol"
let s2 = Symbol('尚硅谷');
let s3 = Symbol('尚硅谷');
console.log(s2 === s3); //false 原因:唯一值
2、Symbol.for() 创建Symbol值的方式
let s4 = Symbol.for('尚硅谷');
let s5 = Symbol.for('尚硅谷');
console.log(s4 === s5); // true
区别:Symbol()和Symbol.for()
(1)Symbol():每次调用都会创建一个新的、唯一的Symbol值,不论是否传入相同的描述字符串。
(2)Symbol.for(key):如果给定的键(key)已经存在,则返回与该键相关联的Symbol值;如果不存在,则创建一个新的Symbol值,并将其与键关联起来存储在全局Symbol注册表中。这使得使用相同键的多次调用会返回相同的Symbol值。
3、不能与其他数据进行运算
// let result = s + 100; //报错
// let result = s > 100; //报错
// let result = s + s; //报错
创建对象属性
在JavaScript中,使用Symbol来创建对象的属性是一种非常有用的技术,因为它可以确保属性的唯一性,避免属性名冲突,并且这些属性默认是不可枚举的,有助于隐藏对象的内部状态
1、常规的添加方法
let game = {
name:'俄罗斯方块',
up: function(){},
down: function(){}
};
game.up = function(){
}
game.FN = function(){
}
console.log(game);
2、 用symbol添加方法
let game = {
name:'俄罗斯方块',
up: function(){},
down: function(){}
};
let methods = {
up: Symbol(),
down: Symbol(),
FN:Symbol(),
};
game[methods.up] = function(){
console.log("我可以改变形状");
}
game[methods.down] = function(){
console.log("我可以快速下降!!");
}
game[methods.FN] = function(){
console.log("fn!!");
}
console.log(game);
对比常规添加对象和symbol添加对象,可知。
当你所要创建的方法在对象中已经存在。那么运用常规的方法将不会创建出新的方法(发生冲突),而用symbol会创建出新的方法。
3、symbol的创建使用还有另一种方式 就是直接在对象内创建使用
let youxi = {
name:"狼人杀",
[Symbol('say')]: function(){
console.log("我可以发言")
},
[Symbol('zibao')]: function(){
console.log('我可以自爆');
}
}
console.log(youxi)
内置属性
1、hasInstance:用于判断一个对象是否是某个构造函数的实例
2、isConcatSpreadable:布尔值属性,用于指定数组合并时是否应该展开数组元素
class Person{
static [Symbol.hasInstance](param){
console.log(param);
console.log("我被用来检测类型了");
return false;
}
}
let o = {};
console.log(o instanceof Person);
const arr = [1,2,3];
const arr2 = [4,5,6];
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr.concat(arr2));
| Symbol属性 | 描述 |
|---|---|
Symbol.hasInstance | 定义instanceof运算符的行为,用于判断一个对象是否是某个构造函数的实例 |
Symbol.isConcatSpreadable | 布尔值属性,用于指定数组合并时是否应该展开数组元素 |
Symbol.iterator | 定义对象的默认迭代器,用于for...of循环等迭代操作 |
Symbol.match | 字符串对象的正则表达式匹配方法,允许重写匹配行为 |
Symbol.replace | 字符串对象的正则表达式替换方法,允许重写替换行为 |
Symbol.search | 字符串对象的正则表达式搜索方法,允许重写搜索行为 |
Symbol.split | 字符串对象的正则表达式分割方法,允许重写分割行为 |
Symbol.toPrimitive | 定义对象到原始值的转换行为,如转换为数字、字符串或布尔值 |
Symbol.toStringTag | 定义对象在转换为字符串时的标签,影响Object.prototype.toString的结果 |
Symbol.unscopables | 指定使用with语句时不应该被包含在内的属性,防止变量名冲突 |
应用场景
在JavaScript中,Symbol的使用场景主要集中在需要确保唯一性、避免属性名冲突或实现特定行为时。以下是几种常见的使用Symbol的情况:
-
创建对象的唯一属性名:
- 当对象需要包含多个属性,且这些属性名在项目中可能与其他对象或库中的属性名冲突时,可以使用Symbol作为属性名。这样可以确保每个属性都是唯一的,从而避免冲突。
- 示例:在大型项目或库中,不同的模块或第三方库可能会向同一个对象添加属性。通过使用Symbol作为属性名,可以确保每个模块或库添加的属性都是唯一的,不会被意外覆盖或修改。
-
实现私有属性:
- 虽然JavaScript没有真正的私有属性概念,但可以通过Symbol来模拟私有属性。由于Symbol的唯一性,使用Symbol作为属性名可以防止外部代码直接访问这些属性,从而在一定程度上实现属性的封装和隐藏。
- 示例:在类的构造函数中,可以使用Symbol作为属性名来存储一些内部状态或数据,然后在类的方法中通过Symbol来访问这些属性。
-
全局唯一标识符:
- 当需要在多个模块或文件之间共享一个唯一的标识符时,可以使用
Symbol.for()方法。这个方法会根据给定的字符串键在全局Symbol注册表中查找并返回一个Symbol值。如果给定的键不存在,则会创建一个新的Symbol值并将其注册到全局注册表中。 - 示例:在多个模块之间共享一个全局唯一的Symbol值,用于标识某个特定的功能或行为。
- 当需要在多个模块或文件之间共享一个唯一的标识符时,可以使用
-
自定义对象行为:
- JavaScript定义了一些内置的Symbol值,如
Symbol.iterator、Symbol.hasInstance等,用于实现对象的自定义行为。通过为对象定义这些内置的Symbol属性,可以改变对象的默认行为。 - 示例:通过为对象定义
Symbol.iterator属性,可以使对象支持for...of循环的迭代。通过为类定义Symbol.hasInstance静态方法,可以自定义instanceof操作符的行为。
- JavaScript定义了一些内置的Symbol值,如
-
避免枚举:
- 当不希望对象的某些属性被
for...in循环、Object.keys()或Object.getOwnPropertyNames()等方法枚举时,可以使用Symbol作为这些属性的键。这样,这些属性就不会出现在常规的属性枚举操作中。 - 示例:在对象中定义一些内部使用的属性,这些属性不应该被外部代码访问或修改。通过使用Symbol作为这些属性的键,可以确保它们不会被意外地枚举或访问。
- 当不希望对象的某些属性被
-
元编程:
- 在一些高级编程场景中,如元编程或框架开发中,可能需要通过动态地修改或扩展对象的行为来实现特定的功能。Symbol提供了一种机制来定义和访问这些动态行为,从而支持更复杂的编程模式。