前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ES6之symbol

ES6之symbol

作者头像
糊糊糊糊糊了
发布2018-05-09 15:54:39
5010
发布2018-05-09 15:54:39
举报
文章被收录于专栏:糊一笑糊一笑

为什么需要使用Symbol

考虑到以下场景:

代码语言:javascript
复制
// a library
var L = (function() {
    var prop1 = 'prop1';
    function haha() { console.log('world haha'); }
    // ...
    return {
        prop1 : prop1,
        haha : someFn
    };
})();

// using
L.haha = function() { console.log('hello world!'); };

// call
L.haha();   // hello world!

看出问题来了么?一个公共库暴露出了一些方法和属性,但是在我们调用的时候我们并不知道它其中有什么方法或者属性,这样当我们在这个库上进行定义了相同的属性的时候,这个库就会被暴露的属性就有可能会被重写。

Symbol引入

所以为了保证变量的唯一性,ES6在原本的6中基础数据类型(Undefined、Null、Boolean、String、Number、Object)下中引入了Symbol类型,它是独一无二的。

代码语言:javascript
复制
// using
var haha = Symbol();
L[haha] = function() { console.log('hello world!'); };

// call
L.haha();   // world haha
L[haha]();  // hello world!
Symbol值

Symbol值通过Symbol函数生成,由于生成的值是一个原始类型,不是对象,所以不能使用new关键字,否则会报错。Symbol值是唯一的,不会和其他属性名产生冲突。

代码语言:javascript
复制
var symbol1 = Symbol();
var symbol2 = Symbol();

console.log(typeof symbol1);    // symbol
symbol1 === symbol2;            // false

// Uncaught TypeError: Symbol is not a constructor
var symbol = new Symbol();      

注:Symbol值不能和其他值运算,否则会报错。

代码语言:javascript
复制
var s1 = 'str1';
var symbol = Symbol();

// Uncaught TypeError: Cannot convert a Symbol value to a string
console.log(s1 + symbol);   
Symbol参数

Symbol函数可以接受一个字符串作为参数,表示对这个Symbol值的描述。如果传递的是一个对象,那么会调用这个对象的toString方法。

代码语言:javascript
复制
var foo = Symbol('foo');
var bar = Symbol('bar');
var obj = {
    toString: function() {
        return 'hello';
    }
};
var o = Symbol(obj);

console.log(foo);   // Symbol(foo)
console.log(bar);   // Symbol(bar)
console.log(o);     // Symbol(hello)

Symbol作为属性名

前面介绍到了,为了保证属性名的不冲突才引入了Symbol。作为对象的属性名,Symbol主要有一下三种写法:

代码语言:javascript
复制
let obj = {};
let s = Symbol();

// 第一种写法
obj[s] = 'mySymbol';

// 第二种写法
obj = {
    [s]: 'mySymbol'
}
// 第三种写法
Object.defineProperty(obj, s, { value: 'mySymbol' });
obj.s;      // undefined

obj.s = 'mySymbol2';
obj[s]      // mySymbol2
obj['s']    // mySymbol

遍历Symbol属性

Symbol值作为属性,无法使用for...in或者for...of来获取,也无法使用Object.keys或者Object.getOwnPropertyNames来获取。那么通过什么办法来获取Symbol属性值呢?我们可以使用Object.getOwnPropertySymbols()来获取一个对象上的属性名,通过Reflect.ownKeys()来获取所有类型的属性名。

代码语言:javascript
复制
let s1 = Symbol('s1');
let s2 = Symbol('s2');
let testObj = { 
    normal: 'normal',
    [s1]: s1,
    [s2]: s2
};

// for...in
for (let key in testObj) {
    if (Object.prototype.hasOwnProperty.call(testObj, key)) {
        console.log('key', key);    // key normal
    }
}

// getOwnPropertyNames
console.log(Object.getOwnPropertyNames(testObj));   // ["normal"]

// getOwnPropertySymbols
console.log(Object.getOwnPropertySymbols(testObj)); // [Symbol(s1), Symbol(s2)]

// Reflect.ownKeys
console.log(Reflect.ownKeys(testObj));  // ["normal", Symbol(s1), Symbol(s2)]

Symbol.for()和Symbol.keyFor()

如果想获取同一个Symbol值,我们可以给Symbol.for()中传递一个字符串参数。生成的这个Symbol值会在全局环境中登记,如果下次还传输这个相同的key值的时候,就会在全局环境中搜索是否有已经存在了这个key值的Symbol值,如果有就会返回。Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key

代码语言:javascript
复制
let s1 = Symbol.for('s1');
let s2 = Symbol.for('s1');

s1 === s2;  // true
console.log(Symbol.keyFor(s1)); // s1

内置的Symbol值

作为对象的内置Symbol属性值,会在特定的情景下触发。比如Symbol.split会在该对象被String.prototype.split调用时触发,而Symbol.itertor指向的函数会在使用for...of遍历这个对象的时候触发。具体其他的内置属性可以参看文档http://es6.ruanyifeng.com/#docs/symbol#内置的Symbol值;

应用场景

作为半私有属性键

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

代码语言:javascript
复制
const _width = Symbol('width');
class Square {
    constructor( width0 ) {
        this[_width] = width0;
    }
    getWidth() {
        return this[_width];
    }
}

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

代码语言:javascript
复制
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 的值不同,它们能为枚举类型提供最好的值。

代码语言:javascript
复制
const directions = {
    UP   : Symbol( 'UP' ),
    DOWN : Symbol( 'DOWN' ),
    LEFT : Symbol( 'LEFT' ),
    RIGHT: Symbol( 'RIGHT' )
};
避免名称冲突

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么需要使用Symbol
  • Symbol引入
    • Symbol值
      • Symbol参数
      • Symbol作为属性名
      • 遍历Symbol属性
      • Symbol.for()和Symbol.keyFor()
      • 内置的Symbol值
      • 应用场景
        • 作为半私有属性键
          • 创建枚举类型
            • 避免名称冲突
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档