学习归纳下ES6中和对象相关的新方法
简洁表示法(简写对象属性、方法)
- 简写对象属性:对象的属性名与变量名同名后可从
属性名:变量名简写为属性名。 - 【常用但易忘】简写对象方法:对象的方法可以直接省略冒号和
function,从方法名:function(){函数体}简写为方法名(){函数体} - 例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17const getUserInfo = (id = 1) => {
//AJAX...
const name = "xiaoming";
const age = 10;
return {
name,//相当于`name:name`简写以后属性名name会自动去找到和它同名的变量,把它的值拿过来当值
age,//相当于`age:age`
say() {//相当于say:function(){},简写可以省略冒号与function
console.log(this.name + this.age);
}
};
};
const xiaoming = getUserInfo();
console.log(xiaoming);//{name: "xiaoming", age: 10, say: ƒ}
console.log(xiaoming.say());//xiaoming10 
属性名表达式
- 定义属性时可以用一个简单的表达式。
- 即:对象属性名可以包含 常量/变量
 
 - 语法:先定义一个常量/变量,然后用
[包含常量/变量的简单表达式]: 属性值定义属性- 补充:ES6用const定义常量,let定义变量
1
2
3
4
5
6
7const key="age"; // 先定义一个常量key
const xiaoming ={
name: "xiaoming",
[`now${key}`]: 14, // 注意:模板字符串是反引号
};
// xiaoming[`now${key}`] = 14 // 或者这样定义
console.log(xiaoming);//{name: "xiaoming", nowage: 14} 
 - 补充:ES6用const定义常量,let定义变量
 - 补充:对象属性名带特殊字符(比如
-):- 例子:定义
let obj = { 'SVC-TYD': '1' },调用obj['SVC-TYD'] = '2' 
 - 例子:定义
 - 注意:对象属性名包含 常量/变量,使用 模板字符串 时,必须搭配中括号
[``];对象属性名带特殊字符,使用引号时可以不用中括号 
复习:扩展运算符复制对象
- 使用扩展运算符复制对象是浅拷贝的。
 - 也就是说当你复制的对象obj1中包含一个对象c时,你实际上复制的是c的引用,当你修改复制所得到的对象copyObj1中的对象c的aa属性时,被复制的obj1对象中的对象c的属性aa也会被改变。
 - 而修改对象copyObj1的第一层属性b时是不会改动到复制对象obj1的属性b的值的:
 
