Symbol

对于一项新知识,要知道它有什么用,解决了什么问题,那么才有研究它的动力。所以学习Symbol从它解决了什么问题出发。

“ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。”

一开始这段话我好像看懂了,但是实际上那要怎么用???迷惑
那就把这段话翻译成代码吧

举个例子,你看上了公司前来的前台妹纸,想了解关于她的更多信息,于是就询问Hr同事,扫地阿姨,于是得到类似这样信息:

let info1 = {
    name: '婷婷',
    age: 24,
    job: '公司前台',
    description: '平时喜欢做做瑜伽,人家有男朋友,你别指望了'
}
let info2 = {
    description: '这小姑娘挺好的,挺热情的,嘿嘿嘿……'
}

当我们汇总这位前台小姐的信息时,就发现,``` description:'这小姑娘挺好的,挺热情的,嘿嘿嘿……'``导致别人有男朋友这个重要的信息丢失了。
但是我们用了symbol之后

let info1 = {
    name: '婷婷',
    age: 24,
    job: '公司前台',
    [Symbol('description')]: '平时喜欢做做瑜伽,人家有男朋友,你别指望了'
}
let info2 = {
    [Symbol('description')]: '这小姑娘挺好的,挺热情的,嘿嘿嘿……'
}
let target ={}
Object.assign(target,info1,info2)
console.log(target)

//age: 24
//job: "公司前台"
//name: "婷婷"
//Symbol(description): "平时喜欢做做瑜伽,人家有男朋友,你别指望了"
//Symbol(description): "这小姑娘挺好的,挺热情的,嘿嘿嘿……"

他的信息就被完整保留下来了。

啊,谢天谢地。终于弄懂Symbol是怎么解决命名冲突的了。

下面开始深入学习Symbol的用法

  • Symbol是js基本数据类型,Symbol 值通过Symbol函数生成。不能用new运算符创建
var a = Symbol() 

括号里面可以接收一个字符串参数,作为该实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

var a = Symbol('foo') 
a // Symbol(foo)
a.toString() // "Symbol(foo)"
  • Symbol()返回值是唯一的,也就是,即使描述相同,他们也是独立的两个值。
Symbol('description') === Symbol('description');    // 返回值是false

这跟引用类型一样

var a = new Object()
var b = new Object()
a===b // false

如果是普通的基本类型,比如

var a = 'string'
var b = 'string'
a===b //true
  • Symbol值不可以和其他类型的值进行运算,可以显示转换为字符串或者布尔值
let sym = Symbol('My symbol');
String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
let sym = Symbol();
Boolean(sym) // true
!sym  // false
if (sym) {
  // ...
}
Number(sym) // TypeError
sym + 2 // TypeError

一些属性和方法

  • 读取Symbol的描述description
const sym = Symbol('foo');
sym.description // "foo"
  • 作为常量的Symbol
    常量使用 Symbol 值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的switch语句会按设计的方式工作。
    emmm到底是怎样的特别适合呢?
    比如
let a='a'+'b'
let b='ab'
a===b  //true

这样case 'ab'中,变量a和b都是符合条件的,但是我们只想要b这种形式的。这个时候把b变成symbol就很有必要啦。
大概是这样吧

const COLOR_RED    = Symbol();
const COLOR_GREEN  = Symbol();
function getComplement(color) {
  switch (color) {
    case COLOR_RED:
      return COLOR_GREEN;
    case COLOR_GREEN:
      return COLOR_RED;
    default:
      throw new Error('Undefined color');
    }
}
  • 作为属性名的Symbol
    这也是一开头提到的那个例子
    有4种写法
let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

以上三种,都保留了Symbol的引用值,就是一开头的mySymbol。这些引用值还是需要不一样,才能正确引用对应的Symbol值。这样写引用方式也比较简单。
但是,这样跟我写

var obj = {
    a:"a",
    b:"b"
}

有什么区别,还不是要起不一样的引用值的名字!
所以就有了最开头例子的那种写法,

let info2 = {
    [Symbol('description')]: '这小姑娘挺好的,挺热情的,嘿嘿嘿……'
}

不保留他的引用值,通过Object.getOwnPropertySymbols(对象名)
这个方法返回的是该对象中Symbol值的数组

let array = Object.getOwnPropertySymbols(target);
console.log(array); 
console.log(target[array[0]]);  

//Array [ Symbol(description), Symbol(description) ]
//'平时喜欢做做瑜伽,人家有男朋友,你别指望了'

在此插播一个小知识点
es6之前变量属性名称都是字符串型的
访问对象普通属性时,可以使用点运算符obj.a或者obj['a']进行访问。访问Symbol类型的对象属性时,用obj[a]

Symbol 值作为属性名时,该属性还是公开属性,不是私有属性。

Symbol 作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。
就是上面的例子中使用过的
Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
};

Reflect.ownKeys(obj)
//  ["enum", "nonEnum", Symbol(my_key)]

由于以 Symbol 值作为名称的属性,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。

let size = Symbol('size');

class Collection {
  constructor() {
    this[size] = 0;
  }

  add(item) {
    this[this[size]] = item;
    this[size]++;
  }

  static sizeOf(instance) {
    return instance[size];
  }
}

let x = new Collection();
Collection.sizeOf(x) // 0

x.add('foo');
Collection.sizeOf(x) // 1

Object.keys(x) 
Object.getOwnPropertyNames(x) 
Object.getOwnPropertySymbols(x)  
//  [Symbol(size)]

看到这里,要去补一下js的class了。。。

  • Symbol的复用
    因为每次使用Symbol()方法,即使描述符相同,也不是同一个symbol了,想要复用symbol,即重新使用同一个 Symbol 值,Symbol.for方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true

感觉面试会问
Symbol()Symbol.for()的区别:
虽然Symbol()Symbol.for()都可以创建一个新的Symbol值,但是前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。

Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key。(就是使用过Symbol.for())

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

小总结:
1.Symbol数据类型的引入,解决了变量名冲突的问题
2.Reflect.ownKeys(obj)可以返回对象所有属性,包括Symbol值
3.Object.getOwnPropertySymbols(obj)可以获得该对象上的Symbol值,返回一个数组
4.通过Object.for('key')在全局环境中查找key值,找到则返回那个值,找不到就在全局环境中注册并返回该值
5.在全局环境中注册过的Symbol值,可以使用Object.keyfor('变量名')返回该值都key值

先那么多吧。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • 1.概述 ES5的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象...
    赵然228阅读 802评论 2 10
  • 概述 ES5的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加...
    oWSQo阅读 515评论 1 3
  • 概述 ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添...
    米诺zuo阅读 332评论 0 0
  • Symbol 概述 作为属性名的symbol 属性名的遍历 Symbol的方法 内置的symbol值 概述 Sym...
    Carmen_e69d阅读 3,766评论 0 2
  • 2017-12-23,急诊胸痛中心建设茶话会 各地胸痛中心建设如火如荼,今天有幸参加了急诊胸痛中心建设研讨的圆...
    坚持行动家阅读 3,031评论 2 0