专栏名称: niayyy
学生
今天看啥  ›  专栏  ›  niayyy

es6-Symbol 你不知道的知识点

niayyy  · 掘金  ·  · 2019-12-08 04:31

文章预览

阅读 102

es6-Symbol 你不知道的知识点

简介

为了解决 ES5 之前对象属性名冲突, ES6 引入了新的原始类型(primitive) Symbol 他是JavaScript中的第七种数据类型,前六种分别是 null undefined Boolean String Number Object

语法

Symbol([description])
复制代码

description 表示对 Symbol 描述,主要用于区分不同 Symbol 值

用法

Symbol 值通过 Symbol 函数生成,不需要使用 new 命令。这是因为Symbol 是一个原始类型的值,不属于对象,基本上,它是一种类似字符串的数据类型

let a = new Symbol()
// Uncaught TypeError: Symbol is not a constructor
let s = Symbol()

typeof s // "symbol"
复制代码

任意 Symbol 是不相等的

Symbol 函数的参数知识对当前值的描述,因此生成的 Symbol 值是不等的

let a = Symbol()
let b = Symbol()
a === b // false

let c = Symbol('foo')
let d = Symbol('foo')
c === d // false
复制代码

当然也有例外

Symbol.for() 可以做到,它接收字符串作为参数,然后全局搜索有没有以该名称命名的 Symbol 值,如果有,就返回,没有就创建一个全局的 Symbol 值

let a = Symbol.for('foo')
let b = Symbol.for('foo')
a === b // true

let c = Symbol('bar')
let d = Symbol.for('bar')
c === d // false
// 因为 c 是没有登记的值,所以 d 会在全局创建一个 Symbol('bar')
复制代码

Symbol.key() 因为登记的是全局环境,所以跨 iframe 或 service worker 也可以取到同一个值

iframe = document.createElement('iframe');
iframe.src = String(window.location);
document.body.appendChild(iframe);

iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo')
// true
复制代码

Symbol.keyFor()

Symbol.keyFor() 可以返回已登记的Symbol值的 description

let a = Symbol.for('foo')
Symbol.keyFor(a) // "foo"

let b = Symbol('bar')
Symbol.keyFor(b) // undefined
//因为 b 未进行登记
复制代码

Symbol 作为属性名

由于 Symbol 值都是不相等的,所以作为标识符,用于对象,可以防止同名属性覆盖

let a = Symbol()
let obj = {
    a: 'string',
    [a]: 'symbol' // 使用Symbol作为属性名 必须加上[]
}
obj.a // string
obj[a] // symbol
obj['a'] // string
obj[Symbol()] // undefined


let obj1 = {
    [Symbol()]: 'Symbol1'
}
let obj2 = {
    [Symbol()]: 'Symbol2'
}
let target = {}
Object.assign(target, obj1, obj2)
// { Symbol(): 'Symbol1', Symbol(): 'Symbol2' }
复制代码

那在访问的时候如何获取对象中的 Symbol 值呢? Object.getOwnPropertySymbols() 方法可以获取,该方法会返回对象中的所有 Symbol 信息,Object.getOwnPropertyDescriptors() 可以获得对象中 Symbol 值,以及对应的 value 信息

Object.getOwnPropertySymbols(target)
// [Symbol(), symbol()]

Object.getOwnPropertyDescroptors(target)
// [Symbol(): {...}, Symbol(): {...}]
复制代码

内置 Symbol 值

Symbol.hasInstance

Symbol.hasInstance 允许你重写 instanceof 运算符。当使用 instanceof 运算符时,会调用 Symbol.hasInstance 方法

class MyArr {
    static [Symbol.hasInstance](data) {
        if (data instanceof Array) {
            return true
        }
    }
}
let data = []
data instanceof MyArr // true
复制代码

Symbol.isConcatSpreadable

Symbol.isConcatSpreadable 用于 Array.prototype.concat() 用于被连接到数组,设置数组是否展开,默认 Symbol.isConcatSpreadableunderfined

数组是默认展开的

let arr = []
let arr1 = [1, 2]
arr.concat(1, 2, [arr1]) // [1, 2, 1, 2]

arr1[Symbol.isConcatSpreadable] = false
arr.concat(1, 2, [arr1]) // [1, 2, Array(2)]
复制代码