1  | const obj1={  | 
复习:扩展运算符合并对象
- 如果合并的两个对象中都含有相同属性名的属性,则属性顺序还是按照前面对象的,但后面对象的属性值覆盖前面的。
 - 不管是使用扩展运算符复制对象还是合并对象,他们都是浅拷贝的。
 
1  | const obj1 = {  | 
部分新方法
Object.is()判断两个值是否为同一个值
- 可参考MDN
 - 语法:传入两个参数到
Object.is(value1, value2) - 返回值:全等则返回true,否则返回false。
 - 满足以下任意条件则两个值相等:
- 都是 undefined
 - 都是 null
 - 都是 true 或都是 false
 - 都是相同长度、相同字符、按相同顺序排列的字符串
 - 都是相同对象(意味着都是同一个对象的值引用)
 - 都是数字且
 - 都是 +0
 - 都是 -0
 - 都是 NaN
 - 都是同一个值,非零且都不是 NaN
 
 - 与
==不同,==运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将"" == false判断为 true),而Object.is()不会强制转换两边的值 - 与
===类似但有区别,体现在+0与-0、NaN与NaN的判断上:- 它的判断规则与全等运算符(
===)有一些不同。在比较引用类型时,Object.is()只有在两个值的指向地址完全相同时才返回 true1
2
3
4
5
6
7
8console.log(Object.is(+0, -0));//false
console.log(+0 === -0);//true
console.log(Object.is(NaN, NaN));//true
console.log(NaN === NaN);//false
console.log(Object.is(true, false));//false
console.log(true === false);//false 
 - 它的判断规则与全等运算符(
 
Object.assign()合并对象(类似...扩展运算符)
- 可参考MDN
 - 补充: assign: 分配/赋值
 - 语法:
Object.assign(target, ...sources); - 返回目标对象,此时的目标对象也会改变
 Object.assign()类似...(扩展运算符),可用于合并对象- 拓展:在vue中使用 
Object.assign()/...扩展运算符 可以使复制的对象保持双向绑定关系 Object.assign()与扩展运算符在合并对象时都是浅拷贝的。当合并的多个对象拥有相同属性时后面的属性值覆盖前面的
1  | // 可以看到b的最终属性值是2,后面的覆盖前面的  | 
Object.keys()和Object.values()和Object.entries()
- 注意:这三个方法都是返回对象自身的对应元素的,当存在对象属性值为对象时,不包含属性值对象的元素
 - 参数:对象
 - 注意: “自身”也就是不包括继承自原型的属性
 Object.keys()返回由对象自身可枚举属性名组成的数组 (不包括 Symbol 值作为名称的属性/原型链上的属性)- 注意:数组每一项都是放在引号内的【字符串形式】
 
Object.values()返回由对象自身的值(属性值)组成的数组。Object.entries()返回由对象自身的键值(属性名和属性值)组成的数组。
1  | const obj = {  | 
区别for-in Object.keys() Object.getOwnPropertyNames() Reflect.ownKeys()
for-in遍历对象所有可枚举属性,包括原型链上的属性- 可搭配
hasOwnProperty()去除原型属性 
- 可搭配
 Object.keys()遍历对象所有自身可枚举属性(不包括 Symbol 值作为名称的属性), 不包括原型链上的属性 (仅自身属性)Object.getOwnPropertyNames()返回一个由对象的所有自身属性的属性名(包括 枚举/不可枚举属性 但不包括 Symbol 值作为名称的属性)组成的数组Reflect.ownKeys()返回对象自身全部属性键组成的数组,包括字符串属性、不可枚举的属性和 Symbol 属性- 它的返回值等同于 
Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)) 
- 它的返回值等同于 
 - 注意:操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以,尽量不要用
for-in循环,而用for-of和Object.keys()结合来代替 
补充:可枚举属性 与 不可枚举属性
- 参考
 - 可枚举属性是指那些内部 “可枚举” 标志设置为 
true的属性。对于通过直接的赋值和属性初始化的属性,该标识值默认为即为true。但是对于通过Object.defineProperty等定义的属性,该标识值默认为false。 - 其中js中基本包装类型的 原型属性 是 不可枚举 的,如
Object,Array,Number等。 - 可枚举的属性可以通过
for-in循环进行遍历(除非该属性名是一个Symbol),或者通过Object.keys()方法返回一个可枚举属性的数组。 - 比如: 
length就是数组的不可枚举属性,Object.keys()不可返回,但Object.getOwnPropertyNames()可返回. 
补充:for-in与【ES6引入的】for-of
- for-of是 ES6 引入的新特性之一,for-of常用于异步遍历(例子见《JS 异步进阶》),for-in以及forEach、for是常规的同步遍历
 - 无论是for-in还是for-of语句都是迭代一些东西,它们之间的主要区别在于它们的迭代方式。
 - 区别例子可参考MDN区别例子可参考MDN
 - for-in 语句以任意顺序迭代对象的可枚举属性,更适合用于遍历对象属性
- 注意:会遍历到继承属性,需要**Object的hasOwnProperty()**判断对象/数组是否包含特定的自身(非继承)属性
 - 遍历的是数组的索引(即键名)/对象属性名
 - 会遍历数组所有的可枚举属性,包括原型属性。index索引为字符串型数字,不能直接进行几何运算
 - 因为迭代的顺序是依赖于执行环境的,所以数组遍历不一定按次序访问元素。因此当迭代访问顺序很重要的数组时,最好用整数索引去进行for循环(或者使用 
Array.prototype.forEach()或for-of循环) 
 - for-of 语句按顺序遍历可迭代对象定义要迭代的数据,更适合用于遍历 数组元素/字符串 等拥有 迭代器 对象的集合。
- 不会遍历到继承属性
 - 但是for-of不能遍历普通对象,因为普通对象没有迭代器
- 只能遍历提供了 ES6 引入的 Iterator 接口的数据类型,适用于可迭代对象(包括
Array,Map,Set,String,TypedArray,arguments对象等等) - 但是对象的
Object.keys()、Object.values()、Object.entries()这三个方法返回的是可迭代对象,使用起来就和Map差不多了 
 - 只能遍历提供了 ES6 引入的 Iterator 接口的数据类型,适用于可迭代对象(包括
 - 与
forEach()不同的是,for-of可以正确响应break、continue和return语句 - 遍历的是数组元素值
 - 不会遍历原型属性(
length之类的) 
 - 不同于
for(key of arr)中,key是数组元素,for(key in arr)中key是数组下标。for(key in obj)中,key是对象属性名。- 总结:for…in用于获取键(key),for…of用于获取值(value)
 
 - 以下示例显示了与Array一起使用时,for-of循环和for-in循环之间的区别:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
let iterable = [3, 5, 7];
iterable.foo = 'hello';
for (let i in iterable) {
console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}
for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // logs 0, 1, 2, "foo"
}
}
for (let i of iterable) {
console.log(i); // logs 3, 5, 7
} 
终止循环的语句
- for、for-in、for-of、while都可以由 break, throw 或 return 终止,可以正确响应continue
- 注意:forEach不行
 
 - break 和 continue
