ES6实践:Symbols及其使用—SitePoint

更新日期: 2018-08-29阅读: 3.2k标签: 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对象作为hashmapphp术语中的关联数组或者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

链接: https://www.fly63.com/article/detial/1039

es6 箭头函数的使用总结,带你深入理解js中的箭头函数

箭头函数是ES6中非常重要的性特性。它最显著的作用就是:更简短的函数,并且不绑定this,arguments等属性,它的this永远指向其上下文的 this。它最适合用于非方法函数,并且它们不能用作构造函数。

详解JavaScript模块化开发require.js

js模块化的开发并不是随心所欲的,为了便于他人的使用和交流,需要遵循一定的规范。目前,通行的js模块规范主要有两种:CommonJS和AMD

js解构赋值,关于es6中的解构赋值的用途总结

ES6中添加了一个新属性解构,允许你使用类似数组或对象字面量的语法将数组和对象的属性赋给各种变量。用途:交换变量的值、从函数返回多个值、函数参数的定义、提取JSON数据、函数参数的默认值...

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

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

ES6的7个实用技巧

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

ES6 Decorator_js中的装饰器函数

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

基于ES6的tinyJquery

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

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

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

Rest/Spread 属性_探索 ES2018 和 ES2019

Rest/Spread 属性:rest操作符在对象解构中的使用。目前,该操作符仅适用于数组解构和参数定义。spread操作符在对象字面量中的使用。目前,这个操作符只能在数组字面量和函数以及方法调用中使用。

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

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

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!