Symbol
概述
引入 Symbol 的原因: 保证每个属性的名字都是独一无二的, 这样就从根本上防止属性名的冲突.
原始数据结构 Symbol, 表示独一无二的值.
Symbol 值通过 Symbol 函数生成.
- let s = Symbol();
- typeof s
- // "symbol"
注意, Symbol 函数前不能使用 new 命令, 否则会报错. 这是由于 Symbol 值不是对象, 所以不能添加属性. 基本上, 它是一种类似于字符串的数据类型.
Symbol 函数可以接受一个字符串作为参数, 表示对 Symbol 实例的描述, 主要是为了在控制台显示, 或者转为字符串时, 比较容易区分.
- let s1 = Symbol('foo');
- let s2 = Symbol('bar');
- s1 // Symbol(foo)
- s2 // Symbol(bar)
- s1.toString() // "Symbol(foo)"
- s2.toString() // "Symbol(bar)"
s1 和 s2 是两个 Symbol 值. 如果不加参数, 它们在控制台的输出都是 Symbol(), 不利于区分. 有了参数以后, 就等于为它们加上了描述, 输出的时候就能够分清, 到底是哪一个值.
如果 Symbol 的参数是一个对象, 就会调用该对象的 toString 方法, 将其转为字符串, 然后才生成一个 Symbol 值.
- const obj = {
- toString() {
- return 'abc';
- }
- };
- const sym = Symbol(obj);
- sym // Symbol(abc)
注意, Symbol 函数的参数只是表示对当前 Symbol 值的描述, 因此相同参数的 Symbol 函数的返回值是不相等的.
/ 没有参数的情况
- let s1 = Symbol();
- let s2 = Symbol();
- s1 === s2 // false
- // 有参数的情况
- let s1 = Symbol('foo');
- let s2 = Symbol('foo');
- s1 === s2 // false
上面代码中, s1 和 s2 都是 Symbol 函数的返回值, 而且参数相同, 但是它们是不相等的.
Symbol 值不能与其他类型的值进行运算, 会报错.
- let sym = Symbol('My symbol');
- "your symbol is" + sym
- // TypeError: can't convert symbol to string
- `your symbol is ${sym}`
- // TypeError: can't convert symbol to string
但是, Symbol 值可以显式转为字符串.
- let sym = Symbol('My symbol');
- String(sym) // 'Symbol(My symbol)'
- sym.toString() // 'Symbol(My symbol)'
另外, Symbol 值也可以转为布尔值, 但是不能转为数值.
- let sym = Symbol();
- Boolean(sym) // true
- !sym // false
- if (sym) {
- // ...
- }
- Number(sym) // TypeError
- sym + 2 // TypeError
- Symbol.prototype.description
创建 Symbol 的时候, 可以添加一个描述.
const sym = Symbol('foo');
上面代码中, sym 的描述就是字符串 foo.
但是, 读取这个描述需要将 Symbol 显式转为字符串, 即下面的写法.
- const sym = Symbol('foo');
- String(sym) // "Symbol(foo)"
- sym.toString() // "Symbol(foo)"
ES2019 提供了一个实例属性 description, 直接返回 Symbol 的描述.
- const sym = Symbol('foo');
- sym.description // "foo"
作为属性名的 Symbol
由于每一个 Symbol 值都是不相等的, 这意味着 Symbol 值可以作为标识符, 用于对象的属性名, 就能保证不会出现同名的属性. 这对于一个对象由多个模块构成的情况非常有用, 能防止某一个键被不小心改写或覆盖
- let mySymbol = Symbol();
- // 第一种写法
- let a = {};
- a[mySymbol] = 'Hello!';
- // 第二种写法
- let a = {
- [mySymbol]: 'Hello!'
- };
- // 第三种写法
- let a = {};
- Object.defineProperty(a, mySymbol, { value: 'Hello!' });
- // 以上写法都得到同样结果
- a[mySymbol] // "Hello!"
上面代码通过方括号结构和 Object.defineProperty, 将对象的属性名指定为一个 Symbol 值.
注意, Symbol 值作为对象属性名时, 不能用点运算符.
- const mySymbol = Symbol();
- const a = {
- };
- a.mySymbol = 'Hello!';
- a[mySymbol] // undefined
- a['mySymbol'] // "Hello!"
上面代码中, 因为点运算符后面总是字符串, 所以不会读取 mySymbol 作为标识名所指代的那个值, 导致 a 的属性名实际上是一个字符串, 而不是一个 Symbol 值.
同理, 在对象的内部, 使用 Symbol 值定义属性时, Symbol 值必须放在方括号之中.
- let s = Symbol();
- let obj = {
- [s]: function (arg) { ... }
- };
- obj[s](123);
上面代码中, 如果 s 不放在方括号中, 该属性的键名就是字符串 s, 而不是 s 所代表的那个 Symbol 值
采用增强的对象写法, 上面代码的 obj 对象可以写得更简洁一些.
- let obj = {
- [s](arg) { ... }
- };
Symbol 类型还可以用于定义一组常量, 保证这组常量的值都是不相等的.
- const log = {};
- log.levels = {
- DEBUG: Symbol('debug'),
- INFO: Symbol('info'),
- WARN: Symbol('warn')
- };
- console.log(log.levels.DEBUG, 'debug message');
- console.log(log.levels.INFO, 'info message');
下面是另外一个例子.
- 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 值最大的好处, 就是其他任何值都不可能有相同的值了, 因此可以保证上面的 switch 语句会按设计的方式工作.
还有一点需要注意, Symbol 值作为属性名时, 该属性还是公开属性, 不是私有属性.
实例: 消除魔术字符串
题目
简单介绍一下 symbol
Symbol 是 ES6 的新增属性, 代表用给定名称作为唯一标识, 这种类型的值可以这样创建
let id=symbol("id")
Symbl 确保唯一, 即使采用相同的名称, 也会产生不同的值, 我们创建一个字段, 仅为知道对应 symbol 的人能访问, 使用 symbol 很有用, symbol 并不是 100% 隐藏, 有内置方法 Object.getOwnPropertySymbols(obj) 可以获得所有的 symbol.
也有一个方法 Reflect.ownKeys(obj) 返回对象所有的键, 包括 symbol.
所以并不是真正隐藏. 但大多数库内置方法和语法结构遵循通用约定他们是隐藏的,
ES6 箭头函数的特性
ES6 增加了箭头函数, 基本语法为
let func = value => value;
相当于
- let func = function (value) {
- return value;
- };
箭头函数与普通函数的区别在于:
箭头函数没有 this, 所以需要通过查找作用域链来确定 this 的值, 这就意味着如果箭头函数被非箭头函数包含, this 绑定的就是最近一层非箭头函数的 this
箭头函数没有自己的 arguments 对象, 但是可以访问外围函数的 arguments 对象
不能通过 new 关键字调用, 同样也没有 new.target 值和原型
简单讲一讲 ES6 的一些新特性
ES6 在变量的声明和定义方面增加了 let,const 声明变量, 有局部变量的概念,
赋值中有比较吸引人的结构赋值,
同时 ES6 对字符串, 数组, 正则, 对象, 函数等拓展了一些方法, 如字符串方面的模板字符串, 函数方面的默认参数, 对象方面属性的简洁表达方式,
ES6 也 引入了新的数据类型 symbol, 新的数据结构 set 和 map,symbol 可以通过 typeof 检测出来
为解决异步回调问题, 引入了 promise 和 generator,
还有最为吸引人了实现 Class 和模块, 通过 Class 可以更好的面向对象编程, 使用模块加载方便模块化编程, 当然考虑到 浏览器兼容性, 我们在实际开发中需要使用 babel 进行编译
重要的特性:
ES5 只有全局作用域和函数作用域, 块级作用域的好处是不再需要立即执行的函数表达式, 循环体中的闭包不再有问题
REST 参数: 用于获取函数的多余参数, 这样就不需要使用 arguments 对象了,
promise: 一种异步编程的解决方案, 比传统的解决方案回调函数和事件更合理强大
模块化: 其模块功能主要有两个命令构成, export 和 import,export 命令用于规定模块的对外接口, import 命令用于输入其他模块提供的功能
箭头函数和 function 有什么区别
箭头函数根本就没有绑定自己的 this, 在箭头函数中调用 this 时, 仅仅是简单的沿着作用域链向上寻找, 找到最近的一个 this 拿来使用
来源: http://www.bubuko.com/infodetail-3202622.html