ES6 对象的扩展
一、 对象成员 的扩展
1. 对象成员 的简洁写法
- 属性
const foo = 'bar';
const result = {foo};
// 上式 等同于:
const foo = 'bar';
const result = {foo: foo};
- 方法
const o = {
method() {
return "Hello!";
}
};
// 上式 等同于
const o = {
method: function() {
return "Hello!";
}
};
2. 自定义创建对象时,属性名 可以是 动态表达式
- 举例说明:
// 如下:ES6支持;ES5不支持
let lastWord = 'last word';
const a = {
'first word': 'hello',
[lastWord]: 'world'
};
- 注意区分:
- ES5、ES6 都支持给 已创建好的对象 添加属性时,属性名为动态表达式
- ES5 不支持在 自定义创建对象时, 为对象添加 动态表达式的属性
- ES6 支持在自 自定义创建对象时, 为对象动态添加属性
// 如下:ES5 、ES6 都支持
let obj = {};
const aa = 'name';
obj[aa] = 'zhang';
- 约束: 属性名表达式与简洁表示法,不能同时使用,会报错
// 报错
const foo = 'bar';
const bar = 'abc';
const baz = { [foo] };
// 正确
const foo = 'bar';
const baz = { [foo]: 'abc'};
二、 对象的 扩展运算符
1. 作用:取出对象中 自身 可枚举的、非继承 的属性
let z = { a: 3, b: 4 };
let result = { ...z };
console.log(result); // { a: 3, b: 4 }
2. 扩展运算符 结合 解构赋值
- 结构赋值时,尚未被读取的属性,都会被分配到指定的对象上面
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }
- 解构赋值不是最后一个参数,所以会报错
let { ...x, y, z } = obj; // 句法错误
let { x, ...y, ...z } = obj; // 句法错误
- 解构赋值是 浅拷贝;不复制 原型上的属性
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3 // { b: 2 }
3. 等同于 Object.assign( ) :对象属性拷贝合并(浅拷贝)
- 重名属性,后者覆盖前者
let result = { ...a, ...b };
// 等同于
let result = Object.assign({}, a, b);
- 不能用于对象的 深拷贝: 对象的属性如果是对象,则为属性对象的 浅拷贝:只是拷贝了 属性对象的指针
const a = {
name: 'zhang',
info: {
age: 18
}
};
const aClone = {...a};
a.name = 'xin';
a.info.age = 20;
console.log(aClone); // aClone.name 值为 'zhang';aClone.info.age 值为20
三、 对象的合并:Object.assign( )
1. 语法 Object.assign(target, source1, source2)
-
参数
target
: 目标对象 -
参数
source1
、source2
: 源对象 -
返回值: 合并了 源对象属性的 目标对象指针
2. 作用:对象属性的合并
-
将源对象 所有可枚举的、非继承的属性, 合并到 目标对象中
-
如果 目标对象 与 源对象 有同名属性、源对象 与 源对象 有重名属性:后者覆盖前者
3. 对象的属性如果是对象,则为属性对象的 浅拷贝:只是拷贝了 属性对象的指针
- 如果 源对象 某个属性的值是对象,那么目标对象拷贝,得到的是这个对象的引用
const a = {age: 20};
const b = {
info: {
name: 'zhang',
sex: 'man'
}
};
const result = Object.assign(a, b);
console.log(result.info === b.info); // true
- 注意:
Object.assign( )
不能用于对象的深拷贝,举例如下
const a = {
name: 'zhang',
info: {
age: 18
}
};
const aClone = Object.assign({}, a);
console.log(a === aClone); // 输出false;但改变了 a.info 中的属性;aClone.info 中的属性也会变化
4. 如果参数不是对象,会被默认转成对象(了解)
-
如果参数不是对象,会默认将参数转为对象(但 null / undefined 不能转为对象)
-
参数
target
:(目标对象)- 若 值为 null / undefined, 会直接报错
- 若值为 string 类型:会将字符串转为包装对象(数组形式);
'ab'
变为[0: 'a', 1: 'b']
- 若值为 Number 类型:会将 数值 转为包装对象
- 若值为 Boolean 类型:会将 布尔值 转为包装对象
-
参数
source1
:(源对象)- 若 值为 null / undefined,会跳过,不报错
- 若值为 string 类型:会将字符串转为包装对象(数组形式);
'ab'
变为[0: 'a', 1: 'b']
- 若值为 Number 类型:会被忽略
- 若值为 Boolean 类型: 会被忽略
四、对象的遍历:Object.keys( )、Object.values( )、Object.entries( )
1. ES5 引入 Object.keys( )
-
作用: 遍历 自身 可枚举的、非继承的属性 的 键值
-
语法:
-
Object.keys(obj)
-
参数: 要遍历的对象
-
返回值: 对象 键名 构成的 一维数组(键名)
-
var obj = { foo: 'bar', baz: 42 };
console.log(Object.keys(obj)); // ["foo", "baz"]
2. ES6 补充 Object.values( )
-
作用: 遍历 自身 可枚举的、非继承的属性 的 键值
-
语法:
-
Object.values(obj)
-
参数: 要遍历的对象
-
返回值: 对象 键值 构成的 一维数组(键值)
-
var obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj)); // ["bar", 42]
3. ES6 补充 Object.entries( )
- 语法:
-
Object.values(obj)
-
参数: 要遍历的对象
-
返回值: 对象 键值 构成的 二维数组(键名 、键值)
-
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ["foo", "bar"], ["baz", 42] ]
- 作用: 遍历 自身 可枚举的、非继承的属性 的 键名、键值
-
遍历对象
-
将对象转为真正的Map结构
-
const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }
4. 遍历 对象属性 的方法汇总
-
(1)遍历对象属性 的几种方法:
-
for...in
:遍历 对象属性 (含:可枚举的、继承的;不含:Symbol 类型的) -
Object.keys(obj)
、Object.values(obj)
、Object.entries(obj)
:返回一个数组;(含:可枚举的;不含:继承的、Symbol类型的) -
Object.getOwnPropertyNames(obj)
:返回一个键名数组;(含:可枚举的、不可枚举的;不含:继承的、Symbol类型的) -
Object.getOwnPropertySymbols(obj)
:返回一个 Symbol 键值的数组;(只含:Symbol 类型的) -
Reflect.ownKeys(obj)
:返回一个数组, (含:除继承外的 所有属性,无论 是否可比遍历、无论Symbol类型;不含:继承的)
-
-
(2)遍历对象属性的顺序: 以上五种方法都遵循,如下
- 首先遍历所有数值键,按照数值升序排列
- 其次遍历所有字符串键,按照加入时间升序排列
- 最后遍历所有 Symbol 键,按照加入时间升序排列
五、对象属性的 底层描述
1. 回顾:获取对象属性 的描述对象 Object.getOwnPropertyDescriptor(obj, property)
-
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为
-
Object.getOwnPropertyDescriptor(obj, property)
方法来 获取对象属性 的描述对象
const obj = { foo: 123 };
const result = Object.getOwnPropertyDescriptor(obj, 'foo');
console.log(result);
// {
// value: 123, // 属性值
// writable: true, // 可写性:是否可修改
// enumerable: true, // 可枚举性:是否可枚举
// configurable: true // 可配置性:是否可以用 delete删除
// }
2. 回顾:给对象添加属性 和 属性的描述信息 Object.defineProperty(obj, property, descriptor)
- 语法:
-
参数1:要添加属性的对象
-
参数2:要添加属性的键值
-
参数3:要添加属性的描述信息
-
var obj = {};
Object.defineProperty(obj, "a", {
value : 1,
writable : true,
configurable : true,
enumerable : true
});
// 如上代码,等同于:
var o = {};
o.a = 1;
3. 回顾:判断对象的属性 是否是继承的 / 自身提供的 myObject.hasOwnProperty(property)
- 语法:
-
参数:要判断的属性
-
返回值:布尔值(true:对象自身属性;false:对象继承属性)
-
4. 回顾:对象属性的 可枚举性 Object.getOwnPropertyDescriptor(obj, property).enumerable
- 查看对象的属性是否 可枚举
console.log(Object.getOwnPropertyDescriptor([], 'length').enumerable);
// false
- 4个操作:会忽略
enumerable
为false
的属性-
for...in
:遍历 对象自身、可枚举、继承 的属性(含继承、不含 Symbol 属性) -
扩展运算符
...
:遍历 对象自身、可枚举属性(不含继承) -
Object.keys()
:遍历 对象自身、可枚举属性(不含继承、 不含 Symbol 属性) -
JSON.stringify()
:只串行化 对象自身、可枚举的属性(不含继承) -
Object.assign()
: 拷贝 对象自身、可枚举属性(不含继承)
-
5. 回顾:判断属性是否是 对象自身的 并且 可枚举 maObject.propertyIsEnumerable(property)
- 返回值: 只有属性 既是自身属性,又可枚举, 才返回
true
6. ES6:获取对象所有自身(不含继承)属性 的描述对象 Object.getOwnPropertyDescriptors
const obj = {
foo: 123,
get bar() { return 'abc' }
};
const result = Object.getOwnPropertyDescriptors(obj)
console.log(result);
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
六、对象原型 操作方法
1. __proto__
获取对象的原型对象:只有浏览器广泛支持(不推荐使用)
-
声明: 该属性没有写入 ES6 的正文,标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署;所以在实际开发中不要使用该属性
-
语法:
obj.__proto__
可以获取obj
对象的原型对象
2. ES6 设置 原型对象 Object.setPrototypeOf(object, prototype)
- 语法:
-
参数1:被操作对象
-
参数2:原型对象
-
返回值:被操作对象本身
-
let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);
proto.y = 20;
proto.z = 40;
obj.x // 10
obj.y // 20
obj.z // 40
- 规定:
- 如果第一个参数不是对象,会自动转为对象;由于返回的还是第一个参数,所以这个操作不会产生任何效果
- 若第一个参数是
undefined
和null
(无法转成对象),会报错
Object.setPrototypeOf(1, {}) === 1 // true
Object.setPrototypeOf('foo', {}) === 'foo' // true
Object.setPrototypeOf(true, {}) === true // true
Object.setPrototypeOf(undefined, {}); // 报错
Object.setPrototypeOf(null, {}); // 报错
3. ES6 获取 原型对象 Object.getPrototypeOf(object)
- 语法:
-
参数:操作对象
-
返回值:操作对象 的 原型对象
-
七、关键字 super --- 指向 当前对象的原型对象(2个使用约束条件)
-
关键字 super:指向当前对象的原型对象
-
约束:
- 只能用在 对象的 简写 方法 之中, 目前 super 在其他地方都会报错
// 正确用法
const obj = {
find() {
return super.foo;
}
};
// 以下三种情况,都不允许,会报错
const obj = {
foo: super.foo // 报错:super 用在了对象的属性中
}
const obj = {
foo: () => super.foo // 报错:super 用在了函数中,又将这个函数定义成对象的方法
}
const obj = {
foo: function () {
return super.foo // 报错:super 用在了函数中,又将这个函数定义成对象的方法
}
}
八、 比较 任意数据类型的值 是否相等:Object.is( )
-
ES6提出: "Same-value equality" 的原则,对比较两个值是否相等,做了更严格的规范
-
语法:
-
参数1,参数2: 要比较是否相等的两个值
Object.is(a, b)
-
返回值: 布尔值
-
-
对比:
-
ES6中的比较方法
Object.is( )
:NaN 等于 NaN;-0 不等于 +0 -
ES5中的比较方法
==
===
:NaN 不等于 NaN;-0 等于 +0;==
会发生隐式转换
-