前方的话语

  ES5遇寓5种原始类型:字符串、数字、布尔值、null和undefined。ES6引入了第6栽原始类型——Symbol

  ES5的对象属性名都是字符串,很容易招属性名冲突。比如,使用了一个人家提供的对象,想啊这个目标上加新的不二法门,新方式的讳就是来或和现有措施发生冲突。如果产生同种体制,保证每个属性之讳都是举世无双之,这样尽管从根本上防止了属于性名冲突。这就是是ES6引入Symbol的原因,本文将详细介绍ES6着之Symbol类型

 

创建

  Symbol
值通过Symbol函数生成。这就是说,对象的性质名好起些许种档次:一栽是字符串,另一样栽是Symbol类型。凡是属于性名属于
Symbol 类型,就还是独一无二的,可以管非会见以及其余属性名产生冲突

let firstName = Symbol();
let person = {};
person[firstName] = "huochai";
console.log(person[firstName]); // "huochai"

  [注意]Symbol函数前无克应用new命,否则会报错。因为变化的
Symbol 是一个原始类型的价,不是目标

//Uncaught TypeError: Symbol is not a constructor
let firstName = new Symbol();

  Symbol函数接受一个可选参数,可以填补加同段子文本来描述即将创建的Symbol,这段描述不可用于属性访问,但是建议于每次创建Symbol时犹增长这样平等段子描述,以便让阅读代码和调试Symbol程序

let firstName = Symbol("first name");
let person = {};
person[firstName] = "huochai";
console.log("first name" in person); // false
console.log(person[firstName]); // "huochai"
console.log(firstName); // "Symbol(first name)"

  Symbol的叙说为贮存在其间[[Description]]特性被,只有当调用Symbol的toString()方法时才足以读取这个特性。在履行console.log()时隐式调用了firstName的toString()方法,所以它的叙述会于打印及日志中,但无克直接当代码里看[[Description]]

【类型检测】

  Symbol是原始值,ES6扩大了typeof操作符,返回”symbol”。所以可以为此typeof来检测变量是否也symbol类型

let symbol = Symbol("test symbol");
console.log(typeof symbol); // "symbol"

 

使用

  由于各个一个Symbol值都是休等于的,这表示Symbol值可以当作标识符,用于对象的属于性名,就能确保非会见现出同名的性能。这对于一个目标由多个模块组成的状况颇有因此,能防备有一个键被无小心改写或埋

  所有以可算属性名的地方,都好运用Symbol

let firstName = Symbol("first name");
// 使用一个需计算字面量属性
let person = {
    [firstName]: "huochai"
};
// 让该属性变为只读
Object.defineProperty(person, firstName, { writable: false });
let lastName = Symbol("last name");
Object.defineProperties(person, {
    [lastName]: {
        value: "match",
        writable: false
    }
});
console.log(person[firstName]); // "huochai"
console.log(person[lastName]); // "match"

  于此示例中,首先通过可计算对象字面量属性语法为person对象创建了单Symbol属性firstName。后面一行代码用之特性设置为只读。随后,通过Object.defineProperties()方法创建一个仅读的Symbol属性lastName,此处又用了目标字面量属性,但可是当做object.defineProperties()方法的老二个参数使用  

  [注意]Symbol 值作为目标属性名时,不可知用点运算符

var mySymbol = Symbol();
var a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"

  由地方结果看出,a.mySymbol和a[‘mySymbol’]里之mySymbol是字符串类型的属于性名,a[mySymbol]里之mySymbol才是Symbol类型的属于性名。虽然还让mySymbol,但价值未一致

  尽管当具备以可计算属性名的地方,都得采用Symbol来顶替,但是为了当不同代码有里行地共享这些Symbol,需要树立一个系

 

共享系统

  有时想于不同的代码中共享同一个Symbol,例如,在采用中起半点栽不同的对象类型,但是要她以与一个Symbol属性来代表一个奇异的标识符。一般而言,在雅怪之代码库中或者越文件追踪Symbol非常不便而容易错,出于这些由,ES6资了一个足随时看的大局Symbol注册表

