Symbol

作者: haha2333 | 来源:发表于2019-07-27 10:46 被阅读0次

    对于一项新知识,要知道它有什么用,解决了什么问题,那么才有研究它的动力。所以学习Symbol从它解决了什么问题出发。

    “ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。”

    一开始这段话我好像看懂了,但是实际上那要怎么用???迷惑
    那就把这段话翻译成代码吧

    举个例子,你看上了公司前来的前台妹纸,想了解关于她的更多信息,于是就询问Hr同事,扫地阿姨,于是得到类似这样信息:

    let info1 = {
        name: '婷婷',
        age: 24,
        job: '公司前台',
        description: '平时喜欢做做瑜伽,人家有男朋友,你别指望了'
    }
    let info2 = {
        description: '这小姑娘挺好的,挺热情的,嘿嘿嘿……'
    }
    

    当我们汇总这位前台小姐的信息时,就发现,``` description:'这小姑娘挺好的,挺热情的,嘿嘿嘿……'``导致别人有男朋友这个重要的信息丢失了。
    但是我们用了symbol之后

    let info1 = {
        name: '婷婷',
        age: 24,
        job: '公司前台',
        [Symbol('description')]: '平时喜欢做做瑜伽,人家有男朋友,你别指望了'
    }
    let info2 = {
        [Symbol('description')]: '这小姑娘挺好的,挺热情的,嘿嘿嘿……'
    }
    let target ={}
    Object.assign(target,info1,info2)
    console.log(target)
    
    //age: 24
    //job: "公司前台"
    //name: "婷婷"
    //Symbol(description): "平时喜欢做做瑜伽,人家有男朋友,你别指望了"
    //Symbol(description): "这小姑娘挺好的,挺热情的,嘿嘿嘿……"
    

    他的信息就被完整保留下来了。

    啊,谢天谢地。终于弄懂Symbol是怎么解决命名冲突的了。

    下面开始深入学习Symbol的用法

    • Symbol是js基本数据类型,Symbol 值通过Symbol函数生成。不能用new运算符创建
    var a = Symbol() 
    

    括号里面可以接收一个字符串参数,作为该实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

    var a = Symbol('foo') 
    a // Symbol(foo)
    a.toString() // "Symbol(foo)"
    
    • Symbol()返回值是唯一的,也就是,即使描述相同,他们也是独立的两个值。
    Symbol('description') === Symbol('description');    // 返回值是false
    

    这跟引用类型一样

    var a = new Object()
    var b = new Object()
    a===b // false
    

    如果是普通的基本类型,比如

    var a = 'string'
    var b = 'string'
    a===b //true
    
    • Symbol值不可以和其他类型的值进行运算,可以显示转换为字符串或者布尔值
    let sym = Symbol('My symbol');
    String(sym) // 'Symbol(My symbol)'
    sym.toString() // 'Symbol(My symbol)'
    
    let sym = Symbol();
    Boolean(sym) // true
    !sym  // false
    if (sym) {
      // ...
    }
    Number(sym) // TypeError
    sym + 2 // TypeError
    

    一些属性和方法

    • 读取Symbol的描述description
    const sym = Symbol('foo');
    sym.description // "foo"
    
    • 作为常量的Symbol
      常量使用 Symbol 值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的switch语句会按设计的方式工作。
      emmm到底是怎样的特别适合呢?
      比如
    let a='a'+'b'
    let b='ab'
    a===b  //true
    

    这样case 'ab'中,变量a和b都是符合条件的,但是我们只想要b这种形式的。这个时候把b变成symbol就很有必要啦。
    大概是这样吧

    const COLOR_RED    = Symbol();
    const COLOR_GREEN  = Symbol();
    function getComplement(color) {
      switch (color) {
        case COLOR_RED:
          return COLOR_GREEN;
        case COLOR_GREEN:
          return COLOR_RED;
        default:
          throw new Error('Undefined color');
        }
    }
    
    • 作为属性名的Symbol
      这也是一开头提到的那个例子
      有4种写法
    let mySymbol = Symbol();
    
    // 第一种写法
    let a = {};
    a[mySymbol] = 'Hello!';
    
    // 第二种写法
    let a = {
      [mySymbol]: 'Hello!'
    };
    
    // 第三种写法
    let a = {};
    Object.defineProperty(a, mySymbol, { value: 'Hello!' });
    
    // 以上写法都得到同样结果
    a[mySymbol] // "Hello!"
    

    以上三种,都保留了Symbol的引用值,就是一开头的mySymbol。这些引用值还是需要不一样,才能正确引用对应的Symbol值。这样写引用方式也比较简单。
    但是,这样跟我写

    var obj = {
        a:"a",
        b:"b"
    }
    

    有什么区别,还不是要起不一样的引用值的名字!
    所以就有了最开头例子的那种写法,

    let info2 = {
        [Symbol('description')]: '这小姑娘挺好的,挺热情的,嘿嘿嘿……'
    }
    

    不保留他的引用值,通过Object.getOwnPropertySymbols(对象名)
    这个方法返回的是该对象中Symbol值的数组

    let array = Object.getOwnPropertySymbols(target);
    console.log(array); 
    console.log(target[array[0]]);  
    
    //Array [ Symbol(description), Symbol(description) ]
    //'平时喜欢做做瑜伽,人家有男朋友,你别指望了'
    

    在此插播一个小知识点
    es6之前变量属性名称都是字符串型的
    访问对象普通属性时,可以使用点运算符obj.a或者obj['a']进行访问。访问Symbol类型的对象属性时,用obj[a]

    Symbol 值作为属性名时,该属性还是公开属性,不是私有属性。

    Symbol 作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。
    就是上面的例子中使用过的
    Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

    let obj = {
      [Symbol('my_key')]: 1,
      enum: 2,
      nonEnum: 3
    };
    
    Reflect.ownKeys(obj)
    //  ["enum", "nonEnum", Symbol(my_key)]
    

    由于以 Symbol 值作为名称的属性,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。

    let size = Symbol('size');
    
    class Collection {
      constructor() {
        this[size] = 0;
      }
    
      add(item) {
        this[this[size]] = item;
        this[size]++;
      }
    
      static sizeOf(instance) {
        return instance[size];
      }
    }
    
    let x = new Collection();
    Collection.sizeOf(x) // 0
    
    x.add('foo');
    Collection.sizeOf(x) // 1
    
    Object.keys(x) 
    Object.getOwnPropertyNames(x) 
    Object.getOwnPropertySymbols(x)  
    //  [Symbol(size)]
    

    看到这里,要去补一下js的class了。。。

    • Symbol的复用
      因为每次使用Symbol()方法,即使描述符相同,也不是同一个symbol了,想要复用symbol,即重新使用同一个 Symbol 值,Symbol.for方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
    let s1 = Symbol.for('foo');
    let s2 = Symbol.for('foo');
    s1 === s2 // true
    

    感觉面试会问
    Symbol()Symbol.for()的区别:
    虽然Symbol()Symbol.for()都可以创建一个新的Symbol值,但是前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。

    Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key。(就是使用过Symbol.for())

    let s1 = Symbol.for("foo");
    Symbol.keyFor(s1) // "foo"
    
    let s2 = Symbol("foo");
    Symbol.keyFor(s2) // undefined
    

    小总结:
    1.Symbol数据类型的引入,解决了变量名冲突的问题
    2.Reflect.ownKeys(obj)可以返回对象所有属性,包括Symbol值
    3.Object.getOwnPropertySymbols(obj)可以获得该对象上的Symbol值,返回一个数组
    4.通过Object.for('key')在全局环境中查找key值,找到则返回那个值,找不到就在全局环境中注册并返回该值
    5.在全局环境中注册过的Symbol值,可以使用Object.keyfor('变量名')返回该值都key值

    先那么多吧。。。

    相关文章

      网友评论

          本文标题:Symbol

          本文链接:https://www.haomeiwen.com/subject/tgscrctx.html