0
_.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?

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

thanks this really helped! Would JSON.stringify work better than array.prototype.slice?
JSON.stringify would solve the problem I mentioned in my "P.S." There could be performance implications depending on your use case, however, e.g. if you're calling JSON.stringify on many, many objects, or complex ones.
0

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 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.