【Symbol.for()】

  如果想创造一个可共享的Symbol,要下Symbol.for()方法。它只是领一个参数,也不怕是即将创建的Symbol的字符串标识符,这个参数同样也叫当Symbol的讲述

let uid = Symbol.for("uid");
let object = {};
object[uid] = "12345";
console.log(object[uid]); // "12345"
console.log(uid); // "Symbol(uid)"

  Symbol.for()方法首先以大局Symbol注册表中搜索键为”uid”的Symbol是否存在。如果是,直接回到就有些Symbol,否则,创建一个新的Symbol,并运用这键在Symbol全局注册表中注册,随即赶回新创造的Symbol

  后续要再扩散同样的键调用Symbol.for()会回去相同之Symbol

let uid = Symbol.for("uid");
let object = {
    [uid]: "12345"
};
console.log(object[uid]); // "12345"
console.log(uid); // "Symbol(uid)"
let uid2 = Symbol.for("uid");
console.log(uid === uid2); // true
console.log(object[uid2]); // "12345"
console.log(uid2); // "Symbol(uid)

  在是示例中,uid和uid2包含相同的Symbol并且可换使用。第一次调动用Symbol.for()方法创建是Symbol,第二浅调用可以一直由Symbol的大局注册表中觅到此Symbol

【Symbol.keyFor()】

  还有一个暨Symbol共享有关的表征:可以采取Symbol.keyFor()方法在Symbol全局注册表中寻找和Symbol有关的键

let uid = Symbol.for("uid");
console.log(Symbol.keyFor(uid)); // "uid"
let uid2 = Symbol.for("uid");
console.log(Symbol.keyFor(uid2)); // "uid"
let uid3 = Symbol("uid");
console.log(Symbol.keyFor(uid3)); // undefined

  uid和uid2都回去了”uid”这个键,而在Symbol全局注册表中无存uid3此Symbol,也就是休设有和的有关的键,所以最终返回undefined

  [注意]Symbol.for否Symbol值登记之名字,是全局环境的,可以当不同之
iframe 或 service worker 中落到和一个价值

let iframe = document.createElement('iframe');
iframe.src = String(window.location);
document.body.appendChild(iframe);

console.log(iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo'));// true

  上面代码中,iframe 窗口生成的 Symbol 值,可以当主页面得到

  Symbol全局注册表是一个看似全局作用域的共享环境,也就是说不克借而即条件被有怎样键。当以第三方组件时,尽量采取Symbol键的命名空间为减小命名冲突。例如,jQuery的代码可以呢所起键添加”jquery”前缀,就比如”jquery.element”或另类之键

 

类型转换

  类型转换是JS中之一个重中之重语言特征,然而其他种类没有和Symbol逻辑等价的值,因而Symbol使用起来不是殊灵敏

  使用console.log()方法来输出Symbol的情,它见面调用Symbol的String()方法并出口有用之音。也可以像这样直接调用string()方法来博取同样之情

let uid = Symbol.for("uid"),
    desc = String(uid);
console.log(desc); // "Symbol(uid)"

  String()函数调用了uid.toString()方法,返回字符串类型的Symbol描述里之情节。但是,如果尝试以Symbol与一个字符串拼接,会招致程序抛来错误

let uid = Symbol.for("uid"),
desc = uid + ""; // 引发错误!

  将uid与空字符串拼接,首先要拿uid强制转换为一个字符串,而Symbol不可以于更换为字符串,故程序直接丢掉来左

  同样,也未能够将Symbol强制转换为数字型。将Symbol与各级一个数学运算符混合使用还见面招程序抛来荒唐

let uid = Symbol.for("uid"),
sum = uid / 1; // 引发错误!

  尝试将Symbol除1,程序直接丢掉来左。而且无论用啊一个数学操作符,都爱莫能助正常运作

  [注意]布尔值除外,因为Symbol与JS中的非空值类似,其等价布尔值为true

let uid = Symbol.for("uid");
console.log(uid);//'Symbol(uid)'
console.log(!uid);//false
console.log(Boolean(uid));//true

 

属性检索

  Symbol作为属性名,该属性不会见产出于for…in、for…of循环中,也无见面给Object.getOwnPropertyNames()、Object.keys()、JSON.stringify()返回。于是,在ES6备受补充加了一个Object.getOwnpropertySymbols()方法来查找对象吃的Symbol属性

  Object.getOwnPropertySymbols()方法的返回值是一个涵盖有Symbol自发生性能的数组

let uid = Symbol.for("uid");
let object = {
    [uid]: "12345"
};
let symbols = Object.getOwnPropertySymbols(object);
console.log(symbols.length); // 1
console.log(symbols[0]); // "Symbol(uid)"
console.log(object[symbols[0]]); // "12345"

  以及时段代码中,object对象来一个名叫也uid的Symbol属性,object.getOwnPropertySymbols()方法返回了富含这个特性之数组

  另一个新的API——Reflect.ownKeys()方法好回去所有类型的键名,包括健康键名和
Symbol 键名

let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
};
console.log(Reflect.ownKeys(obj));//  ["enum", "nonEnum", Symbol(my_key)]

  由于因 Symbol
