JS红宝书 数据类型第三节 Symbol类型上节

Symbol类型

Symbol(符号)是ECMAScript新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生冲突的危险。

尽管和私有属性有点类似,但符号并不是为了提供私有属性的行为增加的。相反符号是为了创建唯一记号,进而用作非字符串形式的对象属性。

1、符号的基本用法

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

例:

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

调用Symbol()函数时,也可以传入一个字符串作为对符号的描述,可以通过这个字符串来调试代码。但这个字符串参数与符号定义或标识完全无关。

        let Gsymbol=Symbol('foo')
        let Osymbol=Symbol('foo')
        console.log(Gsymbol==Osymbol)   //false

符号没有字面量语法,这也它们发挥作用的关键。按规范,只要创建Symbol()实例并将其用作对象的新属性,就可以确保它不会覆盖已有的对象属性,无论是符号还是字符串属性。

        let Gsymbol=Symbol()
        console.log(Gsymbol)        //Symbol()
        let Fsymbol=Symbol('foo')
        console.log(Fsymbol)        //Symbol(foo)
        console.log(Gsymbol)        //Symbol()  Gsymbol没有被覆盖

最重要的是Symbol()函数不能与new关键字一起作为构造函数使用。这样做是为了避免创建符号包装对象。

例:

    let mySymbol=new Symbol()   //报错:TypeError: Symbol is not a constructor

如果想用符号包装对象,可以借用Object()函数。

例:

        let mySymbol=Symbol()
        let mydefinedSymbol=Object(mySymbol)
        console.log(typeof mydefinedSymbol)         //'object'

2、使用全局符号注册表

如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,在全局符号注册表中创建并重用符号包装对象。

为此需要使用Symbol.for()方法:

        let golbalSymbol=Symbol.for('foo')
        console.log(typeof golbalSymbol)        //symbol

Symbol.for()对每个字符串键都执行幂等操作。第一次使用某个字符串调用时,它会检查全局进行时注册表,发现不存在对应符号,于是会生成一个新的符号实例并添加到注册表中。后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。

        let GolbalSymbol=Symbol.for('foo')      //创建新符号
        let otherSymbol=Symbol.for('foo')       //重用已有符号
        console.log(GolbalSymbol==otherSymbol)  //true

即使采用相同的符号描述,使用Symbol()和使用Symbol.for()定义的符号也并不相同

        let GolbalSymbol=Symbol('foo')     
        let otherSymbol=Symbol.for('foo')  
        console.log(GolbalSymbol==otherSymbol) //false

全局注册表中的符号必须使用字符串键来创建,因此作为参数传给Symbol.for()任何值都会被转换为字符串,此外,注册表中使用的键同时也会被用作符号描述。

        let emptySymbol=Symbol.for()
        console.log(emptySymbol)        //Symbol(undefined)

还可以用Symbol.keyFor()来查询注册表,这个方法接收符号,返回该全局对应的字符串键。如果查询的不是全局符号,则返回undefined。

        // 创建全局符号
        let s=Symbol.for('bar')
        console.log(Symbol.keyFor(s))       //foo
        // 创建普通符号
        let s2=Symbol('bar')
        console.log(Symbol.keyFor(s2))      //undefined

如果Symbol.keyFor()的不是符号,则该方法抛出TypeError:

        Symbol.keyFor(123)  //TypeError: 123 is not a symbol

3、使用符号作为属性

凡是可以使用字符串或数值作为属性的地方,都可以使用符号。这包括了对象字面量属性和Object.defineProperty()//Object.defineProperties()定义的属性。对象字面量只能在计算属性方法中使用符号作为属性。

例:

       let s1=Symbol('foo')
            s2=Symbol('bar')
            s3=Symbol('baz')
            s4=Symbol('qux')

        // 符号定义属性
        let o={
            [s1]:'foo val'
        }
        console.log(o) 
        //输出结果为:{Symbol(foo): 'foo val'}

        // Object.defineProperty定义属性
        Object.defineProperty(o,s2,{value:'bar val'})
        console.log(o)  
        //输出结果为:{Symbol(foo): 'foo val', Symbol(bar): 'bar val'}

        // Object.defineProperties定义属性
        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'}

 类似Object.getOwnProperNames()返回对象实例的常规性数组,Object.getOwnPropertySymbols()返回对象的实例的符号属性数组,这两个方法返回值彼此互斥,Object.getOwnPropertyDescriptors()会返回同时包含常规和符号属性描述符的对象。Reflect.ownKeys()会返回两种类型的键。

例:

        let s1=Symbol('foo')
        let s2=Symbol('bar')

        let o={
            [s1]:'foo val',
            [s2]:'bar val',
            baz:'baz val',
            qux:'qux val'
        }
        //返回对象实例的常规性数组: ['baz', 'qux']
        console.log(Object.getOwnPropertyNames(o))
        //返回对象的实例的符号属性数组:[Symbol(foo), Symbol(bar)]
        console.log(Object.getOwnPropertySymbols(o))
        //返回同时包含常规和符号属性描述符的对象:{baz: {…}, qux: {…}, Symbol(foo): {…},     
        Symbol(bar): {…}}
        console.log(Object.getOwnPropertyDescriptors(o))
        //返回两种类型的键:['baz', 'qux', Symbol(foo), Symbol(bar)]
        console.log(Reflect.ownKeys(o))

4、常用内置符号

ES6也引入了一批常用内置符号,用于暴露语言行为。开发者可以直接访问、重写或模拟这些行为。这些内容符号都以Symbol工厂字符串形式存在。这些内置符号最重要用途之一是重新定义它们,从而改变原生结构行为。比如我们知道for-of循环会在相关对象上使用Symbol.iterator属性,那么就可以通过在自定义对象上重新定义Symbol.iterator的值,来改变for-of在迭代该对象时的行为。

这些内置符号没有什么特别之处,它们就是全局变量Symbol的普通字符串属性,指向一个符号的实例。所以内置符号属性都是不可写、不可枚举、不可配置的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值