
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作为一种新的原始数据类型,表示独一无二的值,主要是为了防止属性名冲突。
ES6之后,JavaScript一共有其中数据类型: 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
该对象指定了使用with关键字时,哪些属性会被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将指向外层作用域的变量。











