ES6系列目录
1 let 和 const命令
2 变量的解构赋值
3 字符串的拓展
4 正则的拓展
5 数值的拓展
6 函数的拓展
7 数组的拓展
8 对象的拓展
9 Symbol
10 Set和Map数据结构
11 Proxy
12 Promise对象
13 Iterator和 for...of循环
14 Generator函数和应用
15 Class语法和继承
16 Module语法和加载实现
所有整理的文章都收录到我《Cute-JavaScript》系列文章中,访问地址:http://js.pingan8787.com
8 对象的拓展
8.1 属性的简洁表示
let a = 'a1';
let b = { a }; // b => { a : 'a1' }
// 等同于
let b = { a : a };
function f(a, b){
return {a, b};
}
// 等同于
function f (a, b){
return {a:a ,b:b};
}
let a = {
fun () {
return 'leo';
}
}
// 等同于
let a = {
fun : function(){
return 'leo';
}
}
8.2 属性名表达式
JavaScript
提供2种方法定义对象的属性。
// 方法1 标识符作为属性名
a.f = true;
// 方法2 字符串作为属性名
a['f' + 'un'] = true;
延伸出来的还有:
let a = 'hi leo';
let b = {
[a]: true,
['a'+'bc']: 123,
['my' + 'fun'] (){
return 'hi';
}
};
// b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi'
// b[a] => true ; b['abc'] => 123 ; b['myfun'] => ƒ ['my' + 'fun'] (){ return 'hi'; }
注意:
// 报错
let a1 = 'aa';
let a2 = 'bb';
let b1 = {[a1]};
// 正确
let a1 = 'aa';
let b1 = { [a1] : 'bb'};
8.3 Object.is()
Object.is()
用于比较两个值是否严格相等,在ES5时候只要使用相等运算符( ==
)和严格相等运算符( ===
)就可以做比较,但是它们都有缺点,前者会自动转换数据类型,后者的 NaN
不等于自身,以及 +0
等于 -0
。
Object.is('a','a'); // true
Object.is({}, {}); // false
// ES5
+0 === -0 ; // true
NaN === NaN; // false
// ES6
Object.is(+0,-0); // false
Object.is(NaN,NaN); // true
8.4 Object.assign()
Object.assign()
方法用于对象的合并,将原对象的所有可枚举属性复制到目标对象。基础用法:目标对象,后面参数都是源对象。
let a = {a:1};
let b = {b:2};
Object.assign(a,b); // a=> {a:1,b:2}
注意:
若目标对象与源对象有同名属性,则后面属性会覆盖前面属性。
let a = {a:1, b:2};
let b = {b:3, c:4};
Object.assign(a, b); // a => {a:1, b:3, c:4}
若只有一个参数,则返回该参数。
let a = {a:1};
Object.assign(a) === a; // true
若参数不是对象,则先转成对象后返回。
typeof Object.assign(2); // 'object'
由于
undefined
或NaN
无法转成对象,所以做为参数会报错。
Object.assign(undefined) // 报错
Object.assign(NaN); // 报错
Object.assign()
实现的是浅拷贝。
Object.assign()
拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。
let a = {a: {b:1}};
let b = Object.assign({},a);
a.a.b = 2;
console.log(b.a.b); // 2
将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。
Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3]
9 Symbol
9.1 介绍
ES6引入 Symbol
作为一种新的原始数据类型,表示独一无二的值,主要是为了防止属性名冲突。Symbol
、 undefined
、 null
、 Boolean
、 String
、 Number
、 Object
。
let a = Symbol();
typeof a; // "symbol"
注意:
Symbol
函数不能用new
,会报错。由于Symbol
是一个原始类型,不是对象,所以不能添加属性,它是类似于字符串的数据类型。Symbol
都是不相等的,即使参数相同。
// 没有参数
let a1 = Symbol();
let a2 = Symbol();
a1 === a2; // false
// 有参数
let a1 = Symbol('abc');
let a2 = Symbol('abc');
a1 === a2; // false
Symbol
不能与其他类型的值计算,会报错。
let a = Symbol('hello');
a + " world!"; // 报错
`${a} world!`; // 报错
Symbol可以显式转换为字符串:
let a1 = Symbol('hello');
String(a1); // "Symbol(hello)"
a1.toString(); // "Symbol(hello)"
Symbol可以转换为布尔值,但不能转为数值:
let a1 = Symbol();
Boolean(a1);
!a1; // false
Number(a1); // TypeError
a1 + 1 ; // TypeError
9.2 Symbol作为属性名
好处:防止同名属性,还有防止键被改写或覆盖。
let a1 = Symbol();
// 写法1
let b = {};
b[a1] = 'hello';
// 写法2
let b = {
[a1] : 'hello'
}
// 写法3
let b = {};
Object.defineProperty(b, a1, {value : 'hello' });
// 3种写法 结果相同
b[a1]; // 'hello'
需要注意: Symbol作为对象属性名时,不能用点运算符,并且必须放在方括号内。
let a = Symbol();
let b = {};
// 不能用点运算
b.a = 'hello';
b[a] ; // undefined
b['a'] ; // 'hello'
// 必须放在方括号内
let c = {
[a] : function (text){
console.log(text);
}
}
c[a]('leo'); // 'leo'
// 上面等价于 更简洁
let c = {
[a](text){
console.log(text);
}
}
常常还用于创建一组常量,保证所有值不相等:
let a = {};
a.a1 = {
AAA: Symbol('aaa'),
BBB: Symbol('bbb'),
CCC: Symbol('ccc')
}
9.3 应用:消除魔术字符串
魔术字符串:指代码中多次出现,强耦合的字符串或数值,应该避免,而使用含义清晰的变量代替。
function f(a){
if(a == 'leo') {
console.log('hello');
}
}
f('leo'); // 'leo' 为魔术字符串
常使用变量,消除魔术字符串:
let obj = {
name: 'leo'
};
function f (a){
if(a == obj.name){
console.log('hello');
}
}
f(obj.name); // 'leo'
使用Symbol消除强耦合,使得不需关系具体的值:
let obj = {
name: Symbol()
};
function f (a){
if(a == obj.name){
console.log('hello');
}
}
f(obj.name);
9.4 属性名遍历
Symbol作为属性名遍历,不出现在 for...in
、 for...of
循环,也不被 Object.keys()
、 Object.getOwnPropertyNames()
、 JSON.stringify()
返回。
let a = Symbol('aa'),b= Symbol('bb');
let obj = {
[a]:'11', [b]:'22'
}
for(let k of Object.values(obj)){console.log(k)}
// 无输出
let obj = {};
let aa = Symbol('leo');
Object.defineProperty(obj, aa, {value: 'hi'});
for(let k in obj){
console.log(k); // 无输出
}
Object.getOwnPropertyNames(obj); // []
Object.getOwnPropertySymbols(obj); // [Symbol(leo)]
Object.getOwnPropertySymbols
方法返回一个数组,包含当前对象所有用做属性名的Symbol值。
let a = {};
let a1 = Symbol('a');
let a2 = Symbol('b');
a[a1] = 'hi';
a[a2] = 'oi';
let obj = Object.getOwnPropertySymbols(a);
obj; // [Symbol(a), Symbol(b)]
另外可以使用 Reflect.ownKeys
方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
let a = {
[Symbol('leo')]: 1,
aa : 2,
bb : 3,
}
Reflect.ownKeys(a); // ['aa', 'bb',Symbol('leo')]
由于Symbol值作为名称的属性不被常规方法遍历获取,因此常用于定义对象的一些非私有,且内部使用的方法。
9.5 Symbol.for()、Symbol.keyFor()
Symbol.for()用于重复使用一个Symbol值,接收一个字符串作为参数,若存在用此参数作为名称的Symbol值,返回这个Symbol,否则新建并返回以这个参数为名称的Symbol值。
let a = Symbol.for('aaa');
let b = Symbol.for('aaa');
a === b; // true
Symbol()
和 Symbol.for()
区别:
Symbol.for('aa') === Symbol.for('aa'); // true
Symbol('aa') === Symbol('aa'); // false
Symbol.keyFor()用于返回一个已使用的Symbol类型的key:
let a = Symbol.for('aa');
Symbol.keyFor(a); // 'aa'
let b = Symbol('aa');
Symbol.keyFor(b); // undefined
9.6 内置的Symbol值
ES6提供11个内置的Symbol值,指向语言内部使用的方法:
1.Symbol.hasInstance
instanceof
运算符,判断是否为该对象的实例时,会调用这个方法。比如,fooinstanceofFoo
在语言内部,实际调用的是Foo[Symbol.hasInstance](foo)
。
class P {
[Symbol.hasInstance](a){
return a instanceof Array;
}
}
[1, 2, 3] instanceof new P(); // true
P是一个类,new P()会返回一个实例,该实例的 Symbol.hasInstance
方法,会在进行 instanceof
运算时自动调用,判断左侧的运算子是否为 Array
的实例。
2.Symbol.isConcatSpreadable
Array.prototype.concat()
时,是否可以展开。
let a = ['aa','bb'];
['cc','dd'].concat(a, 'ee');
// ['cc', 'dd', 'aa', 'bb', 'ee']
a[Symbol.isConcatSpreadable]; // undefined
let b = ['aa','bb'];
b[Symbol.isConcatSpreadable] = false;
['cc','dd'].concat(b, 'ee');
// ['cc', 'dd',[ 'aa', 'bb'], 'ee']
3.Symbol.species
get
取值器。
class P extends Array {
static get [Symbol.species](){
return this;
}
}
解决下面问题:
// 问题: b应该是 Array 的实例,实际上是 P 的实例
class P extends Array{}
let a = new P(1,2,3);
let b = a.map(x => x);
b instanceof Array; // true
b instanceof P; // true
// 解决: 通过使用 Symbol.species
class P extends Array {
static get [Symbol.species]() { return Array; }
}
let a = new P();
let b = a.map(x => x);
b instanceof P; // false
b instanceof Array; // true
4.Symbol.match
str.match(myObject)
,传入的属性存在时会调用,并返回该方法的返回值。
class P {
[Symbol.match](string){
return 'hello world'.indexOf(string);
}
}
'h'.match(new P()); // 0
5.Symbol.replace 当该对象被
String.prototype.replace
方法调用时,会返回该方法的返回值。
let a = {};
a[Symbol.replace] = (...s) => console.log(s);
'Hello'.replace(a , 'World') // ["Hello", "World"]
6.Symbol.hasInstance
String.prototype.search
方法调用时,会返回该方法的返回值。
class P {
constructor(val) {
this.val = val;
}
[Symbol.search](s){
return s.indexOf(this.val);
}
}
'hileo'.search(new P('leo')); // 2
7.Symbol.split
String.prototype.split
方法调用时,会返回该方法的返回值。
// 重新定义了字符串对象的split方法的行为
class P {
constructor(val) {
this.val = val;
}
[Symbol.split](s) {
let i = s.indexOf(this.val);
if(i == -1) return s;
return [
s.substr(0, i),
s.substr(i + this.val.length)
]
}
}
'helloworld'.split(new P('hello')); // ["hello", ""]
'helloworld'.split(new P('world')); // ["", "world"]
'helloworld'.split(new P('leo')); // "helloworld"
8.Symbol.iterator
for...of
循环时,会调用Symbol.iterator
方法,返回该对象的默认遍历器。
class P {
*[Symbol.interator]() {
let i = 0;
while(this[i] !== undefined ) {
yield this[i];
++i;
}
}
}
let a = new P();
a[0] = 1;
a[1] = 2;
for (let k of a){
console.log(k);
}
9.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'
10.Symbol.toStringTag
Object.prototype.toString
方法时,如果这个属性存在,它的返回值会出现在toString
方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[objectObject
]或[objectArray]
中object
后面的那个字符串。
// 例一
({[Symbol.toStringTag]: 'Foo'}.toString())
// "[object Foo]"
// 例二
class Collection {
get [Symbol.toStringTag]() {
return 'xxx';
}
}
let x = new Collection();
Object.prototype.toString.call(x) // "[object xxx]"
11.Symbol.unscopables
// 没有 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
将指向外层作用域的变量。