Symbol的学习

Symbol的学习

概述

Symbol是一种新的原始数据类型,是es6的第七种数据类型,其余六种分别是:undefined,null,布尔值(Boolean),字符串(String),数值(Number),对象(Object)。
通过Symbol函数创建一个Symbol,对象的属性名有两种类型:字符串和Symbol.Symbol 的属性名是独一无二的,不会与任何属性名冲突。
声明Symbol变量

let s = Symbol()
typeof s//"symbol"

特点

  • 不能使用new创建,因为它是原始数据类型,而不是对象或者数组
  • 为了区分,可以接受一个字符串的参数
let s1 =Symbol('foo')
let s2= Symbol('boo')

s1//Symbol(foo)
s2//Symbol(boo)

s1.toString()//"Symbol(foo)"
s2.toString()//"Symbol(boo)"
  • 如果参数是对象的话,需要将参数转换成字符串
let obj={
  toString(){
     return "abc"
 }
}
let s =Symbol(obj)
s//Symbol(abc)
  • Symbol是独一无二的,因此无参和同参的函数返回都是不相等
//无参
let s1 = Symbol();
let s2 = Symbol();
s1 === s2;//false

//有参
let s3 =Symbol("foo");
let s4 =Symbol("foo");
s3 === s4;//false
  • 不能与其他类型进行运算
let sym =Symbol("My Symbol");
" my symbol is"+ sym;
//TypeError: Cannot convert a Symbol value to a string
'my symbol is $(sym)'
//TypeError: Cannot convert a Symbol value to a string
  • 可以直接转换成字符串和布尔值,但是不能转换成数值
let sym = Symbol("My Symbol")
String(sym)//"Symbol(My Symbol)"
sym.toString()//"Symbol(My Symbol)"

Boolean(sym)//true
!sym//false

Number(sym)//TypeError: Cannot convert a Symbol value to a number
sym+2 //TypeError: Cannot convert a Symbol value to a number

##作为属性名的Symbol

  • 作为对象的属性名,保证不会出现相同的标识符
let mySymbol = Symbol();
//第一种写法
let a ={};
a = {
  [mySymbol]:"hello"
}

//第二种
let a={
  [mySymbol]:"hello"
}

//第三种
let a ={};
Object.defineProperty(a,mySymbol,{value:'hello'})
//{ [Symbol()]: 'Hello!' }
a[mySymbol]//"hello"
  • 不能使用点运算符,因为点运算符后面跟着的是字符串,不能识别为Symbol值
const mySymbol = Symbol();
const a ={};

a.mySymbol="hello";
a[mySymbol]//undefined
a['mySymbol']//"hello"
  • 如果在对象内部,必须使用方括号,否则键名将会变成字符串,而不是Symbol值
let mySymbol = Symbol();
let obj ={
  [mySymbol](arg)
}

obj[mySymbol](123)
  • 定义一组互不相等的常量,常用于对象属性值和switch语句
const log ={}; 
log.levels={
   DEBUG:Symbol('debug'),
   INFO:Symbol('info').
   WARN:Symbol('warn')
}
//{ DEBUG: Symbol(debug), INFO: Symbol(info), WARN: Symbol(warn) }
console.log(log.levels.DEBUG,'debug message')
//Symbol(debug) 'debug message'

const RED =Symbol()
const GREEN = Symbol()

function getComplement(color){
   switch(color){
      case RED:
         return GREEN;
         break;
      case GREEN:
         return RED;
         break;
      default:throw new Error("undefined color");
  }
}

实例:消除魔术字符串

魔术字符串就是代码中重复多次的,与代码形成强耦合关系的某一具体字符串,良好的代码风格,就是要尽量避免魔术字符串的出现

function getArea(shape,options){
    switch(shape){
       case 'triangle':
           area = .5 *options.width*options.height;
           break;
    }
    return area;
}

getArea('trangle',{width:100,height:500})//魔术字符串为triangle
//250000

消除方法

  • 可以将triangle变成一个变量
  • 写成Symbol值
const shapeType ={
   triangle:'triangle'
   //triangle:Symbol
};

function getArea(shape,options){
   switch(shape){
        case shapeType.triangle:
           area = .5* options.width * options.height;
           break;
    }  
    return area;
}
getArea(shapeType.triangle,{width:100,height:100});
//5000

有利于代码的修改和维护

属性名的遍历

在Symbol作为属性名遍历的时候,不会for … in 或者for…of循环以及Object.keys(),Object.getOwnPropertyName(),JSON.stringify()等方法获取。
一般使用getOwnPropertySymbols()和Reflect.ownKeys()

  • Object.getOwnpropertySymbols()的参数是一个对象,返回是一个数组,成员是当前对象所有属性名的Symbol值
const obj = {};

const a= Symbol('a');
const b=Symbol('b');

obj[a] = 'hello';//"hello"
obj[b] ='world'; //"world"

let propertySymbol =Object.getPropertySymbol(obj);
[Symbol(a),Symbol(b)]
  • 与for …in和Object.getOwnAPropertyNames()比较
