他们作用均为改变函数的this指向
function.call(thisArg, arg1, arg2, …)
func.apply(thisArg, [argsArray])
先来一个问题:说出下面代码产生的结果以及原因
参考 stackoverflow
let myArray = Array.apply(null, {length: 10}).map(Number.call, Number);
myArray //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
分析:
把上面式子拆成两个部分:
Array.apply(null, {length: 10}) //[undefined X 10],10个undefined的数组
和
.map(Number.call, Number)
大家知道,
Array(10) //[,,,,,,,,,]
Array.prototype.slice.call({length: 3, 0:1, 1:2, 2:3}) //[1, 2, 3]
关于类数组的使用还有很多
for (var i=0; i<arr.length; i++) {
console.log(arr[i]) // logs 1 .. 2 .. 3
}
Array.prototype.forEach.call({length: 3, 0: 1, 1: 2, 2: 3}, function(x) {
console.log(x) // 1 2 3
})
MDN:
从 ECMAScript 第5版开始,(apply第二个参数)可以使用任何种类的类数组对象,就是说只要有一个 length
属性和(0…length-1)范围的整数属性。例如现在可以使用 NodeList 或一个自己定义的类似 {‘length’: 2,
‘0’: ‘eat’, ‘1’: ‘bananas’} 形式的对象。
这里用apply,传递了一个只给长度的类数组,那么数组就会变成由10个undefined组成
再分析一下第二部分
.map(Number.call, Number)
map第一个函数是其回调callback,并且默认向回调函数传三个参数;第二个参数是改变callback函数内部的this指向
MDN-array-map
callback 函数会被自动传入三个参数:数组元素,元素索引,原数组本身。
var new_array = arr.map(function callback(currentValue[, index[,array]]) {
// Return element for new_array
}[, thisArg])
map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括undefined)组合起来形成一个新数组
由Number.call等同于 Function.prototype.call 上面可改成:
.map(function() {
return Function.prototype.call.apply(this,arguments)
},Number)
再改一下(将第二个参数Number替换掉this):
.map(function() {
return Number.call(arguments[0],arguments[1],arguments[2])
})
加上第一部分:
[undefined,undefined,...].map(function() {
return Number.call(arguments[0],arguments[1],arguments[2])
})
此时的arguments[0]为undefined, 大家知道:
Number.call(undefined,1) //1 => Number(1)
因为Number()只能接受一个参数,即arguments[2]被忽略
则有
[undefined,undefined,...].map(function() {
return Number(arguments[1]) //这里的第二个参数是数组索引(0,1,2,3.....)
})
最后自然结果[0,1,2,3,4,5,6,7,8,9]就出来!
如果你对js的方法足够熟悉了,这些也就不再是问题!
再来个数组扁平函数
函数出处: 你真的了解es6吗(二)
function flatten(arr) {
while(arr.some(res => Array.isArray(res))) {
arr = Function.apply.call([].concat, [], arr)
}
return arr
}
flatten([1,[2,[4,5]]]) //[1, 2, 4, 5]
建议自己先分析一波。
分析:
MDN-arr-some
arr.some(callback(element[, index[, array]])[, thisArg])
该方法作用是遍历数组,看是否存在至少一个满足callback里面的条件,存在即返回true,否则返回false
MDN-arr-concat
var new_array = old_array.concat(value1[, value2[, …[, valueN]]])
合并2+个数组,不改变原数组,返回新数组
提取关键部分
// 如果arr成员中存在数组,就一直执行while内部代码块
while(arr.some(res => Array.isArray(res))) {
arr = Function.apply.call([].concat, [], arr)
}
对 arr = Function.apply.call([].concat, [], arr) 做个简化
arr = [].concat.apply([], arr)
//为了便于理解再变一下
arr = Array.prototype.concat.apply([],arr)
//在变一下,结合上面实例
arr = [].cancat(arr[0],arr[1]) // arr[0]代表的是1+个arr的非数组类型成员,
//arr[1]则代表的是1+个arr的数组类型成员
当arr成员中存在数组时,就继续concat,
做个小演示:
Array.prototype.concat.apply([],[1,[2,[3,4]]]) //[1, 2, Array(2)]
var step1 = Array.prototype.concat.apply([],[1,[2,[3,4]]])
step1 //[1, 2, [3, 4]]
var step2 = Array.prototype.concat.apply([],s1)
step2 // [1, 2, 3, 4]
利用的就是以下两个特性
//1
[].concat(1,[1,2]) // [1,1,2]
//2
function f(){ this.name = 'xx'; }
f.apply(thisArg,arr) => f(arr[0],arr[1],...) { thisArg.name = 'xx'};
理解不对的地方还望各位看官不吝赐教
你想吃彩虹糖吗?我有长颈鹿,可以挤给你!