当我使用Array.prototype.slice时为什么要使用param thisArg?

时间:2016-06-12 10:20:58

标签: javascript

这里是代码:

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()的错误在哪里?

2 个答案:

答案 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.applythat.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的一个很好的实现:

  1. (您已在问题中解决此问题。) 它会创建两个implicit globalsargsthat,这是一个非常糟糕的主意。 janje suggests它可能是来自 The Good Parts 的Crockford curry的错误引用版本;如果是这样,;Array.prototype.sliceslice.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

  2. 调用原始函数时会阻塞this;相反,它应该使用与调用curried函数相同的Function.prototype

  3. 它正在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 }); })(); 上的所有其他方法都是不可枚举的,最好保持这种方式。

  4. 相反:

    "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函数时都不一定要创建一个新数组),但它们并没有真正买得多。