美文网首页
es学习-Symbol-推荐看

es学习-Symbol-推荐看

作者: 安石0 | 来源:发表于2019-04-11 11:32 被阅读0次

symbol的来头

symbol是第六种基础类型,厌倦了只能用String做key,现在可以用Symbol。

a = Symbol('a')
p  = {[a]: 'a'}
p[a] // "a"

Symbol共享体系

Symbol.for('xxx')首先在全局Symbol注册表中查找是否存在xxx,有则返回现有的,没有新创建并返回。Symbol.keyFor方法在Symbol全局注册表中检测与Symbol有关的键。

uid = Symbol.for('uid')
uid2 = Symbol.for('uid')
uid3 = Symbol('uid')
uid === uid2 // true
uid === uid3 // false
Symbol.keyFor(uid) // "uid"
Symbol.keyFor(uid2) // "uid"
Symbol.keyFor(uid3)// undefined

内置Symbol

除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。

Symbol.hasInstance

当我们使用obj instanceof Array 等价于执行 Array[Symbol.hasInstance](obj),我们可以改变instanceof的运行方式。

对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。比如,foo instanceof Foo在语言内部,实际调用的是Foo[Symbol.hasInstance](foo)。

class MyClass {
  [Symbol.hasInstance](foo) {
    return foo instanceof Array;
  }
}

[1, 2, 3] instanceof new MyClass() // true
a = new MyClass()
// 原来的instanceof也有用
a instanceof MyClass // true

Symbol.isConcatSpreadable

对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。数组的默认行为是可以展开,Symbol.isConcatSpreadable默认等于undefined。该属性等于true时,也有展开的效果。类似数组的对象正好相反,默认不展开。

let arr1 = ['c', 'd'];
['a', 'b'].concat(arr1, 'e') //(5) ["a", "b", "c", "d", "e"]
arr1[Symbol.isConcatSpreadable] // undefined
arr1[Symbol.isConcatSpreadable] = false
['a', 'b'].concat(arr1, 'e') // (4) ["a", "b", Array(2), "e"]
// 设为undefined也可以
arr1[Symbol.isConcatSpreadable] = undefined
['a', 'b'].concat(arr1, 'e') // (5) ["a", "b", "c", "d", "e"]
//对象
let obj = {length: 2, 0: 'c', 1: 'd'};
['a', 'b'].concat(obj, 'e') // (4) ["a", "b", {…}, "e"]
obj[Symbol.isConcatSpreadable] = true
['a', 'b'].concat(obj, 'e') // (5) ["a", "b", "c", "d", "e"]
class MyArray extends Array {
}

const a = new MyArray(1, 2, 3);
const b = a.map(x => x);
const c = a.filter(x => x > 1);

b instanceof MyArray // true
c instanceof MyArray // true
true
b instanceof MyArray // true
true
c instanceof MyArray // true
true
c instanceof Array // true
true

Symbol.species

对象的Symbol.species属性,指向一个构造函数。创建衍生对象时,会使用该属性。

class MyArray extends Array {
}
const a = new MyArray(1, 2, 3);
const b = a.map(x => x);
const c = a.filter(x => x > 1);
console.log(b instanceof MyArray) // true
console.log(c instanceof MyArray) // true
undefined
class MyArray1 extends Array {
    static get [Symbol.species]() { return Array; }
}
const a1 = new MyArray1(1, 2, 3);
const b1 = a1.map(x => x);
const c1 = a1.filter(x => x > 1);
console.log(b1 instanceof MyArray) // false
console.log(c1 instanceof MyArray) // false

Symbol.toPrimitive

当执行特定操作时(如:==)经常会将对象转换成原始值然后再比较,es6之前是由内部操作决定的,es6则可通过Symbol.toPrimitive方法可以更改那个暴露出来的值。每当执行原始值转换是,总会调用Symbol.toPrimitive方法并传入一个参数:类型提示hint(js 引擎注入)。有三种选择'number', 'string', 'default'(==,+ , Date()构造函数传参)。
1 number:
调用valueof 如果为原始值则返回 --> 调用toString(),如果为原始值则返回
--> 如果再无可选值则报错
2 string
调用toString(),如果为原始值则返回 --> 调用valueof 如果为原始值则返回
--> 如果再无可选值则报错
在大多数情况下,标准对象按默认按数字模式处理。
我们可以自定义:

function Temperature (d) {
  this.degrees = d
}
Temperature.prototype[Symbol.toPrimitive] = function (hint) {
    switch(hint) {
       case 'string':
          return this.degrees + '\u00b0'
       case 'number':
          return this.degrees
       case 'default':
         return this.degrees + '度'
     }
}
var f = new Temperature(62)
f+'!' // "62度!"
f /2 // 31
String(f) // "62°"

一般的做法是将default设为和string或number保持一致。

Symbol.toStringTag

es5我们一般根据

function type (val, xxx) {
 return Object.prototype.toString.call(val) === `[object ${xxx}]`
}

来判断数据的类型,es6中定义了toString返回的值储存在对象的Symbol.toStringTag属性中,同样的你也可以改变它。

function type (val) {
  return Object.prototype.toString.call(val)
}
let son = {}
type(son) // "[object Object]"
son[Symbole.toStringTag] = 'your father is zlj'
type(son) // "[object your father is zlj]"

请看下面这段代码:

p = {}
p[Symbol.toStringTag] = 'zlj'
function type (val) {
  return Object.prototype.toString.call(val)
}
type(p) // "[object zlj]"
p.toString() // "[object zlj]"
p.toString = function () {
  return 'haha'
}
p.toString() // "haha"
type(p) // "[object zlj]"

toString的行为默认和Symbol.toStringTag一致,但是你可以改变toString方法,Symbol.toStringTag表现不变。

Symbol.unscopables(非作用域的)

在非严格模式下可以使用with语句:

var values = [1,2,3]
colors = ['red', 'green', 'blue']
color = 'black'
with(colors){
  push(color)
  push(...values) 
}
colors //  (7) ["red", "green", "blue", "black", 1, 2, 3]

再看一个例子

var values = [1,2,3]
colors = ['red', 'green', 'blue']
color = 'black'
length = 100
with(colors){
  push(color)
  push(...values)
  push(length) 
}
colors // (8) ["red", "green", "blue", "black", 1, 2, 3, 7]

举例:我想要的是length用外面的而不是里面的(该数组的),Symbol.unscopables可以帮助你,Symbol.unscopables: 指用于指定对象值,其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称。

Array.prototype[Symbol.unscopables].length = true
var values = [1,2,3]
colors = ['red', 'green', 'blue']
color = 'black'
length = 100
with(colors){
  push(color)
  push(...values)
  push(length) 
}
colors // (8) ["red", "green", "blue", "black", 1, 2, 3, 100]

总结

以上这些行为有危险性,毕竟改变了语言本身的执行方式,除非你知道你自己在干嘛!

相关文章

网友评论

      本文标题:es学习-Symbol-推荐看

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