- break 语句用于跳出整个循环
 - continue 跳过当前迭代的剩余代码,并直接进行下一次迭代
 
 - throw可创建自定义错误,搭配try-catch可用于定义catch捕捉到的error
- 例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function processItems(items) {
for (const item of items) {
if (item === 'stop') {
throw new Error('Loop stopped.'); // 抛出异常中断循环
}
console.log(item);
}
}
const items = ['apple', 'banana', 'stop', 'orange'];
try {
processItems(items); // apple banana
} catch (error) {
console.log(error.message); // 输出: Loop stopped.
} - 注意:当你使用 throw 中断循环时,需要在适当的位置try-catch捕获异常。否则,异常会向上冒泡并可能导致程序的终止
 
 - 例子:
 - return终止函数的执行并返回函数的值
- 注意:return跳出的是函数,所以循环一定要写在函数内才能用return跳出循环,否则return会报错
error: Illegal return statement - 错误范例:
1
2
3
4
5
6
7const array1 = ['a', 'b', 'c'];
for (const element of array1) {
console.log(element);
if(element == 'b') return
}
// 报错error: Illegal return statement - 正确范例:
1
2
3
4
5
6
7
8const array1 = ['a', 'b', 'c'];
function a () {
for (const element of array1) {
console.log(element);
if(element == 'b') return
}
}
a() // a b 
 - 注意:return跳出的是函数,所以循环一定要写在函数内才能用return跳出循环,否则return会报错
 
判断对象自身属性
【不推荐】for-in+hasOwnProperty()
- 用 
for-in语句遍历对象属性时我们可以使用hasOwnProperty()检查对象是否包含属性名,但语句复杂且不是直接放入数组,故不推荐使用。obj.hasOwnProperty("属性名"):- 参数:要检测的属性的 String 字符串形式表示的名称。
 - 返回:布尔值,表示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。是对象自身属性则true,不是则false。
 
 - 关于
for-in与hasOwnProperty()遍历对象自身属性的例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const obj = {
a: 1,
b: 2,
c: {
j: 222,
a: 66
}
}
for (let key in obj){
if(obj.hasOwnProperty(key)){
console.log(key);
}
}
// 运行结果: a b c 
【推荐】for-of+Object.keys()
- 可使用
Object.keys()代替上面例子中的hasOwnProperty()来判断是否为自身属性。 for-of语句遍历对象属性。- 使用
for-of语句与Object.keys()打印自身属性的例子: 
1  | const obj = {  | 
- 注意:
for-of语句与Object.keys()搭配,for-in与hasOwnProperty()搭配。 
Reflect.ownKeys()所有属性组成的数组
- 可参考MDN: 返回对象自身全部属性键组成的数组,包括不可枚举的属性和 Symbol 属性
 - 它的返回值等同于 
Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)) - 区别
Reflect.ownKeys()和Object.keys():区别在于返回的属性键的类型和范围Reflect.ownKeys(obj):返回一个由对象自身的所有属性键组成的数组,包括字符串属性、不可枚举的属性和 Symbol 属性1
2
3
4
5
6
7
8
9const obj = {
key1: 'value1',
[Symbol('key2')]: 'value2',
};
const keys = Reflect.ownKeys(obj);
console.log(keys); // ['key1', Symbol(key2)]
const keys2 = Object.keys(obj);
console.log(keys2); // ['key1']Object.keys(obj):返回一个由对象自身的所有可枚举属性键组成的数组,只包括字符串键,不包括不可枚举的属性和 Symbol 属性1
2
3
4
5
6
7const obj = {
key1: 'value1',
key2: 'value2',
};
const keys = Object.keys(obj);
console.log(keys); // ['key1', 'key2']- 总结: 
Reflect.ownKeys()返回对象自身的所有属性键,包括字符串属性、不可枚举的属性和 Symbol 属性,而Object.keys()只返回对象自身的可枚举的字符串属性 
 
