这里是代码:
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.apply
有一个null
参数,而slice.apply
没有一个?{/ p>
当我将其更改为args = slice.apply(null, arguments)
时,它抛出一个错误,上面写着:
未捕获的TypeError:调用null或undefined
的Array.prototype.slice
我对Function.prototype.apply()
的错误在哪里?
答案 0 :(得分:1)
.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
期望处理像对象这样的数组,但如果没有上下文则会失败。
答案 1 :(得分:1)
该函数中的slice.apply
和that.apply
调用具有不同的用途。
快速回顾一下:Function#apply
最多接受两个参数:在调用原始函数期间用作this
的值,以及任何具有参数的数组类对象(如果有的话) )传递给函数。
slice.apply
次来电,例如:
args = slice.apply(arguments);
...正在传递arguments
作为第一个参数,因此使用slice
调用this
来引用arguments
对象并且根本没有参数。这是将类数组的arguments对象转换为true数组的相当常见的习惯用法。 (在使用ES2015的现代代码中,我们可能会使用args = Array.from(arguments);
代替。)
that.apply
调用完全执行其他操作:调用调用curry
的函数对象,将提供给curry
的参数传递给它,然后传递curried函数时提供的参数实际上被称为。它传递null
作为第一个参数,即在调用期间用作this
的值,这意味着将使用this
调用原始函数来引用全局对象(如果这是松散模式)或null
(严格模式)。
不要嘲笑它,但如果它被正确引用,那就不是curry
的一个很好的实现:
(您已在问题中解决此问题。) 它会创建两个implicit globals: args
和that
,这是一个非常糟糕的主意。 janje suggests它可能是来自 The Good Parts 的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
:
this
调用原始函数时会阻塞this
;相反,它应该使用与调用curried函数相同的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");
示例:
{{1}}
可以进行优化(在每次调用curried函数时都不一定要创建一个新数组),但它们并没有真正买得多。