一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情。
十五、ES6+对象方法
15-1、Object.is
在ES5中,判断两个值是否相等基本都是使用==或===,ES6中提供了Object.is方法用于判断两个值是否相同,此方法弥补了使用等号判断存在的问题。
15-1-1、ES5中的==与===
15-1-1-1、==
在使用==对比两个操作数时,如果两个操作数类型不同则会进行隐式转换:
0 == -0 // true
0 == '0' // true
0 == false // true
0 == '' // true
"" == false // true
null == undefined // true
1 == '1' // true
true == 'true' // false
NaN == 'NaN' // false
NaN == NaN // false
{"name": "zs"} == {"name": "zs"} // false
let a = {"name": "zs"}
let b = a
console.log(a == b) // true
可见:
- NaN不等于包含本身的任何值
- 0与-0、false、空字符串都相等
- null与null、undefined相等
- 两个操作值引用同一个对象返回true,否则返回false
15-1-1-2、===
===是严格相等的,与==类似,但是区别在于===不会执行隐式转换,只有当两个操作值的值与类型都相等的前提下,才会返回true,但是也会存在问题:
+0 === -0 // true
true === true // true
null === null // true
NaN === NaN // false, NaN永远不等于NaN
1 === '1' // false, 值类型不同:数值和字符串
true === 'true' // false
null === undefined // false
let a = {"name": "zs"}
let b = a
console.log(a === b) // true
可见:
- NaN不等于包含它本身的任何值
- 0等于 -0 、+0;-0与+0也相等
- null等于null,但不等于undefined
- 两个操作值引用同一个对象,也会返回true
15-1-2、Object.is的基本使用方法
Object.is接收两个需要判断的参数,返回一个布尔值,相等返回true,不等返回false
语法:
Object.is(value1, value2);
Object.is('zs', 'zs'); // true
Object.is('zs', 'Zs'); // false
Object.is(1, '1'); // false
Object.is(window, window); // true
Object.is([], []); // false
var foo = { a: 1 };
var bar = { a: 1 };
var obj = foo;
Object.is(foo, foo); // true
Object.is(foo, bar); // false
Object.is(foo, obj); // true
Object.is(null, null); // true
// 特例
Object.is(0, -0); // false
Object.is(0, +0); // true
Object.is(-0, -0); // true
Object.is(NaN, 0/0); // true
Object.is(NaN, NaN); // true
需要注意的是:
- 使用Object.is对比两个NaN是相等的
- 0与-0是不相等的
- 0与+0是是相等的
总结:
Object.is在对比两个值是否相等时接近“===”,但又有以下不同:
- 使用===判断时 0 与 +0 、-0均相等,而NaN与NaN不相等
- 使用Object.is判断时0 与 +0相等、0与-0不相等、-0与-0相等;而且NaN等于其本身
15-2、Object.assign
该方法用于将一个对象复制/合并到另一个对象中。
语法:
Object.assign(target, ...sources)
参数描述:
参数 | 描述 |
---|---|
target | 需要拷贝到的目标对象 |
sources | 源对象 |
15-2-1、Object.assign的基本使用方法
- 拷贝对象
const target = {};
const source = {a: 1, b: 2, c: 3};
Object.assign(target, source);
target.d = 4;
console.log(target) // {a: 1, b: 2, c: 3, d: 4}
console.log(source) // {a: 1, b: 2, c: 3}
Object.assign可以把源对象拷贝到目标对象上去,目标对象的更改不会影响到源对象
- 合并对象
const target = {a: 1};
const source1 = {b: 2};
const source2 = {c: 3};
Object.assign(target, source1, source2);
console.log(target); // {a: 1, b: 2, c: 3}
Object.assign同样也可以用于合并对象,可见不会将目标对象清空,而是会合并后面所有的对象上的值
- 覆盖对象前面的属性值
const target = {a: 1, b: 1};
const source1 = {b: 2, c: 2};
const source2 = {c: 3};
Object.assign(target, source1, source2);
console.log(target); // {a: 1, b: 2, c: 3}
如果目标对象有与源对象相同的属性,那么源对象该属性的值会覆盖掉目标对象该属性的值;同样,如果后面源对象中有与前面源对象相同的属性,那么后面源对象属性值也会覆盖掉前面源对象的属性值。
15-2-2、Object.assign的注意事项
- Object.assign属于浅拷贝,只拷贝第一层的属性值,如果这个值是一个对象类型,那么Object.assign不会对该属性进行深拷贝,也就是说拷贝后的对象下的这个对象是源对象与目标对象共有的,无论谁对该对象进行了修改,都会受到影响:
const target = {};
const source = {a: 1, b: {c: 2, d: 3}};
Object.assign(target, source);
target.a = 5;
target.b.c = 9;
console.log(target) // {a: 5, b: {c: 9, d: 3}}
console.log(source) // {a: 1, b: {c: 9, d: 3}}
15-3、Object.keys
我们知道迭代对象可以使用for...in,但for..in会枚举出对象原型链上的属性,这使得我们还要去进一步判断一下是不是原型链上的属性;Object.keys可以接收一个对象并返回一个可枚举的数组,该数组中的元素的顺序与for...in遍历的顺序一致,但是Object.keys返回的可枚举数组中只会包含对象自身的属性(不包含其原型链上的属性)
- 其实在ES5中就有了Object.keys方法,但是ES5中的Object.keys方法如果接收的参数是一个进本数据类型不是对象的话会抛出TypeError错误
- 而ES6对Object.keys底层实现做了更新,使得Object.keys可以接收非对象类型的数据,接收到后会将其强制转换为一个对象
15-3-1、Object.keys的基本使用方法
Object.keys接收一个对象,返回该对象自身可枚举属性组成的数组,数组中属性名的排列顺序与for..in遍历顺序一致
语法:
Object.keys(obj)
Object.keys不光可以接收对象,也可以接收数组、类数组等:
- Object.keys用于对象
const obj = {
name: 'zs',
age: 18
}
console.log(Object.keys(obj)); // ["name", "age"]
用于对象时,返回该对象键值作为元素的数组
- Object.keys用于数组
const arr = ['a', 'b', 'c'];
console.log(Object.keys(arr));
// console: ['0', '1', '2']
用于数组时,返回该数组索引作为元素的数组
- Object.keys用于类数组
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // ['0', '1', '2']
- 要注意的是,如果键值是数字和字符串的混合,那么会先进行数值的排序,然后再按照添加的顺序排列字符串:
const obj = { name: 'zs', 10: 'a', 3: 'b', age: 18 };
console.log(Object.keys(obj));
// ["3", "10", "name", "age"]
15-3-2、Object.keys的注意事项
- Object.keys不可以获取到不可枚举的属性
// 创建一个obj对象带有一个不可枚举属性
const obj = Object.create({}, {
getFoo: {
value: function () { return this.foo; }
}
});
obj.foo = 1;
console.log(Object.keys(obj)); // ['foo']
- Object.keys会对数值型的键进行排序,字符串型 / Symbol型的键则会按照添加顺序排序
const obj1 = {99: '九十九', 5: '五', 7: '七'}
Object.keys(obj1) // ["5", "7", "99"]
const obj2 = {c: 'z', a: 'x', b: 'y'}
Object.keys(obj2) // ["c", "a", "b"]
const obj3 = { name: 'zs', 10: 'a', 3: 'b', age: 18 };
Object.keys(obj3); // ["3", "10", "name", "age"]
15-3-3、Object.keys接收其他类型的参数
Object.keys会根据传入的参数进行类型转换,转换为Object类型的值:
参数类型 | 结果 |
---|---|
Undefined | 抛出TypeError |
Null | 抛出TypeError |
Boolean | 转换为Boolean对象 |
Number | 转换为Number对象 |
String | 转换为String对象 |
Symbol | 转换为Symbol对象 |
Object | 不转换 |
// 参数是undefined或null
Object.keys(undefined) // Uncaught TypeError: Cannot convert undefined or null to object
// 参数是数值
Object.keys(123) // []
// 参数是布尔值
Object.keys(true) // []
// 参数是字符串
Object.keys('hello') // ["0", "1", "2", "3", "4"]
- 由于数值与布尔值在转为对象时没有可提取的属性,所以会返回空数组
- 而String对象有可提取的属性,所以会返回内容
15-4、Object.values
该方法用于返回对象可枚举属性的值作为元素的数组。
语法:
Object.values(obj);
15-4-1、Object.values的基本使用方法
- 参数是对象
const obj = {a: 1, b: 2, c: 3};
Object.values(obj) // [1, 2, 3]
- 参数是数值或布尔值
Object.values同Object.keys一样,如果接收的参数不是对象类型,那么会对参数进行类型转换。
Object.values(123); // []
Object.values(false) // []
- 参数是字符串
Object.values('abc') // ['a', 'b', 'c']
15-4-2、Object.values注意事项
由于Object.keys会对属性是数值的键进行排序,在这个过程中属性对应的值也会跟着改变位置,所以Object.values返回的数组是按Object.keys顺序后的结果展示的,所以得到的值要和排序后的属性一一对应。
const obj = {10: 'a', 1: 'b', 7: 'c'};
Object.values(obj) // ['b', 'c', 'a']
15-5、Object.entries
如果我们同时想获取键值与其对应的值,可以使用ES6提供的Object.entries:
- Object.entries会返回一个二维数组,数组的每一项是可枚举对象上的属性和值的数组
语法:
Object.entries(obj);
15-5-1、Object.entries的基本使用方法
- 参数是对象
const obj = {a: 1, b: 2, c: 3};
console.log(Object.entries(obj)) // [['a', 1], ['b', 3], ['c', 3]]
- 参数是布尔值/数字
Object.entries(50) // []
Object.entries(false) // []
- 参数是字符串
console.log(Object.entries('abc')) // [["0", "a"], ["1", "b"], ["2", "c"]]
15-5-2、Object.entries的注意事项
当键值有数字时,会按照数值的大小从小到大遍历,因此返回的二维数组时排好序的:
const obj = {10: 'a', 1: 'b', 7: 'c'};
console.log(Object.entries(obj))
// [["1", "b"], ["7", "c"], ["10", "a"]]
15-5-3、Object.entries的使用场景
- 将对象转换为Map结构
const obj = { name: 'zs', age: 7 };
const map = new Map(Object.entries(obj));
console.log(map) // Map(2) {"name" => "zs", "age" => 7}
console.log(map.get(name)) // zs
- 由于Map构造函数中可以接收一个二维数组,所以我们可以利用Object.entries获取对象的键值对组成的二维数组后再转为Map结构~
15-6、Object.fromEntries
Object.fromEntries是Object.entries的反转函数,接收一个可迭代对象如:Array、Map等来返回一个新的对象。
- 将二维数组转换为对象
const arr = [ ['a', '0'], ['b', '1'], ['c', '2'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // {a: "0", b: "1", c: "2"}
- 数组可看成包含键与值的二维数组,这样的二维数组才能转换为对象
- 将Map转换为对象
const map = new Map();
map.set('name', 'zs');
map.set('age', 7);
console.log(map); // Map(2) {"name" => "zs", "age" => 7}
const obj = Object.fromEntries(map);
console.log(obj); // {name: "zs", age: 7}