一、Symbol -- ES6新增的类型
(一)、定义
它是基础类型,不是类,不可实例化;
let s = Symbol('创建')
Symbol()函数每次调用会创建一个新的唯一值
console.log(Symbol() === Symbol())
// false
Symbol() 函数接受一个可选参数作为描述,这样使Symbol更具有语义性。
下面创建两个symbol分别为: firstName and lastName:
let firstName = Symbol('first name'),
lastName = Symbol('last name');
console.log(firstName); // Symbol(first name)
console.log(lastName); // Symbol(last name)
console.log(firstName.toString()); // Symbol(first name)
console.log(lastName.toString()); // Symbol(last name)
console.log(firstName.description); // first name
console.log(lastName.description); // last name
当console.log()打印symbol的时候会隐式调用symbol的toString()方法;里头的值是description;
(二)、共享 Symbol
要创建一个共享的symbol,要使用Symbol.for()函数,而不是Symbol()。
Symbol.for() 也接受一个可选参数作为描述
let a = Symbol.for('aaa');
let b = Symbol.for('aaa');
console.log(b === a); // true
console.log(Symbol.keyFor(a));
console.log(Symbol.keyFor(b));
Symbol.for()不会像Symbol每次重新创建一个symbol,它会首先在全局中查找是否有已经创建的'aaa'的symbol,如果有就会返回已经创建的symbol,如果没有就会创建一个新的symbolSymbol.for()创建的变量通过Symbol.keyFor()方法获取值;Symbol()创建的变量通过Symbol.decription方法获取值;
二、作用
(一)、使用Symbol作唯一值
let statuses = {
OPEN: Symbol('已下单'),
IN_PROGRESS: Symbol('配送中'),
COMPLETED: Symbol('订单完成'),
CANCELED: Symbol('订单取消')
};
(二)、 使用symbol作为对象属性
使用Symbol作为属性名称
let user1 = {
name: '张三'
key: Symbol()
}
let user2 = {
name: '张三'
key: Symbol()
}
let userJson = {
[user1.key]: {grade: 23},
[user2.key]: {grade: 26}
}
console.log(userJson);
一般定义一样的值的对象,会指向同一个地址,所以下面的user2会覆盖user1; 但是可以通过Symbol可以指向两个地址;
三、symbol内置函数
(一)、Symbol.hasInstance
Symbol.hasInstance 是一个改变instanceof操作符默认行为的symbol
instanceof的使用:
obj instanceof type;
// 判断引用数据的类型,通过原型链一直往上找,找到的话返回true,否则为false
那么js就会执行Symbol.hasInstance方法;它会调用type的Symbol.hasInstance静态方法,将obj作为参数;
class Stack {
}
console.log([] instanceof Stack);
// false
[] 数组不是Stack类所创建的实例,所以返回false。
假设要使[] 数组是Stack类所创建的实例,返回true,我们可以重写Symbol.hasInstance的方法
上面例子中,Stack本应该不是数组,但是可以通过重写Symbol.hasInstance修改instanceof的返回值;
(二)、 Symbol.iterator
Symbol.iterator 指定函数是否会返回对象的迭代器。具有 Symbol.iterator 属性的对象称为可迭代对象。在ES6中,Array、Set、Map和string都是可迭代对象。
ES6提供了for...of循环,它可以用在可迭代对象上。
var numbers = [1, 2, 3];
for (let num of numbers) {
console.log(num);
}
// 1
// 2
// 3
在背后,JavaScript引擎首先调用numbers数组的 Symbol.iterator 方法来获取迭代器对象,然后它调用 iterator.next() 方法并将迭代器对象的value属性复制到num变量中,3次迭代后,对象的done属性为true,循环退出。
可以通过Symbol.iterator来获取数组的迭代器对象;
var iterator = numbers[Symbol.iterator]();
console.log(iterator.next()); // Object {value: 1, done: false}
console.log(iterator.next()); // Object {value: 2, done: false}
console.log(iterator.next()); // Object {value: 3, done: false}
console.log(iterator.next()); // Object {value: undefined, done: true}
通过给自定义的类自定义Symbol.iterator 让其可以被循环取值
class List {
constructor() {
this.elements = [];
}
add(element) {
this.elements.push(element);
return this;
}
*[Symbol.iterator]() {
for (let element of this.elements) {
yield element;
}
}
}
let chars = new List();
chars.add('A')
.add('B')
.add('C');
// 使用Symbol.iterator实现了迭代
for (let c of chars) {
console.log(c);
}
(三)、 Symbol.isConcatSpreadable
数组中可以通过concat()方法合并多个数组;也可以使用concat()来传入单个元素,而非数组;
let odd = [1, 3],
even = [2, 4];
let all = odd.concat(even);
console.log(all); // [1, 3, 2, 4]
let extras = all.concat(5);
console.log(extras); // [1, 3, 2, 4, 5]
除了数组别的引用类型或自定义的对象都不支持concat()方法;
要在concat()时将list对象中的元素单独添加到数组中的,我们需要将Symbol.isConcatSpreadable属性添加到list对象中,像下面这样:
let list = {
0: 'JavaScript',
1: 'Symbol',
length: 2,
[Symbol.isConcatSpreadable]: true
};
let message = ['Learning'].concat(list);
console.log(message); // ["Learning", "JavaScript", "Symbol"]
如果将Symbol.isConcatSpreadable设置为true,concat()就会将list整个对象合并到数组中。
(四)、 Symbol.toPrimitive
对象的Symbol.toPrimitive属性。指向一个方法。该对象被转化为原始类型的值时,会调用这个办法,返回该对象对应的原始类型值。 Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一个有三种模式。
Number: 该场合需要转成数值String: 该场合需要转成字符串Default: 该场合可以转成数值,也可以转成字符串。
Symbol.toPrimitive在类型转换方面,优先级是最高的
const test = {
i: 10,
toString: function() {
console.log('toString');
return this.i;
},
valueOf: function() {
console.log('valueOf');
return this.i;
},
[Symbol.toPrimitive](hint) {
if(hint === 'number'){
console.log('Number场景');
return 123;
}
if(hint === 'string'){
console.log('String场景');
return 'str';
}
if(hint === 'default'){
console.log('Default 场景');
return 'default';
}
}
}
console.log(test); // { i:10, toString: f, valueOf: f, Symbol(Symbol.toPrimitive): f }
console.log(+test); // 123 Number场景
console.log(''+test); // default Default 场景
console.log(String(test)); // str String场景
console.log(Number(test)); // 123 Number场景
console.log(test == '10'); // false default场景
console.log(test === '10'); // false
上面代码中、+test中的加号命名为一元加号,+test本质就是转成数值的意思;
(五)、其他内置函数
Symbol.match(regex):一个在调用String.prototype.match()方法时调用的方法,用于比较字符串。Symbol.replace(regex, replacement):一个在调用String.prototype.replace()方法时调用的方法,用于替换字符串的子串。Symbol.search(regex):一个在调用String.prototype.search()方法时调用的方法,用于在字符串中定位子串。Symbol.species(regex):用于创建派生对象的构造函数。Symbol.split:一个在调用String.prototype.split()方法时调用的方法,用于分割字符串。Symbol.toStringTag:一个在调用String.prototype.toString()方法时使用的字符串,用于创建对象描述。Symbol.unscopables:一个定义了一些不可被with语句引用的对象属性名称的对象集合。
感谢您抽出宝贵的时间观看本文;本文是JavaScript 系列的第 1 篇,后续会持续更新ES, Dom, 队列等等内容,欢迎关注~