值作为名称的性,不会见受正常方法遍历得到。可以使用这特性,为目标定义有非私有的、但还要愿意只用于中的方法

var size = Symbol('size');

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

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

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

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

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

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

  上面代码中,对象x的size属性是一个Symbol值,所以Object.keys(x)、Object.getOwnPropertyNames(x)都没法儿得到其。这就是造成了平栽非私有的其中方法的职能

 

内置Symbol

  除了定义自己下的Symbol值以外,ES6尚提供了11单放的Symbol值,指向语言中采用的计

  1、Symbol.haslnstance

  一个当尽instanceof时调用的其中方法,用于检测对象的接轨信息

  2、Symbol.isConcatSpreadable

  一个布尔值,用于表示当传递一个汇作为Array.prototype.concat()方法的参数时,是否合宜以集内之要素规整到同一层级

  3、Symbol.iterator

  一个赶回迭代器的主意

  4、Symbol.match

  一个于调用String.prototype.match()方法时调用的方,用于比字符串

  5、Symbol.replace

  一个以调用String.prototype.replace()方法时调用的道,用于替换字符串的子串

  6、Symbol.search

  一个于调用String.prototype.search()方法时调用的艺术,用于在字符串中定位子串

  7、Symbol.species

  用于创造派生类的构造函数

  8、Symbol.split

  一个在调用String.prototype.split()方法时调用的法子,用于分割字符串

  9、Symbol.toprimitive

  一个返对象原始值的点子

  10、Symbol.ToStringTag

  一个每当调用Object.prototype.toString()方法时采用的字符串,用于创建对象描述

  11、Symbol.unscopables

  一个概念了片不可被with语句引用的靶子属性名称的靶子集合

【Symbol.haslnstance】

  每个函数都发一个Symbol.haslnstance方法,用于确定目标是否也函数的实例。该措施在Function.prototype中定义,所有函数都持续了instanceof属性的默认行为。为了保险Symbol.haslnstance不会被飞重写,该办法让定义为不可写、不可配置并且不可枚举

  Symbol.haslnstance方法才受一个参数,即如反省的价值。如果传入的价是函数的实例,则赶回true

obj instanceof Array;

  以上这行代码等价于下面这行

Array[Symbol.hasInstance](obj);

  本质上,ES6只是以instanceof操作符重新定义为这办法的简写语法。现在引入方法调用后,就可无限制改变instanceof的周转方式了

class MyClass {
  [Symbol.hasInstance](foo) {
    return foo instanceof Array;
  }
}
console.log([1, 2, 3] instanceof new MyClass()); // true

  假设定义一个随便实例的函数,就好拿Symbol.haslnstance的归来值硬编码为false

