zoukankan      html  css  js  c++  java
  • ES6类型扩展Symbol类型

    ES5中包含5种原始类型:字符串、数字、布尔值、null和undefined。ES6引入了第6种原始类型 —— Symbol类型

    为什么要引入Symbol?ES5中对象的属性名都是字符串,这很容易造成命名冲突,最常见的就是扩展第三库的时候,要添加的新方法很可能和已有方法产生冲突。Symbol可以保证每个属性的名字都是独一无二的,从根本上防止属性名冲突。

    创建

    Symbol值可以通过Symbol()函数创建。现在对象的属性名不仅可以是字符串,也可以是Symbol类型。凡是属性名是Symbol类型的,都是独一无二的。

    let name = Symbol(), person = {}
    person[name] = 'wmui'
    console.log(person[name]) // wmui
    

    注意: Symbol函数前不能使用new命令,否则会报错。因为生成的 Symbol 是一个原始类型的值,不是对象

    Symbol函数接收一个可选参数,参数是一段文本,用来描述要创建的Symbol。描述仅用于方便代码的阅读和调试。

    let name = Symbol('name'), person = {}
    person[name] = 'wmui'
    console.log(person[name]) // wmui
    console.log(name) // Symbol(name)
    

    Symbol描述被存储在内部的[[Description]]属性中,只有调用Symbol的toString()方法时才可以读取到这个属性。console.log(name) 会隐式调用name的toString()方法,但是不能直接在代码中访问[[Description]]

    Symbol是原始值,ES6扩展了typeof操作符,所以可以用typeof来检测变量是否为symbol类型,如果是则返回"symbol"。

    let name = Symbol('name')
    console.log(typeof name) // symbol
    

    使用

    所有使用可计算属性名的地方,都可以使用Symbol。比如对象的属性名。

    let name = Symbol('name')
    let person = {
      [name]: 'wmui'
    }
    
    // 设置年龄
    let age = Symbol('age')
    Object.defineProperties(person, {
      [age]: {
        value: 10,
        writable: false
      }
    })
    
    console.log(person[name]) // wmui
    console.log(person[age]) // 10
    

    注意: Symbol值作为对象属性名时,不能用点运算符设置

    let name = Symbol('name')
    let person = {}
    person.name = 'wmui'
    console.log(person[name]) // undefined
    

    共享体系

    在大型应用程序中,很多时候需要跨文件共享同一个Symbol,ES6提供了一个可以随时访问的全局Symbol注册表。

    Symbol.for()

    Symbol.for()可以创建共享的Symbol,它接收一个参数,表示要创建的Symbol字符串标识符。这个参数同时也被用于Symbol的描述

    let name = Symbol.for('name')
    let name2 = Symbol.for('name')
    let person = {}
    person[name] = 'wmui'
    
    console.log(name === name2) // true
    console.log(person[name2]) // wmui
    console.log(name2) // Symbol(name)
    

    Symbol.for()方法首先在全局Symbol注册表中搜索键为name的Symbol是否存在。如果存在,直接返回已有的Symbol,否则,创建一个新的Symbol,并使用这个键在Symbol全局注册表中注册,随即返回新创建的Symbol

    这个示例中,name和name2包含相同的Symbol,第一次调用Symbol.for()方法会创建这个Symbol,第二次调用可以直接从Symbol的全局注册表中检索到这个Symbol,所以他们是相等的

    Symbol.keyFor()

    Symbol.keyFor()方法可以在Symbol全局注册表中检索与Symbol有关的键

    let name = Symbol.for('name')
    let name2 = Symbol.for('name')
    let name3 = Symbol('name')
    
    console.log(Symbol.keyFor(name)) // name
    console.log(Symbol.keyFor(name2)) // name
    console.log(Symbol.keyFor(name3)) // undefined
    

    name和name2都返回了name这个键,而在Symbol全局注册表中不存在name3这个Symbol,所以最终返回undefined

    注意: Symbol.for()为Symbol值登记的名字,是全局环境的,可以在不同的 iframe 或 service worker 中取到同一个值

    类型转换

    类型转换是JS中的一个重要语言特性,然而其他类型没有与Symbol逻辑等价的值

    // 示例1
    let name = Symbol('name') + ''
    console.log(name) // 报错
    
    // 示例2
    let name = Symbol('name') / 1
    console.log(name) // 报错
    
    // 示例3
    let name = Symbol('name')
    console.log(!!name) // true
    

    无论是尝试将Symbol和字符串拼接,还是和数字进行混合运算,均不能强制转换Symbol的类型,所以会报错。

    注意: 布尔值除外,因为Symbol与JS中的非空值类似,其等价布尔值为true

    属性检索

    Symbol作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.getOwnPropertyNames()、Object.keys()、JSON.stringify()返回。于是,在ES6中添加了一个Object.getOwnpropertySymbols()方法来检索对象中的Symbol属性

    Object.getOwnPropertySymbols()方法的返回值是一个包含所有Symbol自有属性的数组

    let name = Symbol.for('name')
    let person = {
      [name]: 'wmui'
    }
    
    let pro = Object.getOwnPropertySymbols(person)
    console.log(pro.length) // 1
    console.log(pro[0]) // Symbol(name)
    console.log(person[pro[0]]) // wmui
    

    另外一个API,Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名

    let name = Symbol.for('name')
    let person = {
      [name]: 'wmui',
      age: 10
    }
    
    console.log(Reflect.ownKeys(person)) // ["age", Symbol(name)] 09:25:57.327
    

    内置Symbol

    ES6提供了11个内置的Symbol值,指向语言内部使用的方法

    Symbol.haslnstance

    一个在执行instanceof时调用的内部方法,用于检测对象的继承信息

    Symbol.isConcatSpreadable

    一个布尔值,用于表示当传递一个集合作为Array.prototype.concat()方法的参数时,是否应该将集合内的元素规整到同一层级

    Symbol.iterator

    一个返回迭代器的方法

    Symbol.match

    一个在调用String.prototype.match()方法时调用的方法,用于比较字符串

    Symbol.replace

    一个在调用String.prototype.replace()方法时调用的方法,用于替换字符串的子串

    Symbol.search

    一个在调用String.prototype.search()方法时调用的方法,用于在字符串中定位子串

    Symbol.species

    用于创建派生类的构造函数

    Symbol.split

    一个在调用String.prototype.split()方法时调用的方法,用于分割字符串

    Symbol.toprimitive

    一个返回对象原始值的方法

    Symbol.ToStringTag

    一个在调用Object.prototype.toString()方法时使用的字符串,用于创建对象描述

    Symbol.unscopables

    一个定义了一些不可被with语句引用的对象属性名称的对象集合

    这些方法平时用到的情况较少,Symbol.isConcatSpreadable可能会用到,这里介绍一下,其余不再详细介绍

    对象的Symbol.isConcatSpreadable属性是一个布尔值,表示调用数组的concat()方法时,多维数组是否可以展开

    // 示例1
    let arr = ['a','b'], arr2 = ['c','d']
    arr.concat(arr2,'e','f') // ["a", "b", "c", "d", "e", "f"]
    console.log(arr[Symbol.isConcatSpreadable]) // undefined
    
    // 示例2
    let arr = ['a','b'], arr2 = ['c','d']
    arr[Symbol.isConcatSpreadable] = true
    arr.concat(arr2,'e','f') // ["a", "b", "c", "d", "e", "f"]
    console.log(arr[Symbol.isConcatSpreadable]) // true
    

    从示例可以看出,数组的默认行为是可以展开。Symbol.isConcatSpreadable属性等于undefined或true,都有这个效果

    当Symbol.isConcatSpreadable属性默认为false,数组将不会展开

    let arr = ['a','b'], arr2 = ['c','d']
    arr[Symbol.isConcatSpreadable] = false
    arr2[Symbol.isConcatSpreadable] = false
    arr.concat(arr2,'e','f') // [["a", "b"], ["c", "d"], "e", "f"]
    
    优秀文章首发于聚享小站,欢迎关注!
  • 相关阅读:
    204. 计数质数
    236. 二叉树的最近公共祖先
    优先队列和哈夫曼树
    185. 部门工资前三高的所有员工(求组内前几的值)
    部门工资最高的员工(求组内最大值)
    回调函数的案例
    单链表
    动态数组
    一致性哈希算法的基本原理
    只用2GB内存在20亿个整数中找到出现次数最多的数
  • 原文地址:https://www.cnblogs.com/yesyes/p/15352390.html
Copyright © 2011-2022 走看看