前方的话

  ES5中涵盖5种原始类型:字符串数字布尔值nullundefined。ES陆引进了第伍种原始类型——Symbol

  ES5的目标属性名都以字符串,很轻便产生属性名争持。比如,使用了四个外人提供的对象,想为这一个目的加多新的措施,新章程的名字就有望与存活措施产生争辨。要是有一种机制,保险每一种属性的名字都以绝无仅有的,那样就从根本上防止了属性名争执。那正是ES6引进Symbol的原故,本文将详细介绍ES6中的Symbol类型

 

创建

  Symbol
值通过Symbol函数生成。那就是说,对象的性质名能够有两种类型:壹种是字符串,另一种是Symbol类型。凡是属性名属于
Symbol 类型,就都以惟1的,可以保障不会与此外属性名产生冲突

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函数接受3个可选参数,可以增多一段文本来讲述将在创立的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是原始值,ES六恢弘了typeof操作符,再次来到”symbol”。所以能够用typeof来检查测试变量是不是为symbol类型

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

 

使用

  由于每多少个Symbol值都以不对等的,这意味Symbol值能够视作标记符,用于对象的属性名,就能确认保证不会产出同名的个性。那对于一个目的由七个模块组合的状态拾1分有用,能防止某三个键被非常的大心改写或掩盖

  全数应用可总括属性名的地点,都足以行使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,供给建立2个系统

 

共享系统

  有时希望在分歧的代码中国共产党享同一个Symbol,例如,在选择中有二种不一样的靶子类型,不过希望它们利用同2个Symbol属性来表示3个非同小可的标记符。壹般来讲,在非常的大的代码库中或跨文件跟踪Symbol分外不便而且轻巧失误,出于这一个原因,ES陆提供了二个方可每壹天访问的全局Symbol注册表

【Symbol.for()】

  借使想创制1个可共享的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和uid二包蕴一样的Symbol并且能够调换使用。第三回调用Symbol.for()方法创设这些Symbol,第3遍调用能够直接从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全局注册表中不存在uid三以此Symbol,也正是不设有与之有关的键,所以最终再次来到undefined

  [注意]Symbol.for为Symbol值登记的名字,是大局环境的,能够在不一致的
iframe 或 service worker 中取到同1个值

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全局注册表是1个像样全局效用域的共享环境,相当于说不能够假诺近日环境中留存怎么样键。当使用第壹方组件时,尽量利用Symbol键的命名空间以收缩命名争论。例如,jQuery的代码可以为全体键加多”jquery”前缀,就像是”jquery.element”或别的类似的键

 

类型转变

  类型调换是JS中的三个重中之重语言特色,不过其他类别未有与Symbol逻辑等价的值,由此Symbol使用起来不是很灵敏

  使用console.log()方法来输出Symbol的内容,它会调用Symbol的String()方法并出口有用的消息。也足以像这么直接调用string()方法来获取1致的剧情

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强制调换为2个字符串,而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()再次来到。于是,在ES陆中加多了3个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
值作为名称的个性,不会被正常办法遍历获得。能够选取那些特点,为对象定义1些非私有的、但又愿意只用于内部的不二秘诀

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属性是3个Symbol值,所以Object.keys(x)、Object.getOwnPropertyNames(x)都无法儿获得它。这就导致了壹种非私有的在那之中方法的职能

 

内置Symbol

  除了定义本身使用的Symbol值以外,ES6还提供了拾个放置的Symbol值,指向语言内部使用的方法

  1、Symbol.haslnstance

  贰个在实行instanceof时调用的里边方法,用于检测对象的接续音信

  2、Symbol.isConcatSpreadable

  七个布尔值,用于表示当传递1个集结作为Array.prototype.concat()方法的参数时,是不是应该将集聚内的因素规整到同1层级

  3、Symbol.iterator

  一个回去迭代器的点子

  4、Symbol.match

  二个在调用String.prototype.match()方法时调用的章程,用于相比字符串

  5、Symbol.replace

  2个在调用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

  叁个定义了1部分不行被with语句引用的对象属性名称的对象集合

【Symbol.haslnstance】

  每一种函数都有一个Symbol.haslnstance方法,用于确定目的是或不是为函数的实例。该办法在Function.prototype中定义,全体函数都三番五次了instanceof属性的默许行为。为了确定保证Symbol.haslnstance不会被意外重写,该方法被定义为不可写、不可配置并且比比皆是

  Symbol.haslnstance方法只接受叁个参数,即要检查的值。若是传入的值是函数的实例,则赶回true

obj instanceof Array;

  以上那行代码等价于下边那行

Array[Symbol.hasInstance](obj);

  本质上,ES五只是将instanceof操作符重新定义为此情势的简写语法。未来引进方法调用后,就可以肆意改动instanceof的运维方式了

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

  要是定义2个无实例的函数,就足以将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()方法才能够改写1个不行写属性,下面的言传身教调用这一个办法来改写symbol.haslnstance,为其定义四个一而再回到false的新函数,就算obj实际上确实是Myobject类的实例,在调用过object.defineProperty()方法之后,instanceof运算符重临的也是false

  当然,也足以依照随机条件,通过值检查来鲜明被检查测试的是否为实例。例如,可以将一~拾0的数字定义为八个非正规数字类型的实例,具体贯彻的代码如下

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

  在这段代码中定义了3个symbol.hasInstance方法,当班值日为Number的实例且其值在壹~十0之间时回来true。所以正是SpecialNumber函数和变量two之间未有间接关联,变量two也被认可为specialNumber的实例

  假如要触发Symbol.haslnstance调用,instanceof的左操作数必须是1个目的,要是左操作数为非对象会导致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;
}

  上面是3个例证

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品质,指向3个函数。当实践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属性,指向1个格局,当该目的被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本性,指向3个办法,当该目的被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性格,指向3个办法,当该目的被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被调用时,会接受二个字符串参数,表示近日运算的格局,壹共有二种格局

  一、Number:本场合须求转成数值

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

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

  ES陆新扩展内置对象的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']

  上边代码表达,数组有几个属性,会被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地图