【问题标题】:doubts on javascript apply - function memoization关于javascript apply的疑惑——函数记忆
【发布时间】:2014-04-30 00:07:26
【问题描述】:

我正在为一本书上的一个 js 记忆示例而苦苦挣扎,代码如下:

Function.prototype.memoized = function(key){
    this._values = this._values || {};
    return this._values[key] !== undefined ? this._values[key] : this._values[key] = this.apply(this, arguments);
}

这里有一个 fiddle 的完整示例

我真正不明白的是这段代码是如何工作的以及它做了什么,尤其是apply 部分:

return this._values[key] !== undefined ? this._values[key] : this._values[key] = this.apply(this, arguments);

我知道并理解apply 的工作原理

apply() 方法使用给定的 this 值和作为数组提供的参数调用函数

假设this._values[key]等于undefined,那么返回值将是this.apply(this, arguments):这段代码会重新启动memoized函数吗?我尝试在函数中添加一些日志,以查看函数被调用了多少次,但它似乎只启动了一次..

谁能给我一个提示?这可能是一个愚蠢的问题,请耐心等待,谢谢

【问题讨论】:

    标签: javascript memoization


    【解决方案1】:

    让我们用一个简单的例子,斐波那契数。

    function fib(n) {
        if (n < 2) return 1;
        return fib.memoized(n-1) + fib.memoized(n-2);
    }
    

    这里我们可以看到memoized方法被应用在fib函数上,即你的this keyword指的是fib函数。它不会重新启动 memoized 函数,而是“启动”调用它的函数。但是,它确实在将this 设置为函数本身的情况下调用它,这没有任何意义。更好:

    Function.prototype.memoized = function(key){
        if (!this._values)
            this._values = {};
        if (key in this._values)
            return this._values[key];
        else
            return this._values[key] = this.apply(null, arguments);
    // pass null here:                            ^^^^
    }
    

    如果memoized 返回一个闭包会更好:

    Function.prototype.memoized = function(v) {
        var fn = this, // the function on which "memoized" was called
            values = v || {};
        return function(key) {
            if (key in values)
                return values[key];
            else
                return values[key] = fn.apply(this, arguments);
        }
    }
    var fib = function(n) {
        if (n < 2) return 1;
        return fib(n-1) + fib(n-2);
    }.memoized();
    // or even
    var fib = function(n) { return fib(n-1) + fib(n-2) }.memoized({0:1, 1:1});
    

    【讨论】:

    • 谢谢,您是真正的 javascript 忍者,IYKWIM。我想我现在明白了:当我打电话给isPrime.memoized(5)时,实际上我没有打电话给isPrime()(我完全错过了!),我打电话给isPrimememoized()函数:该函数检查密钥是否有过去已经处理过。如果是,则返回已处理的结果,否则启动isPrime(5)(正如您在this.apply(...) 中指出的this 是调用函数上下文,换句话说isPrime),将值存储在缓存中并返回值。这是正确的吗?
    • @thefourtheye 和 bergi 谢谢你们的干净解释
    【解决方案2】:

    备注

    1. 由于您将memoized 附加到Function.prototype,因此您只能在其他一些函数上调用此memoized。就像你的例子一样

      isPrime.memoized(5)
      
    2. 由于您在函数上调用memoized,因此this 将引用调用memoized 的函数。所以,在这种情况下,this 指的是isPrime


    实际解释

    this._values = this._values || {};
    

    这一行确保isPrime 有一个名为_values 的属性,并且它应该有一个空对象,如果它还没有的话。

    this._values[key] !== undefined
    

    这个检查是为了确保我们已经被key 调用过。如果值不是undefined,则返回this._values[key]

    否则,

    this._values[key] = this.apply(this, arguments)
    

    将调用this.apply(this, arguments)的结果存储在this._values[key]中并返回。现在是重要的部分。

    this.apply(this, arguments)
    

    这是直截了当的。 arguments 是一个类似对象的数组。所以,如果你真的像这样isPrime(1, 2, 3, 4) 调用了isPrime,参数将有{'0': 1, '1': 2, '2': 3, '3': 4}。现在我们在memoized 内部,我们需要调用isPrime,因为它本来是要被调用的。所以,this.apply(this, arguments) 完成了。 Function.prototype.apply,在调用函数时尝试像作为第二个参数传递的对象一样传播数组。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-03
      • 2011-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-22
      • 2022-01-21
      • 1970-01-01
      相关资源
      最近更新 更多