function MyObject() {
    // ...
}
Object.defineProperty(MyObject, Symbol.hasInstance, {
    value: function(v) {
        return false;
    }
});
let obj = new MyObject();
console.log(obj instanceof MyObject); // false

  只有经过Object.defineProperty()方法才能够改写一个不行写属性,上面的演示调用这个方式来改变写symbol.haslnstance,为夫定义一个接连回到false的初函数,即使obj实际上确实是Myobject类的实例,在调用过object.defineProperty()方法后,instanceof运算符返回的也是false

  当然,也得以依据随机条件,通过价值检查来规定于检测的是否也实例。例如,可以用1~100之数字定义为一个特数字型的实例,具体实现的代码如下

function SpecialNumber() {
  // empty
}
Object.defineProperty(SpecialNumber, Symbol.hasInstance, {
    value: function(v) {
        return (v instanceof Number) && (v >=1 && v <= 100);
    }
});
let two = new Number(2),
zero = new Number(0);
console.log(two instanceof SpecialNumber); // true
console.log(zero instanceof SpecialNumber); // false

  以马上段代码中定义了一个symbol.hasInstance方法,当值为Number的实例且其价值当1~100次常常回来true。所以就SpecialNumber函数和变量two之间没有直接关乎,变量two也叫肯定为specialNumber的实例

  如果要触发Symbol.haslnstance调用,instanceof的左操作数必须是一个目标,如果左操作数为无对象会促成instanceof总是回到false  

  当然,可以重写所有内建函数(如Date和Error函数)默认的symbol.haslnstance属性。但是如此做的名堂是代码的运作结果变得不足预期都有或使得人感到疑惑,所以未引进这样做,最好之做法是,只当必要情况下转写自己声明的函数的Symbol.haslnstance属性

【Symbol.isConcatSpreadable】

  对象的Symbol.isConcatSpreadable属性是布尔值,表示该对象下Array.prototype.concat()时,是否可拓展

