当我使用Array.prototype.slice时,为什么应用参数thisArg被省略了



这里的代码是:

Function.prototype.curry = function() {
var slice = Array.prototype.slice,
args = slice.apply(arguments), // no thisArg ? arguments are the sec param [argsArray]
that = this;
return function() {
// thisArg: null
return that.apply(null, args.concat(slice.apply(arguments)));
}
}

以上就是我所理解的。那么,为什么that.applynull参数,而slice.apply没有呢?

当我把它改为args = slice.apply(null, arguments)时,它抛出了一个错误,上面写着:

未捕获类型错误:在null或未定义的上调用了Array.prototype.slice

Function.prototype.apply()哪里错了?

.apply为函数设置上下文和参数:

my_fn.apply({}, [1,2,3]);
function my_fn() {
console.log(this); // {}
console.log(arguments); // [1,2,3]
}

slice.apply(arguments);是一种将类似数组的对象转换为实际数组的破解方法,实际上它也可能是.call(arguments);,因为调用的工作方式几乎与.apply:类似

my_fn.call({}, 1,2,3); // <- no array but more arguments
function my_fn() {
console.log(this); // {}
console.log(arguments); // [1,2,3]
}

所以that.apply(null, ...只是没有为函数that设置上下文。而Array.prototype.slice希望处理类似数组的对象,如果没有上下文,则会失败。

该函数中的slice.applythat.apply调用有不同的用途。

简单回顾一下:Function#apply最多接受两个参数:在调用原始函数期间用作this的值,以及任何具有要传递给函数的参数(如果有)的类似数组的对象。

slice.apply调用,比如这个:

args = slice.apply(arguments);

正在传递arguments作为第一个参数,因此调用slicethis引用arguments对象,并且根本没有参数。这是一个相当常见的习惯用法,用于将类似数组的arguments对象转换为真正的数组。(在ES2015的现代代码中,我们可能会使用args = Array.from(arguments);。)

that.apply调用完全是在做其他事情:它调用调用curry的函数对象,向它传递提供给curry的参数,然后传递实际调用当前函数时提供的参数。它传递null作为第一个参数,该值在调用过程中用作this,这意味着原始函数将被调用,this引用全局对象(如果处于宽松模式)或null(处于严格模式)。


不要骑自行车,但如果引用正确,那就不是curry的好实现:

  1. (您已经在问题中解决了这个问题。)它创建了两个隐式全局:argsthat,这是一个非常糟糕的主意。janje认为这可能是《好的部分》中Crockford的curry的错误引用版本;如果是,则Array.prototype.slice之后和slice.apply(arguments)之后的;应该是,,而不是

    Function.prototype.curry = function() {
    var slice = Array.prototype.slice, // <== comma here
    args = slice.apply(arguments), // <== comma here
    that = this;
    return function() {
    return that.apply(null, args.concat(slice.apply(arguments)));
    };                                 // Crockford probably didn't leave this semicolon out
    };                                     // Or this one
    

  2. 它在调用原始函数时阻塞this;相反,它应该使用调用当前函数时使用的this

  3. 它正在Function.prototype上创建一个可枚举属性;Function.prototype上的所有其他方法都是不可枚举的,最好保持这种状态。

相反:

(function() {
var slice = Array.prototype.slice;
Object.defineProperty(Function.prototype, "curry", {
value: function() {
var originalFunction = this;
var args = slice.apply(arguments);
return function() {
return originalFunction.apply(this, args.concat(slice.apply(arguments)));
};
},
writable: true,
configurable: true
});
})();

示例:

"use strict";
// Define it
(function() {
var slice = Array.prototype.slice;
Object.defineProperty(Function.prototype, "curry", {
value: function() {
var originalFunction = this;
var args = slice.apply(arguments);
return function() {
return originalFunction.apply(this, args.concat(slice.apply(arguments)));
};
},
writable: true,
configurable: true
});
})();
// Demonstrate it
function foo() {
console.log("this.answer:", this && this.answer);
console.log("args:", arguments);
}
var obj = {answer: 42, foo: foo.curry("curried")};
obj.foo("supplied during call");

可以进行一些优化(不是严格意义上必须在每次调用curried函数时创建一个新数组),但它们并没有带来多大好处。

最新更新