文档章节

ES6 的 Symbol 类型及使用案例

达尔文
 达尔文
发布于 2017/01/04 21:46
字数 1550
阅读 5170
收藏 54

ES6 为 JavaScript 引入了一种新的基本类型:Symbol,它由全局 Symbol() 函数创建,每次调用 Symbol()函数,都会返回一个唯一的 Symbol。

let symbol1 = Symbol();
let symbol2 = Symbol();
 
console.log( symbol1 === symbol2 ); 
> false

因为每个 Symbol 值都是唯一的,因此该值不与其它任何值相等。

Symbol 是 JavaScript 中的新原始类型。

console.log( typeof symbol1 );
> "symbol"

Symbol 充当唯一的对象键。

let myObject = { 
    publicProperty: 'Value of myObject[ "publicProperty" ]'
};
 
myObject[ symbol1 ] = 'Value of myObject[ symbol1 ]';
myObject[ symbol2 ] = 'value of myObject[ symbol2 ]';
 
console.log( myObject );
> Object
>    publicProperty: "Value of myObject[ "publicProperty" ]"
>    Symbol(): "Value of myObject[ symbol1 ]"
>    Symbol(): "value of myObject[ symbol2 ]"
>    __proto__: Object
 
console.log( myObject[ symbol1 ] );
> Value of myObject[ symbol1 ]

当控制台打印myObject时,你能看到两个 Symbol 值都存储在对象中。"Symbol()" 是调用toString()的返回值,此值表示控制台中存在 Symbol 键。如果我们想访问正确的 Symbol,可以检索相应的值。

Symbol 键的属性不会在对象的 JSON 中显示,也不会在 for-in 循环和Object.keys中被枚举出来:

JSON.stringify( myObject )
> "{"publicProperty":"Value of myObject[ \"publicProperty\" ] "}"
 
for( var prop in myObject ) {
    console.log( prop, myObject[prop] );
}
> publicProperty Value of myObject[ "publicProperty" ] 
 
console.log( Object.keys( myObject ) );
> ["publicProperty"]

即使 Symbol 键的属性没有在上述案例中出现,这些属性在严格意义上也不是完全私有的。Object.getOwnPropertySymbols提供了一种检索对象的 Symbol 的方法。

Object.getOwnPropertySymbols(myObject)
> [Symbol(), Symbol()]
 
myObject[ Object.getOwnPropertySymbols(myObject)[0] ]
> "Value of myObject[ symbol1 ]"

如果你使用 Symbol 键来表示私有变量,要确保不要用Object.getOwnPropertySymbols来检索可能私有化的属性。在这种情况下,Object.getOwnPropertySymbols的唯一使用情况就是测试和调试。

你遵循上述规则,从代码开发的角度来看,对象键值是私有的,但在实际情况中,其他人仍能访问你的私有值。

虽然 Symbol 键不能被for...of扩展运算符和Object.keys枚举,但它们仍被包含在浅拷贝里:

clonedObject = Object.assign( {}, myObject );
 
console.log( clonedObject );
> Object
>    publicProperty: "Value of myObject[ "publicProperty" ]"
>    Symbol(): "Value of myObject[ symbol1 ]"
>    Symbol(): "value of myObject[ symbol2 ]"
>    __proto__: Object

正确命名 Symbol 对指明其用途至关重要,如果你需要额外的语义指导,还可在 Symbol 上附上一个描述。Symbol 的描述体现在 Symbol 的字符串值中。

let leftNode = Symbol( 'Binary tree node' );
let rightNode = Symbol( 'Binary tree node' );
 
console.log( leftNode )
> Symbol(Binary tree node)

始终提供 Symbol 的描述,并始终保持描述的唯一性。如果用 Symbol 访问私有属性,请将其描述视为变量名。

如果你将相同的描述传递给两个 Symbol,它们的值仍不相同。

console.log( leftNode === rightNode );
> false

全局 Symbol 注册表

ES6 有一个用于创建 Symbol 的全局资源:Symbol 注册表,它为字符串和 Symbol 提供了一对一的关系。注册表使用 Symbol.for( key )返回 Symbol。