let arr1 = ['c', 'd'];
['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
arr1[Symbol.isConcatSpreadable] // undefined

let arr2 = ['c', 'd'];
arr2[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']

  上面代码说明,数组的默认行为是可以开展。Symbol.isConcatSpreadable性能等于undefined或true,都出此功效

  类数组对象呢足以进行,但她的Symbol.isConcatSpreadable属性默认为false,必须手动打开

let obj = {length: 2, 0: 'c', 1: 'd'};
['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']

obj[Symbol.isConcatSpreadable] = true;
['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']

  对于一个像样来说,Symbol.isConcatSpreadable特性必须写成实例的习性

class A1 extends Array {
  constructor(args) {
    super(args);
    this[Symbol.isConcatSpreadable] = true;
  }
}
class A2 extends Array {
  constructor(args) {
    super(args);
    this[Symbol.isConcatSpreadable] = 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]]

  上面代码中,类A1举凡只是进行的,类A2凡不行展开的,所以下concat时有不同等的结果

【Symbol.species】

  对象的Symbol.species性,指向当前目标的构造函数。创造实例时,默认会调用这个点子,即祭此特性返回的函数当作构造函数,来创造新的实例对象

class MyArray extends Array {
  // 覆盖父类 Array 的构造函数
  static get [Symbol.species]() { return Array; }
}

  上面代码中,子类MyArray继往开来了父类Array。创建MyArray的实例对象时,本来会调用它和谐之构造函数,但是由定义了Symbol.species属性,所以会使用此特性返回的的函数,创建MyArray的实例

  这个事例吗作证,定义Symbol.species特性要动get读取器。默认的Symbol.species属性等同于下的写法

static get [Symbol.species]() {
  return this;
}

  下面是一个例证

class MyArray extends Array {
  static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);

mapped instanceof MyArray // false
mapped instanceof Array // true

  上面代码中,由于构造函数被替换成了Array。所以,mapped目标不是MyArray的实例,而是Array的实例

【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.replace】

  对象的Symbol.replace性,指向一个道,当该对象吃String.prototype.replace法调用时,会返回该办法的返回值

String.prototype.replace(searchValue, replaceValue)
// 等同于
searchValue[Symbol.replace](this, replaceValue)

  下面是一个例证

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

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

Symbol.replace方法会收到两单参数,第一单参数是replace措施在企图的靶子,上面例子是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')) // 0

【Symbol.split】

  对象的Symbol.split性能,指向一个艺术,当该目标吃String.prototype.split措施调用时,会返回该办法的返回值

String.prototype.split(separator, limit)
// 等同于
separator[Symbol.split](this, limit)

  下面是一个例子

class MySplitter {
  constructor(value) {
    this.value = value;
  }
  [Symbol.split](string) {
    var 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.split方法,重新定义了字符串对象的split办法的行

【Symbol.iterator】

  对象的Symbol.iterator属性,指为该对象的默认全方位历器方法

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

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

  对象进行for...of循环时,会调用Symbol.iterator术,返回该目标的默认方方面面历器

class Collection {
  *[Symbol.iterator]() {
    let i = 0;
    while(this[i] !== undefined) {
      yield this[i];
      ++i;
    }
  }
}

let myCollection = new Collection();
myCollection[0] = 1;
myCollection[1] = 2;

for(let value of myCollection) {
  console.log(value);
}
// 1
// 2

【Symbol.toPrimitive】

  对象的Symbol.toPrimitive性,指向一个方式。该目标被转为原始类型的价值经常,会调用这个法子,返回该对象对应的原始类型值

Symbol.toPrimitive为调用时,会接受一个字符串参数,表示手上运算的模式,一共来三种植模式

  1、Number:该场地用改成为数值

  2、String:该场地用变更成字符串

  3、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'

【String.toStringTag】

  对象的Symbol.toStringTag性能,指向一个主意。在该目标方面调用Object.prototype.toString方时,如果这个特性是,它的回到值会面世在toString道返回的字符串之中,表示对象的类。也就是说,这个特性可以据此来定制[object Object][object Array]object末尾的死字符串

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

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

  ES6初添内置对象的Symbol.toStringTag属于性值如下、

    JSON[Symbol.toStringTag]:'JSON'
    Math[Symbol.toStringTag]:'Math'
    Module[Symbol.toStringTag]:'Module'
    ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'
    DataView.prototype[Symbol.toStringTag]:'DataView'
    Map.prototype[Symbol.toStringTag]:'Map'
    Promise.prototype[Symbol.toStringTag]:'Promise'
    Set.prototype[Symbol.toStringTag]:'Set'
    %TypedArray%.prototype[Symbol.toStringTag]:'Uint8Array'
    WeakMap.prototype[Symbol.toStringTag]:'WeakMap'
    WeakSet.prototype[Symbol.toStringTag]:'WeakSet'
    %MapIteratorPrototype%[Symbol.toStringTag]:'Map Iterator'
    %SetIteratorPrototype%[Symbol.toStringTag]:'Set Iterator'
    %StringIteratorPrototype%[Symbol.toStringTag]:'String Iterator'
    Symbol.prototype[Symbol.toStringTag]:'Symbol'
    Generator.prototype[Symbol.toStringTag]:'Generator'
    GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'

【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']

  上面代码说明,数组有7独特性,会让with令排除

// 没有 unscopables 时
class MyClass {
  foo() { return 1; }
}

var foo = function () { return 2; };

with (MyClass.prototype) {
  foo(); // 1
}

// 有 unscopables 时
class MyClass {
  foo() { return 1; }
  get [Symbol.unscopables]() {
    return { foo: true };
  }
}

var foo = function () { return 2; };

with (MyClass.prototype) {
  foo(); // 2
}

  上面代码通过点名Symbol.unscopables属性,使得with语法块不会见于时下作用域寻找foo属性,即foo用对外层作用域的变量

 

相关文章

网站地图xml地图