ES6实践:Symbols及其使用—SitePoint

时间: 2018-08-29阅读: 130标签: es6

虽然ES2015已经引入了许多开发人员期待已久的语言特性,但还有一些新特性不太为人所知和理解,其好处也不太清楚——比如symbols。

symbol(符号)是一种新的原始数据类型,一个确保不会和其它符号冲突的唯一令牌。从这个意义上讲,你可以把符号看作是一种UUID(通用唯一识别码)。 让我们看看符号是如何工作的,以及我们能用它做些什么。


创建符号

创建符号非常直接,就是简单地调用Symbol函数。需要注意的是这只是一个标准的函数而不是一个对象构造器。使用new操作符调用它会导致一个类型错误。每次调用Symbol函数的时候,都会得到一个全新的唯一的值。

const foo = Symbol();
const bar = Symbol();

foo === bar
// <-- false


创建符号的时候可以给符号添加一个标签,方式是传递字符串作为第一个参数。标签并不能影响符号的值,只是利于调试,并且当符号的toString()方法被调用的时候会显示出来。创建具有相同标签的多个符号是可行的,但是这样做没有任何好处,并很可能引起疑惑。

let foo = Symbol('baz');
let bar = Symbol('baz');

foo === bar
// <-- false
console.log(foo);
// <-- Symbol(baz)


符号能用来做什么?

符号可以很好地替代作为类/模块常量的字符串或者整数:

class Application {
  constructor(mode) {
    switch (mode) {
      case Application.DEV:
        // Set up app for development environment
        break;
      case Application.PROD:
        // Set up app for production environment
        break;
      case default:
        throw new Error('Invalid application mode: ' + mode);
    }
  }
}

Application.DEV = Symbol('dev');
Application.PROD = Symbol('prod');

// Example use
const app = new Application(Application.DEV);


字符串和整数并不是唯一的值;比如数字2或者字符串development也可能在程序的其它地方被用于不同的目的。使用符号意味着我们可以对提供的值更有信心。

符号的另外一个有趣的用法是作为对象属性的键值。如果你曾经把JavaScript对象作为hashmap(PHP术语中的关联数组或者Python中的字典)使用,你就会对使用括号语法获取/设置属性很熟悉:

const data = [];

data['name'] = 'Ted Mosby';
data['nickname'] = 'Teddy Westside';
data['city'] = 'New York';


使用括号语法,我们也可以使用符号做为属性的键值。这样做有很多优点。首先,可以确保基于符号的键值不会冲突,不像字符串键值有可能会和对象已有的属性或者方法冲突。其次,符号不会被for ... in枚举,并且会被Object.keys(),Object.getOwnPropertyNames(),JSON.stringify()等方法忽略。对于在序列化对象时不想被包含的属性来说,符号是一个理想的选择。

const user = {};
const email = Symbol();

user.name = 'Fred';
user.age = 30;
user[email] = 'fred@example.com';

Object.keys(user);
// <-- Array [ "name", "age" ]

Object.getOwnPropertyNames(user);
// <-- Array [ "name", "age" ]

JSON.stringify(user);
// <-- "{"name":"Fred","age":30}"


然而,值得注意的是,使用符号做为键值并不能保证私有。有一些新的工具允许访问符号类型的属性键值。Object.getOwnPropertySymbols()返回基于符号的键值组成的数组,Reflect.ownKeys()返回所有键值,包含符号键值,组成的数组。

Object.getOwnPropertySymbols(user);
// <-- Array [ Symbol() ]

Reflect.ownKeys(user)
// <-- Array [ "name", "age", Symbol() ]


有名的符号

因为以符号做为键值的属性在ES6之前的代码中是不可见的,所以在保证向后兼容的同时,符号是给JavaScript现有类型添加新功能的理想选择。所谓“有名”的符号是预定义在Symbol函数上的属性,它们被用来自定义某些语言特性的行为,实现新的功能,比如迭代器。

Symbol.iterator是一个有名的符号,被用来给对象添加一个特殊的方法,使得对象可以被迭代:

const band = ['Freddy', 'Brian', 'John', 'Roger'];
const iterator = band[Symbol.iterator]();

iterator.next().value;
// <-- { value: "Freddy", done: false }
iterator.next().value;
// <-- { value: "Brian", done: false }
iterator.next().value;
// <-- { value: "John", done: false }
iterator.next().value;
// <-- { value: "Roger", done: false }
iterator.next().value;
// <-- { value: undefined, done: true }


内建类型字符串,数组,类型数组,Map(映射)和Set(集合)都有一个默认的Symbol.iterator方法。当上述类型的实例被for ... of循环或者用于扩展运算符的时候就会调用这个方法。浏览器也开始使用Symbol.iterator键值让DOM结构,比如NodeList和HTMLCollection以同样的方式被迭代。