当出现key1 === key2时就会有Symbol.for( key1 ) === Symbol.for( key2 )。这种对应关系甚至是跨 service worker 和 iframe 的。

let privateProperty1 = Symbol.for( 'firstName' );
let privateProperty2 = Symbol.for( 'firstName' );
 
myObject[ privateProperty1 ] = 'Dave';
myObject[ privateProperty2 ] = 'Zsolt';
 
console.log( myObject[ privateProperty1 ] );
// Zsolt

因为 Symbol 注册表中的 Symbol 值和字符串之间有一一对应的关系,所以我们也可以检索字符串键。使用Symbol.keyFor方法。

Symbol.keyFor( privateProperty1 );
> "firstName"
 
Symbol.keyFor( Symbol() );
> undefined

Symbol 作为半私有属性键

即使 Symbol 不能使属性私有,它们也能用作带有私有属性的符号。你可以使用 Symbol 来分隔公有和私有属性的枚举,Symbol 能使它更清楚。

const _width = Symbol('width');
class Square {
    constructor( width0 ) {
        this[_width] = width0;
    }
    getWidth() {
        return this[_width];
    }
}

只要你能隐藏_width就行了,隐藏_width的方法之一是创建闭包:

let Square = (function() {
 
    const _width = Symbol('width');
 
    class Square {
        constructor( width0 ) {
            this[_width] = width0;
        }
        getWidth() {
            return this[_width];
        }
    }
 
    return Square;  
 
} )();

这样做的好处是,他人很难访问到我们对象的私有_width值,而且也能很好地区分,哪些属性是公有的,哪些属性是私有的。但这种方法的缺点也很明显:

  • 通过调用Object.getOwnPropertySymbols,我们可以使用 Symbol 键。
  • 如果要写很多的代码,这会使得开发者的体验不佳,访问私有属性不像 Java 或 TypeScript 那样方便。

如果你要用 Symbol 来表示私有字段,那你需要表明哪些属性不能被公开访问,如若有人试图违背这一规则,理应承担相应的后果。

创建枚举类型

枚举允许你定义具有语义名称和唯一值的常量。假定 Symbol 的值不同,它们能为枚举类型提供最好的值。

const directions = {
    UP   : Symbol( 'UP' ),
    DOWN : Symbol( 'DOWN' ),
    LEFT : Symbol( 'LEFT' ),
    RIGHT: Symbol( 'RIGHT' )
};

避免名称冲突

当使用 Symbol 作为变量时,我们不必建立可用标识符的全局注册表,也不必费心思想标识符名字,只需要创建一个 Symbol 就行了。

外部库的做法也是这样。

知名 Symbol

这里有一些比较常用的 Symbol,用以访问和修改内部 JavaScript 行为。你可以用它们重新定义内置方法。运算符和循环。

演练

演练1.用下划线来表示字段的私有,有什么利弊?用这种方法和 Symbol 比较。

let mySquare {
    _width: 5,
    getWidth() { return _width; }
}

利:

  • 开发者体验佳
  • 不会造成复杂的代码结构

弊:

  • 属性仅被表示为私有,在实践中并不是私有的,容易被破解
  • 不同于 Symbol,这种方式的公有和私有属性没有很好地区分,私有属性出现在对象的公有接口中,它们使用能被扩展运算符,Object.keysfor..of循环枚举。

演练2. 模拟 JavaScript 中的私有字段。

解决方案:当涉及到构造函数时,可以使用var, let, 或 const在构造函数中声明私有成员。

function F() {
   let privateProperty = 'b';
   this.publicProperty = 'a';
}
 
let f = new F();
 
// f.publicProperty returns 'a'
// f.privateProperty returns undefined 

