将参数记忆为键

memoize arguments as keys

_.memoize = function(func) {
    var cached = {};
    return function() {
      var args = Array.prototype.slice.call(arguments);
      if (cached[args]) { 
        console.log('returning cached');
        return cached[args];
      } else {
        cached[args] = func.apply(this, args);
        return cached[args];
      }  
    };
  };

  _.memoize = function(func) {
    var cached = {};
    return function() {
      if (cached[arguments]) { 
        console.log('returning cached');
        return cached[arguments];
      } else {
        cached[arguments] = func.apply(this, arguments);
        return cached[arguments];
      }  
    };
  };

  var add = function(a, b) {
    return a + b;
  };

  var memoAdd = _.memoize(add);

  memoAdd(1, 2)  => 3;

  memoAdd(3, 4)
   'returning cached'
        => 3; ????

为什么第二个 memoize 实现在没有 Array.prototype.slice.call 的情况下无法工作?

括号符号实际上是将单词参数字符串化为键而不是实际的实参数吗?

is the bracket notation actually stringifying the word arguments as a key instead of the actual real arguments?

接近,但不完全是。你是对的,涉及字符串化的差异。 arguments,你可能知道也可能不知道,它不是一个数组,它是一个特殊的Arguments object。由于一些原因,这很不方便,但可以这样说明与此处相关的原因:

function argsObj() {
  var obj = {};
  var args = Array.prototype.slice.call(arguments);

  obj[arguments] = 1;
  obj[args] = 2;

  return obj;
}

console.log(argsObj(123, 456, 789));
// => { "[object Arguments]": 1,
//      "123,456,789": 2
//    }

当 Arguments 对象 "stringified" 用作 属性 名称时,我们总是得到 [object Arguments]。如果我们希望 属性 名称实际反映参数本身——我们需要记忆——我们必须首先将 Arguments 对象转换为数组,这就是 Array.prototype.slice.call(arguments) 所做的。

P.S。即使 Array.prototype.slice 这个记忆方案也有很大的弱点。看看当您尝试使用对象甚至数组作为参数时会发生什么。

arguments 是一个 对象 而不是 数组

The arguments object is not an Array. It is similar to an Array, but does not have any Array properties except length.

因此,当您使用第二个实现 运行 记忆时,您会在 cached 变量中看到:

Cached = {} //Empty cache
(index):56 Arguments = [object Arguments] //Arguments is an object
(index):57 Is Array = false //Not an array
(index):74 3
//Will set the object as key, not an array
(index):55 Cached = {"[object Arguments]":3} 
(index):56 Arguments = [object Arguments]
(index):57 Is Array = false
//because it will still access to cached[[object ...]]
(index):59 returning cached
(index):76 3

因此它将始终设置和访问密钥 [object Arguments],因为 stringification

因此,您必须使用第一个实现,使用 Array.prototype.slice.call 将转换为真正的数组。

然后,在字符串化之后你会得到:

Cached = {}
(index):39 Arguments = 1,2 //Is an array as you can see
(index):40 Is Array = true
(index):74 3
//Set an array as key
(index):38 Cached = {"1,2":3}
//Key 3,4 is not in the cached variable, you can continue
(index):39 Arguments = 3,4
(index):40 Is Array = true
(index):76 7