区分getOwnPropertyNames和getOwnPropertySymbols
Object.getOwnPropertyNames(): 返回一个包含了对象 obj 的所有自身可枚举属性的字符串键(不包括Symbol键)的数组。这些属性是直接在对象上定义的,而不是从原型链继承而来Object.getOwnPropertySymbols(): 返回一个包含了对象 obj 的所有自身 Symbol 键(不包括字符串键)属性的数组,包括可枚举和不可枚举的属性。这些属性是直接使用 Symbol 定义在对象上的,而不是从原型链继承而来- 注意:这两个方法都只返回对象自身的属性键,而不会返回继承的属性键。如果你希望获取继承的属性键,可以使用 
for...in循环遍历对象或使用Reflect.ownKeys()方法 
1  | const symbolKey1 = Symbol('key1');  | 
- 在上面的例子中,我们创建了一个包含字符串键和 Symbol 键的对象 
obj - 使用 
Object.getOwnPropertyNames(),我们只能获取到对象的字符串键stringKey1和stringKey2 - 而使用 
Object.getOwnPropertySymbols(),我们只能获取到对象的 Symbol 键Symbol(key1)和Symbol(key2) 
__proto__属性读取对象原型
__proto__属性代表当前对象的原型。- 所有的引用类型(数组、对象、函数)的
__proto__属性值 都指向它的 构造函数的prototype属性值。- “指向”即“全等”,
__proto__属性值===它的构造函数的prototype属性值 
 - “指向”即“全等”,
 - 很多浏览器不支持,只出现在ES6附录中没出现在正文。
 - 调试的时候使用就好,不要使用在正式代码中。
 - 例子:(下方“
Object.setPrototypeOf()修改对象原型”的例子中) - 比起
__proto__属性更加推荐使用Object.getPrototypeOf()读取对象原型(下方详述)。 
补充:Object.create()创建对象并指定原型
Object.create()用于创建一个对象时指定原型。- 语法:
const objName = Object.create(原型对象名); - 例子:(下方“
Object.setPrototypeOf()修改对象原型”的例子中) 
Object.setPrototypeOf()修改对象原型
- 语法:
Object.setPrototypeOf(需要修改的对象名,新的原型名) Object.setPrototypeOf()性能低下,建议不要使用。 可以用Object.create()1
2
3
4
5
6
7
8
9
10
11
12
13const obj1={
a:1
};
const obj2={
b:2
}
const obj=Object.create(obj1);//以obj1为原型创建对象obj
console.log(obj.__proto__);//{a: 1}
Object.setPrototypeOf(obj,obj2);//使用setPrototypeOf()将obj的原型改为obj2
console.log(obj.__proto__);//{b: 2}- 注意:
console.log(obj);打印出的是{}。 
Object.getPrototypeOf()读取对象原型
- 语法:
Object.getPrototypeOf(对象名) - 比起
__proto__属性更加推荐使用Object.getPrototypeOf()读取对象原型。- 补充:所有的引用类型(数组、对象、函数)的
__proto__属性值 都指向它的 构造函数的prototype属性值。- “指向”即“全等”,
__proto__属性值===它的构造函数的prototype属性值 - 所以可以使用
Object.getPrototypeOf()读取对象原型 
 - “指向”即“全等”,
 
 - 补充:所有的引用类型(数组、对象、函数)的
 
1  | const obj1 = {  | 
super关键字(访问原型对象)
- 可以通过
super关键字访问原型对象上的属性和方法。 - 注意:只有使用简洁表示法写函数才可以调用
super关键字,换成箭头函数或者普通函数写法去调用super关键字会报错。1
2
3
4
5
6
7
8
9const obj={name:"小明"};
const copyObj={
say(){
console.log(`我的名字是${super.name}`);
}
}
//将对象copyObj的原型改为对象obj才可以在copyObj中通过supeobj的属性name
Object.setPrototypeOf(copyObj,obj);
copyObj.say();//我的名字是小明