Symbol

作者: haha2333 | 来源:发表于2019-07-27 10:46 被阅读0次

对于一项新知识,要知道它有什么用,解决了什么问题,那么才有研究它的动力。所以学习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值

先那么多吧。。。

相关文章

  • Symbol

    Symbol 每个Symbol都是独一无二的Symbol() !== Symbol() Symbol 不能进行运算...

  • Symbol

    1、window.Symbol 2、不允许new Symbol 3、symbol() !== symbol() 4...

  • Symbol学习笔记

    一.Symbol是什么 typeof Symbol() === 'symbol',symbol是js中第7种基本类...

  • ES6补充以及Node事件轮询

    Symbol es6中新增了symbol类型,symbol类型的值是通过symbol函数生成的,相同symbol函...

  • 深入理解ES6六

    Symbol ES6新增的数据类型Symbol 创建Symbol:通过去全局的Symbol函数创建一个Symbol...

  • JS-学习ES6之-Symbol数据类型[整理稿]

    为什么要有Symbol Symbol可以传参数 Symbol 作为属性名的用法 Symbol.for() 1. 为...

  • Symbol简单理解

    Symbol 概述 作为属性名的symbol 属性名的遍历 Symbol的方法 内置的symbol值 概述 Sym...

  • ES6-新增数据类型

    symbol类型 全局函数window.Symbol() typeof window.Symbol() 返回的类型...

  • ES6-Symbol

    创建Symbol实例let s1 = Symbol()let s2 = Symbol('another symbo...

  • umdh--查找内存泄漏--使用方法

    set _NT_SYMBOL_PATH=symbol目录多个目录,以’;’隔开 例:Set _NT_SYMBOL_...

网友评论

      本文标题:Symbol

      本文链接:https://www.haomeiwen.com/subject/tgscrctx.html