1. 对象的扩展方法
1.1 Object.is()
ES5
比较两个值是否相等,只有两个运算符:相等运算符(==
)和严格相等运算符(===
)。它们都有缺点,前者会自动转换数据类型,后者的NaN
不等于自身,以及+0
等于-0
。JavaScript
缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。 ES6
提出“Same-value equality
”(同值相等)算法,用来解决这个问题。Object.is
就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===
)的行为基本一致
console.log(Object.is('foo', 'foo'));
console.log(Object.is(NaN, NaN))
// true
console.log(Object.is({}, {}));
console.log(Object.is(+0, -0));
// false
不同之处只有两个:一是+0
不等于-0
,二是NaN
等于自身
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
1.2 Object.assign()
Object.assign
方法用于对象的合并,将源对象(source
)的所有可枚举属性,复制到目标对象(target
)
let target = { a: 1 };
let source1 = { b: 2 };
let source2 = { c: 3 };
Object.assign(target, source1, source2);
console.log(target) // { a:1, b:2, c:3 }
Object.assign
方法的第一个参数是目标对象,后面的参数都是源对象。
注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性
let target = { a: 1, b: 1 };
let source1 = { b: 2, c: 2 };
let source2 = { c: 3 };
Object.assign(target, source1, source2);
console.log(target) // { a:1, b:2, c:3 }
如果只有一个参数,Object.assign
会直接返回该参数
let obj = {a: 1};
Object.assign(obj) === obj // true
如果该参数不是对象,则会先转成对象,然后返回
typeof Object.assign(2) // "object"
由于undefined
和null
无法转成对象,所以如果它们作为参数,就会报错
Object.assign(undefined) // 报错
Object.assign(null) // 报错
注意:Object.assign
可以用来处理数组,但是会把数组视为对象
Object.assign([1,2,3], [4,5]);
// [4,5,3]
// 把数组视为属性名为 0、1、2 的对象,因此源数组的 0 号属性 4 覆盖目标数组的 0 号属性 1
1.3 Object.keys()
ES5
引入了Object.keys
方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable
)属性的键名
var obj = { foo: 'bar', baz: 42 };
console.log(Object.keys(obj))
// ["foo", "baz"]
ES8
引入了跟Object.keys
配套的Object.values
和Object.entries
,作为遍历一个对象的补充手段,供for...of
循环使用
let { keys, values, entries } = Object;
let obj = { a: 1, b: 2, c: 3 };
for (let key of keys(obj)) {
console.log(key); // 'a', 'b', 'c'
}
for (let value of values(obj)) {
console.log(value); // 1, 2, 3
}
for (let [key, value] of entries(obj)) {
console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}
1.4 Object.values()
Object.values
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable
)属性的键值
let obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
1.5 Object.entries
Object.entries
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable
)属性的键值对数组
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
除了返回值不一样,该方法的行为与Object.values
基本一致
2. 数组 的扩展
2.1 Array.from()
Array.from
方法用于将类数组转为真正的数组
// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
console.log(p);
});
// arguments对象
function foo() {
var args = Array.from(arguments);
// ...
}
参数:
- 第一个参数:一个类数组对象,用于转为真正的数组
- 第二个参数:类似于数组的
map
方法,用来对每个元素进行处理,将处理后的值放入返回的数组。 - 第三个参数:如果
map
函数里面用到了this
关键字,还可以传入Array.from
的第三个参数,用来绑定this
2.2 Array.of()
Array.of
方法用于将一组值,转换为数组。
这个方法的主要目的,是弥补数组构造函数Array()
的不足。因为参数个数的不同,会导致Array()
的行为有差异
//Array
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]
Array.of(3, 11, 8) // [3, 11, 8]
Array.of(3) // [3]
Array.of(3).length // 1
2.3 数组实例的 copyWithin()
数组实例的copyWithin
方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改原数组
它接受三个参数。
-
target
(必需):从该位置开始替换数据。 -
start
(可选):从该位置开始读取数据,默认为0
。如果为负值,表示倒数。 -
end
(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数
这三个参数都应该是数值,如果不是,会自动转为数值
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
2.4 数组实例的 find()
和 findIndex()
数组实例的find
方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true
的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
[1, 4, -5, 10].find(n => n < 0)
// -5
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
// find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
findIndex
方法的用法与find
方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
})
// 2
这两个方法都可以接受第二个参数,用来绑定回调函数的
this
对象
2.5 数组实例的 fill()
fill
方法使用给定值,填充一个数组。
fill
方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
fill
方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
2.6 for...of
es6
引入的作为遍历所有数据结构的统一的方法。
一个数据结构只要部署了Symbol.iterator
属性,就被视为具有 iterator
接口,就可以用for...of
循环遍历它的成员。也就是说,for...of
循环内部调用的是数据结构的Symbol.iterator
方法
2.7 数组实例的 entries()
,keys()
和 values()
entries()
,keys()
和values()
——用于遍历数组。它们都返回一个遍历器对象,可以用for...of
循环进行遍历,唯一的区别是keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
2.8 数组实例的 includes()
方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes
方法类似。ES2016
引入了该方法
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
该方法的第二个参数表示搜索的起始位置,默认为0
。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4
,但数组长度为3
),则会重置为从0
开始。
没有该方法之前,我们通常使用数组的indexOf
方法,检查是否包含某个值。
indexOf
方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1
,表达起来不够直观。二是,它内部使用严格相等运算符(===
)进行判断,这会导致对NaN
的误判
2.9 数组的空位
数组的空位指,数组的某一个位置没有任何值。
注意,空位不是undefined
,一个位置的值等于undefined
,依然是有值的。空位是没有任何值,in
运算符可以说明这一点
0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false
2.9.1 ES5
对空位的处理,已经很不一致了,大多数情况下会忽略空位。
-
forEach()
,filter()
,reduce()
,every()
和some()
都会跳过空位。 -
map()
会跳过空位,但会保留这个值 -
join()
和toString()
会将空位视为undefined
,而undefined
和null
会被处理成空字符串。
ES6
则是明确将空位转为undefined
。
2.9.2 Array.from
方法会将数组的空位,转为undefined
,也就是说,这个方法不会忽略空位。
Array.from(['a',,'b'])
// [ "a", undefined, "b" ]
2.9.3 扩展运算符(...
)也会将空位转为undefined
[...['a',,'b']]
// [ "a", undefined, "b" ]
2.9.4 copyWithin()
会连空位一起拷贝
[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
2.9.5 fill()
会将空位视为正常的数组位置
new Array(3).fill('a') // ["a","a","a"]
2.9.6 for...of
循环也会遍历空位
let arr = [, ,];
for (let i of arr) {
console.log(1);
}
// 1
// 1
上面代码中,数组arr
有两个空位,for...of
并没有忽略它们。如果改成map
方法遍历,空位是会跳过的
2.9.7 entries()
、keys()
、values()
、find()
和findIndex()
会将空位处理成undefined
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'].keys()] // [0,1]
// values()
[...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) // 0
由于空位的处理规则非常不统一,所以建议避免出现空位