Function.prototype的apply&call(.apply.call())

他们作用均为改变函数的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'};

理解不对的地方还望各位看官不吝赐教

你想吃彩虹糖吗?我有长颈鹿,可以挤给你!

发布了15 篇原创文章 · 获赞 3 · 访问量 3436

猜你喜欢

转载自blog.csdn.net/qq_39370934/article/details/103633484