WinchenzoMagnifico
WinchenzoMagnifico

Reputation: 3415

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; ????

Why does the second memoize implementation not work without the Array.prototype.slice.call?

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

Upvotes: 0

Views: 1114

Answers (2)

Paul Boutes
Paul Boutes

Reputation: 3315

The arguments is an object and not an array.

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

So when you run memoization with your second implementation, you will see this in your cached variable :

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

So it will always set and access to the key [object Arguments], due to the stringification.

So, you have to use the first implementation, by using the Array.prototype.slice.call which will convert to a real Array.

Then, after stringification you will get :

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

Upvotes: 0

Jordan Running
Jordan Running

Reputation: 106027

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

Close, but not quite. You're right that a difference in stringification is involved. arguments, as you may or may not know, isn't an Array, it's a special Arguments object. This is inconvenient for a few reasons, but the one that's relevant here can be illustrated thusly:

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
//    }

When an Arguments object is "stringified" to be used as a property name we always get [object Arguments]. If we want the property name to actually reflect the arguments themselves—which we need for memoization—we have to convert the Arguments object to an Array first, which is what Array.prototype.slice.call(arguments) does.

P.S. Even with Array.prototype.slice this memoization scheme has a big weakness. See what happens when you try it with arguments that are objects, or even arrays.

Upvotes: 1

Related Questions