对象默认是不展开的

let arr = []
let obj = { '0': 2, '1': 2, 'length': 2 }
arr.concat(1, 2, obj) // [1, 2, {...}]

obj[Symbol.isConcatSpreadable] = true
arr.concat(1, 2, obj) // [1, 2, 1, 2]
复制代码

Symbol.species

Symbol.species 是一个非常机智的 Symbol,它指向了一个类的构造函数,这允许类能够创建属于自己的、某个方法的新版本

class MyArr extends Array {
    static get [Symbol.species]() {
        return Array
    }
}
let a = new MyArr([1, 2, 3])
let b = a.map(x => x)
b instanceof MyArr // false
b instanceof Array // true
复制代码

ES5 中的 Array.prototype.map 实现可能是

Array.prototype.map = function(func) {
    var newArr = new Array(this.length)
    this.forEach(function (value, index, array) {
        newArr[index] = func(value, index, array)
    })
    return newArr
}
复制代码

ES6 中的 Array.prototype.map 实现可能是

Array.prototype.map = function(func) {
    let Species = this.constructor[Symbol.species]
    let newArr = new Species(this.length)
    this.forEach((value, index, array) => {
        newArr[index] = func(value, index, array)
    })
    return newArr
}
复制代码

通过定义 Symbol.species 在调用 Array.prototype.map 时可以直接返回你想要的数据类型

Symbol.match

Symbol.match 允许你重写 String.prototype.match(myObj) 方法。当调用 String.prototype.match(myObj) 时,如果存在该属性,则会调用。

class MyObj {
    constructor(value) {
        this.value = value
    }
    [Symbol.match](str) {
        if (str.includes(this.value)) {
            return true
        }
        return false
    }
}
let myObj = new MyObj('hello')
'hello world'.match(myObj) // true
'hell'.match(myObj) //false
复制代码

Symbol.replace

Symbol.replace 允许你重写 String.prototype.replace(myObj) 方法。当调用String.prototype.replace(myObj) 时,如果存在该属性,则会调用。

class MyObj {
  constructor(value) {
    this.value = value
  }
  [Symbol.replace](str, replacer) {
    if (replacer === 'hello') {
      return replacer
    }
    else {
      return str
    }
  }
 }
 let myObj = new MyObj('hello')
'hello world'.replace(myObj, 'hello') // hello

'hello world'.replace(myObj, 'hel') // hello world
复制代码

Symbol.search

Symbol.search允许你重写 String.prototype.search(myObj) 方法。当调用 String.prototype.search(myObj) 时,如果存在该属性,则会调用。

Symbol.split

Symbol.split 允许你重写 String.prototype.split(myObj) 方法。当调用String.prototype.split(myObj) 时,如果存在该属性,则会调用。

Symbol.iterator

ES6 中的 for of 循环会调用 Symbol.iterator 方法,可以通过 Symbol.iterator 来重写循环

class Collection {
  *[Symbol.iterator]() {
    let i = 0;
    while(this[i] !== undefined) {
      yield this[i];
      ++i;
    }
  }
}

let myCollection = new Collection();
myCollection[0] = 1;
myCollection[1] = 2;

for(let value of myCollection) {
  console.log(value);
}
// 1
// 2
复制代码

Symbol.toPrimitive

当当前对象被转为原始类型是会调用 Symbol.toPrimitive 方法,返回该对象相应的原始类型值

一共有三种模式

  • Number

  • String

  • Default

let obj = {
  [Symbol.toPrimitive](hint) {
    switch (hint) {
      case 'number':
        return 123;
      case 'string':
        return 'str';
      case 'default':
        return 'default';
      default:
        throw new Error();
     }
   }
};

2 * obj // 246
3 + obj // '3default'
obj == 'default' // true
String(obj) // 'str'
复制代码

Symbol.toStringTag

对象的 Symbol.toStringTag 属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制 [object Array]object 后面的那个字符串

// 例一
({[Symbol.toStringTag]: 'Foo'}.toString())
// "[object Foo]"

// 例二
class Collection {
  get [Symbol.toStringTag]() {
    return 'xxx';
  }
}
let x = new Collection();
Object.prototype.toString.call(x) // "[object xxx]"
复制代码

参考文章

  1. ECMAScript 6 入门

  2. 了不起的 Symbol

………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览