let obj ={};
let mySymbol =Symbol('foo');
Object.getProperty(obj,mySymbol,{value:'hello!'});
//{[Symbol(foo)]:'hello!'}
for( let i in obj){
  console.log(i)//无输出
}

Oject.getOwnPropertyNanmes(obj);//[]
Oject.getOwnPropertySymbols(obj);//[Symbol(foo)]
  • Reflect.ownKeys()获取所有包括常规和Symbol的属性名.返回一个数组.成员当前对象为所有属性名
let mySymbol=Symbol();
   let obj1 ={
   [mySymbol]:'num1',
   num1:1,
   flag:true
};

Reflect.ownKeys(obj1);//[ 'num1', 'flag', Symbol() ]

Symbol.for()和Symbol.keyFor()

  • Symbol.for接受一个字符串参数作为键名,在全局环境中进行查找,如果已经存在,直接返回值,如果不存在,则新建,但是Symbol()不管存在与否都会新建,
let sym1 = Symbol.for('foo');
let sym2 =Symbol.for('foo');
 sym1===sym2;//true;
let sym3=Symbol('foo');
sym3=sym1;//false
  • Symbol.keyFor返回一个已经登记的Symbol的key
let s1=Symbol.for('foo');
Symbol.keyFor(s1);//"foo"

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

内置的Symbol值

Symbol.hasInstance()

指向一个内部方法,当其他对象使用instanceOf时,判断是否为该对象的实例时调用

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

[1, 2, 3] instanceof new MyClass() // true

Symbol.isConcatSpreadable

对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开

class A1 extends Array {
  constructor(args) {
    super(args);
    this[Symbol.isConcatSpreadable] = true;
  }
}
class A2 extends Array {
  constructor(args) {
    super(args);
  }
  get [Symbol.isConcatSpreadable] () {
    return false;
  }
}
let a1 = new A1();
a1[0] = 3;
a1[1] = 4;
let a2 = new A2();
a2[0] = 5;
a2[1] = 6;
[1, 2].concat(a1).concat(a2)
// [1, 2, 3, 4, [5, 6]]

Symbol.species

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

class MyArray extends Array {
  static get [Symbol.species]() { return Array; }
}

const a = new MyArray();
const b = a.map(x => x);

b instanceof MyArray // false
b instanceof Array // true

Symbol.match

对象的Symbol.match属性,指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。

String.prototype.match(regexp)
// 等同于
regexp[Symbol.match](this)

class MyMatcher {
  [Symbol.match](string) {
    return 'hello world'.indexOf(string);
  }
}

'e'.match(new MyMatcher()) // 1

Symbol.repalce

对象的Symbol.replace属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。

const x = {};
x[Symbol.replace] = (...s) => console.log(s);

'Hello'.replace(x, 'World') // ["Hello", "World"]

Symbol.search

对象的Symbol.search属性,指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值。

String.prototype.search(regexp)
// 等同于
regexp[Symbol.search](this)

class MySearch {
  constructor(value) {
    this.value = value;
  }
  [Symbol.search](string) {
    return string.indexOf(this.value);
  }
}
'foobar'.search(new MySearch('foo')) // 

Symbol.split

对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。

class MySplitter {
  constructor(value) {
    this.value = value;
  }
  [Symbol.split](string) {
    let index = string.indexOf(this.value);
    if (index === -1) {
      return string;
    }
    return [
      string.substr(0, index),
      string.substr(index + this.value.length)
    ];
  }
}

'foobar'.split(new MySplitter('foo'))
// ['', 'bar']

'foobar'.split(new MySplitter('bar'))
// ['foo', '']

'foobar'.split(new MySplitter('baz'))
// 'foobar'

Symbol.iterator

对象的Symbol.iterator属性,指向该对象的默认遍历器方法

const myIterable = {};
myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

[...myIterable] // [1, 2, 3]

Symbol.toPrimitive

Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。

  • Number:该场合需要转成数值
  • String:该场合需要转成字符串
  • Default:该场合可以转成数值,也可以转成字符串
let obj = {
  [Symbol.toPrimitive](hint) {
    switch (hint) {
      case 'number':
        return 123;
      case 'string':
        return 'str';
      case 'default':
        return 'default';
      default:
        throw new Error();
     }
   }
};

2 * obj // 246
3 + obj // '3default'
obj == 'default' // true
String(obj) // 'str'

Symbol.toStringTag

对象的Symbol.toStringTag属性,指向一个方法。

({[Symbol.toStringTag]: 'Foo'}.toString())
// "[object Foo]"

// 例二
class Collection {
  get [Symbol.toStringTag]() {
    return 'xxx';
  }
}
let x = new Collection();
Object.prototype.toString.call(x) // "[object xxx]"

Symbol.unscopables

对象的Symbol.unscopables属性,指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除。

Array.prototype[Symbol.unscopables]
// {
//   copyWithin: true,
//   entries: true,
//   fill: true,
//   find: true,
//   findIndex: true,
//   includes: true,
//   keys: true
// }

Object.keys(Array.prototype[Symbol.unscopables])
// ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys']

参考文献

Symbol 《ECMAscript入门》作者:阮一峰

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值