为了对类使用相同的方法,我们必须放置方法定义:在可访问私有属性的作用域中的构造函数方法中使用私有属性的方法。我们将使用Object.assign来达到此目的。(灵感来自Managing private data of ES6 classes

class C {
    constructor() {
        let privateProperty = 'a';
        Object.assign( this, {
            logPrivateProperty() { console.log( privateProperty ); }
        } );
    }
}
 
let c = new C();
c.logPrivateProperty();

字段privateProperty在对象c中不可访问。

该解决方案也适用于我们扩展 C 类。

原文:ES6 Symbols and their Use Cases

编译:开源中国-达尔文

© 著作权归作者所有

共有 人打赏支持
达尔文

达尔文

粉丝 441
博文 25
码字总数 34746
作品 0
深圳
运营/编辑
私信 提问
加载中

评论(4)

whinc
whinc
Symbol.iterator 这个预设的 symbol 比较常用
李嘉图
李嘉图
mark
hantsy
hantsy
Exploring ES6 一书可以全面了解 ES 6 特性,
young7
young7
不错,Symbol的用法虽然之前略有所闻,但是具体的应用场景还是不太清楚,这篇文章给出了一些建议
ES6 Symbol之浅入解读😂

一、介绍 是ES6新增的数据类型,是一种基础数据类型,MDN 中的对Symbol类型的描述为:数据类型 “symbol” 是一种原始数据类型,该类型的性质在于这个类型的值可以用来创建匿名的对象属性。该...

SaltAir
2018/12/17
0
0
【探秘ES6】系列专栏(八):JS的第七种基本类型Symbols

ES6作为新一代JavaScript标准,已正式与广大前端开发者见面。为了让大家对ES6的诸多新特性有更深入的了解,Mozilla Web开发者博客推出了《ES6 In Depth》系列文章。CSDN已获授权,将持续对该...

一配
2015/11/08
0
0
ES6的Symbol竟然那么强大,面试中的加分点啊

symbol是es6出的一种类型,他也是属于原始类型的范畴(string, number, boolean, null, undefined, symbol) basic symbol for 这个东西是可共享,在创建的时候会检查全局是否寻在这个key的sym...

xiaohesong
2018/11/02
0
0
低门槛彻底理解JavaScript中的深拷贝和浅拷贝

在说深拷贝与浅拷贝前,我们先看两个简单的案例: 按照常规思维,应该和一样,不会因为另外一个值的改变而改变,而这里的 却随着的改变而改变了。同样是变量,为什么表现不一样呢?这就要引入...

lunaqi
2018/05/11
0
0
TypeScript学习笔记之二基本数据类型

一、使用环境 Mac 电脑 WebStorm TypeScript3.x版本 二、需要了解的基本类型有哪些 数据类型有两种: A、原始数据类型 (Primitive data types) boolean 布尔值 number 数值 string 字符串 ...

摸着石头过河_崖边树
2018/12/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周一乱弹 —— 白掌柜说了卖货不卖身

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @爱漫爱 :这是一场修行分享羽肿的单曲《Moony》 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :开不开心? 开心呀, 我又不爱睡懒觉…...

小小编辑
今天
7
0
大数据教程(11.7)hadoop2.9.1平台上仓库工具hive1.2.2搭建

上一篇文章介绍了hive2.3.4的搭建,然而这个版本已经不能稳定的支持mapreduce程序。本篇博主将分享hive1.2.2工具搭建全过程。先说明:本节就直接在上一节的hadoop环境中搭建了! 一、下载apa...

em_aaron
今天
2
0
开始看《JSP&Servlet学习笔记》

1:WEB应用简介。其中1.2.1对Web容器的工作流程写得不错 2:编写Servlet。搞清楚了Java的Web目录结构,以及Web.xml的一些配置作用。特别是讲了@WebServlet标签 3:请求与响应。更细致的讲了从...

max佩恩
今天
4
0
mysql分区功能详细介绍,以及实例

一,什么是数据库分区 前段时间写过一篇关于mysql分表的的文章,下面来说一下什么是数据库分区,以mysql为例。mysql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面(可...

吴伟祥
今天
3
0
SQL语句查询

1.1 排序 通过order by语句,可以将查询出的结果进行排序。放置在select语句的最后。 格式: SELECT * FROM 表名 ORDER BY 排序字段ASC|DESC; ASC 升序 (默认) DESC 降序 1.查询所有商品信息,...

stars永恒
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部