全局注册表

规范还定义了一个运行时范围的符号注册表,这意味着你可以在不同的执行上下文,比如在文档、内嵌iframe或者service worker之间,存储和获取符号。

Symbol.for(key)用来获取注册表中给定键值的符号。如果对应这个键值的符号不存在,会返回一个新的符号。正如你预期的那样,对于同一个值,后续的调用都会返回同一个符号。

Symbol.keyFor(symbol)允许你获取给定符号的键值。如果注册表中不存在给定的符号,那么会返回undefined:

const debbie = Symbol.for('user');
const mike   = Symbol.for('user');

debbie === mike
// <-- true

Symbol.keyFor(debbie);
// <-- "user"


用例

在一些用例中,使用符号提供了优势。其中之一是在文章开头提到的,当给一个对象添加不想被对象序列化的时候包含在内的属性,就好像属性是“隐藏”的时候,使用符号就是一个很好的选择。

代码库的作者可以放心地使用符号给客户端对象添加属性和方法,而不用担心覆盖已有的键值(或者自己添加的键值被别的代码覆盖)。例如,小部件组件(比如时间选择器)在初始化的时候会产生很多选项和状态,这些选项和状态需要存储在某个地方。把组件实例赋值给DOM元素对象的一个属性并不是理想的选择,因为这个属性有可能会和其它的键值冲突。使用基于符号的键值可以巧妙地解决这个问题,确保组件实例不会被覆盖。有关这个想法更详细的探索,请查看Mozilla Hacks的博客ES6 in Depth: Symbols


浏览器支持情况

如果你想体验符号,主流浏览器的支持是很好的。如你所见,Chrome,Firefox,Microsoft Edge和Opera的当前版本,移动设备上的Android 5.1和iOS 9都原生支持符号了。如果需要兼容较老版本的浏览器,可以使用腻子脚本


总结

虽然引入符号的主要原因似乎是为了在兼容现有代码的前提下为语言添加新功能,但它们确实有一些有趣的用途。对于所有开发人员来说,至少对它们有一个基本的了解,并且熟悉最常用的、有名的符号及其用途是值得的。


原文链接: www.sitepoint.com  
翻译来源:https://www.zcfy.cc/article/es6-in-action-symbols-and-their-uses-sitepoint

es6中const定义的属性是否可以改变_为什么有人说const并非一定为常量

const是用来定义常量的,而且定义的时候必须初始化,且定义后不可以修改。const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

ES5和ES6对象导出和导入

如果模块中使用 export default {},只能通过 import 对象名称 from 模块路径 ,不能通过 import {对象名称} from 模块路径。如果使用 import {对象名称} from 模块路径 导出具体某个对象或者方法名称

在使用es6语法class的时候,babel到底做了什么?

自从有了webpack之后,我们这些jscoder似乎得到了前所未有的解放,箭头函数,对象解构,let,const关键字,以及class、extends等等,webpack会帮我们把这些es6代码转换成浏览器能够识别的es5代码,那么有多少人真正的看过,babel转换之后的代码呢?

使用ES6让你的React代码提升到一个新档次

ES6使您的代码更具表现力和可读性。而且它与React完美配合!现在您已了解更多基础知识:现在是时候将你的ES6技能提升到一个新的水平!嵌套props解构、 传下所有props、props解构、作为参数的函数、列表解构

es6中新增的字符串方法

es6中新增的字符串方法:字符串模板用法${变量名}、字符串查找方法string.includes(要找得字符串)、检查字符串是否已xxx开头、字符串重复方法string.repeat(次数)、字符串填充string.padStart

ES6 中的一些技巧,使你的代码更清晰,更简短,更易读!

ES6 中的一些技巧:模版字符串、块级作用域、Let、Const、块级作用域函数问题、扩展运算符、函数默认参数、解构、对象字面量和简明参数、动态属性名称、箭头函数、for … of 循环、数字字面量。

基于ES6的tinyJquery

Query作为曾经Web前端的必备利器,随着MVVM框架的兴起,如今已稍显没落。用ES6写了一个基于class简化版的jQuery,包含基础DOM操作,支持链式操作...

ES6 Decorator_js中的装饰器函数

ES6装饰器(Decorator)是一个函数,用来修改类的行为 在设计阶段可以对类和属性进行注释和修改。从本质上上讲,装饰器的最大作用是修改预定义好的逻辑,或者给各种结构添加一些元数据。

ES6的7个实用技巧

ES6的7个实用技巧包括:1交换元素,2 调试,3 单条语句,4 数组拼接,5 制作副本,6 命名参数,7 Async/Await结合数组解构

ES6中let变量的特点,使用let声明总汇

ES6中let变量的特点:1.let声明变量存在块级作用域,2.let不能先使用再声明3.暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,4.不允许重复声明