学习归纳下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();//我的名字是小明