文档章节

ES6 的 Symbol 类型及使用案例

达尔文
 达尔文
发布于 2017/01/04 21:46
字数 1550
阅读 1.6W
收藏 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

编译:开源中国-达尔文

达尔文

达尔文

粉丝 445
博文 25
码字总数 34746
作品 0
深圳
运营/编辑
私信 提问
加载中
此博客有 4 条评论,请先登录后再查看。
CDH5: 使用parcels配置lzo

一、Parcel 部署步骤 1 下载: 首先需要下载 Parcel。下载完成后,Parcel 将驻留在 Cloudera Manager 主机的本地目录中。 2 分配: Parcel 下载后,将分配到群集中的所有主机上并解压缩。 3 激...

cloud-coder
2014/07/01
6.8K
1
基于JavaScript的ES6虚拟机--Continuum

ECMAScript6(ES6)规范计划在今年正式发布,作为JavaScript的核心,新版本的一些特性可能会让目前的开发方式发生巨大的变化。目前一些现代浏览器(如Chrome、Firefox等)中已经逐步实现了E...

匿名
2013/01/06
1.9K
0
使用IBPP在C++中操作FireBird/Interbase数据库

FireBird是一种小巧的关系型数据库,它有多种版本,包括服务器版(象MySQL),单机版(象Access)以及嵌入式(象SQLite)。而且不管是服务器版还是嵌入式版它都完整支持视图、触发器、存储过程等...

Waiting4you
2009/07/26
3.8K
2
使用CImg处理三维图像

http://www.cppprog.com/2009/0429/110.html

Waiting4you
2009/05/05
1.6K
0
在C++中使用Lua

http://www.cppprog.com/2009/0209/62.html

Waiting4you
2009/05/05
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

【数据结构与算法Python描述】——字符串、元组、列表内存模型简介

文章目录 一、计算机内部存储模型简介 1. 内存地址 2. 变量本质 3. 数组概念 二、字符串元组列表内存模型 1. 字符串的内存模型 2. 列表元组内存模型 3. 两种内存模型比较 4. 自定义类字符串序...

osc_hxm151is
3分钟前
0
0
在JavaScript中修剪字符串? - Trim string in JavaScript?

问题: 如何修剪JavaScript中的字符串? 解决方案: 参考一: https://stackoom.com/question/25nu/在JavaScript中修剪字符串 参考二: https://oldbug.net/q/25nu/Trim-string-in-JavaScrip...

法国红酒甜
3分钟前
0
0
在使用钢筋弯箍机时有哪些需要注意的地方

钢筋弯箍机属于一种对钢筋弯曲机结构的改进。大家在使用钢筋弯箍机时也需要进行一下培训 ,在使用钢筋弯箍机的时候的一些注意事项,避免对造成自身危害,接下来就由小拜年给大家介绍一下吧。...

zmjt063
4分钟前
0
0
Python类(一):总结复习

面向对象 “面向对象的方法主要是把事物给对象化,包括其属性和行为。面向对象编程更贴近实际生活的思想。总体来说面向对象的底层还是面向过程,面向过程抽象成类,然后封装,方便使用就是面...

osc_e462bqw3
5分钟前
0
0
你中奖了吗?恭喜JK,请尽快填写地址,寄出奖品!往期中奖名单,除了struts2未取得联系外都已寄出!再次感谢支持,如果你有什么问题我能帮到,欢迎私聊!✌🏻️!

本文分享自微信公众号 - 转行程序员(be_coder)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。...

励志程序员
2019/09/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部