【ES6】Symbol

242 阅读6分钟

背景

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);

9d1d70ac70d033f59a78da9c0a2dfeb.png

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);

cc99ddf85c55339f6e7a43dba27b691.png

对比常规添加对象和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的情况:

  1. 创建对象的唯一属性名

    • 当对象需要包含多个属性,且这些属性名在项目中可能与其他对象或库中的属性名冲突时,可以使用Symbol作为属性名。这样可以确保每个属性都是唯一的,从而避免冲突。
    • 示例:在大型项目或库中,不同的模块或第三方库可能会向同一个对象添加属性。通过使用Symbol作为属性名,可以确保每个模块或库添加的属性都是唯一的,不会被意外覆盖或修改。
  2. 实现私有属性

    • 虽然JavaScript没有真正的私有属性概念,但可以通过Symbol来模拟私有属性。由于Symbol的唯一性,使用Symbol作为属性名可以防止外部代码直接访问这些属性,从而在一定程度上实现属性的封装和隐藏。
    • 示例:在类的构造函数中,可以使用Symbol作为属性名来存储一些内部状态或数据,然后在类的方法中通过Symbol来访问这些属性。
  3. 全局唯一标识符

    • 当需要在多个模块或文件之间共享一个唯一的标识符时,可以使用Symbol.for()方法。这个方法会根据给定的字符串键在全局Symbol注册表中查找并返回一个Symbol值。如果给定的键不存在,则会创建一个新的Symbol值并将其注册到全局注册表中。
    • 示例:在多个模块之间共享一个全局唯一的Symbol值,用于标识某个特定的功能或行为。
  4. 自定义对象行为

    • JavaScript定义了一些内置的Symbol值,如Symbol.iteratorSymbol.hasInstance等,用于实现对象的自定义行为。通过为对象定义这些内置的Symbol属性,可以改变对象的默认行为。
    • 示例:通过为对象定义Symbol.iterator属性,可以使对象支持for...of循环的迭代。通过为类定义Symbol.hasInstance静态方法,可以自定义instanceof操作符的行为。
  5. 避免枚举

    • 当不希望对象的某些属性被for...in循环、Object.keys()Object.getOwnPropertyNames()等方法枚举时,可以使用Symbol作为这些属性的键。这样,这些属性就不会出现在常规的属性枚举操作中。
    • 示例:在对象中定义一些内部使用的属性,这些属性不应该被外部代码访问或修改。通过使用Symbol作为这些属性的键,可以确保它们不会被意外地枚举或访问。
  6. 元编程

    • 在一些高级编程场景中,如元编程或框架开发中,可能需要通过动态地修改或扩展对象的行为来实现特定的功能。Symbol提供了一种机制来定义和访问这些动态行为,从而支持更复杂的编程模式。