用Array的方法,对不是Array的东西做一些事情…
call方法真是一个有意思的东西,它可以改变函数调用时this的值。而我们知道,在函数里,this指向了调用这个函数的环境对象,比如一道经典面试题:
1 | var num = 2; |
换句话说,如果一个对象obj上有方法foo,你可以通过obj.foo()
调用;如果没有obj上没有方法foo,obj.foo()
是会报错的,但是,使用foo.call(obj)
,可以强行达到obj.foo()
的效果,比如:1
2
3
4
5
6
7function foo(){
console.log(this.num);
}
var obj = {
num: 1
}
foo.call(obj);// 1
Array.prototype.slice.call
的用处就是这样,可以在array-like(类数组,就是长得像数组,但不是数组)的对象上强行使用slice方法,比如:Array.prototype.slice.call(arguments)
就是把arguments
对象转化为数组。当然,除了arguments
,我们还能在HTMLCollection
或NodeList
身上使用。那么到底什么算是类数组呢?
有length属性的对象。
比如:
1 | var obj1 = { |
那如果没有length呢?
1 | var obj1 = { |
原来没有length属性的对象也会被转为数组,只不过认为它length=0而已。
那如果对象的属性没有按照0-n顺序乖乖排好呢?
1 | var obj1 = { |
原来转化的时候,会以length
为基础,生成一个长度为length
的数组,obj
的属性是数组的有效index
的话,就会把对应值填入到对应位置,其他的位置找不到值,就会填入undefined
。
所以前面的说法其实不对,所有的对象都可以被视为类数组,有length
的视为长度为length
的数组,没有的,视为长度为0的数组。
以length
属性为基础
这句话很重要。
另外,call
方法的参数如果是原始值类型
,会传入它的自动包装对象
:1
var arr = [].slice.call('hello');
等价于:1
2
3
4
5
6
7
8
9
10
11var arr = [].slice.call(new String('hello'));/* [ 'h', 'e', 'l', 'l', 'o' ] */
因为new String('hello')就是
{
0: "h",
1: "e",
2: "l",
3: "l",
4: "o",
length: 5
}
以上就是Array.prototype.slice.call
的一些细节,那么除了slice
之外,Array
对象还有很多其他的方法,这些方法是不是也能用到对象身上呢?
Array.prototype.join
join方法是把数组转化为字符串的方法,具体表现不再赘述,看两个例子:1
2
3
4
5
6
7
8
9
10
11
12
13var obj1 = {
0: 'Tom',
1: 'Jack',
2: 'Jason',
length: 6
}
var arr = [].join.call(obj1, '-');// Tom-Jack-Jason
var obj1 = {
0: 'Tom',
1: 'Jack',
2: 'Jason',
}
var arr = [].join.call(obj1, '-'); // ''
还是那句话,以length
为基础,没有length
属性的,视为长度为0的数组。
Array.prototype.push
这个方法比较好玩:
1 | var obj1 = { |
可以看到obj1
里新增属性6
,值为'Dave'
,并且length
也更新为7
,这说明调用push
时会对原有对象进行修改。
我们可以利用这个特性,比如当我们需要一个obj1
的类数组副本时:
1 | var obj = { |
如果,没有传入length
呢?
1 | var obj1 = { |
这里的行为有些诡异,不过也更好地解释了以length为基础这句话:
没有length
的时候,认为数组长度为0
,并且会对obj
进行修改,把属性0的值改为Dave
.
那么,会举一反三的话,对于pop
, shift
和unshift
这三个方法的行为应该能想象得出来,就不再赘述了。
Array.prototype.reverse
1 | var obj1 = { |
reverse
的话,arr === obj1
Array.prototype.sort
1 | var obj1 = { |
sort
也一样,arr === obj1
Array.prototype.concat
concat
的表现就不是我们意料之中的了:
1 | var obj1 = { |
1 | var obj1 = { |
可以看到obj1
并不会改变,不会像push
一样会接着形成一个类数组的对象.
Array.prototype.splice
1 | var obj1 = { |
1 | var obj1 = { |
1 | var obj1 = { |
splice
的行为回归了,它现在对obj1
产生影响,并且是我们预计的样子
Array.prototype.every
1 | var obj1 = { |
Array.prototype.filter
1 | var obj1 = { |
Array.prototype.forEach
1 | var obj1 = { |
Array.prototype.map
1 | var obj1 = { |
Array.prototype.reduce
1 | var obj1 = { |