Pulling parameters off a function passed as an argument - javascript

I am attempting to implement underscore's memoize function.
Memoize should be called as follows:
subtract = function(a, b) {
return a - b;
};
memoSub = _.memoize(subract);
memoSub(2, 1);
//1
Memoize should "remember" previous calls to its saved function, and rather than re-calculate (re-run the function), it should return those saved values.
I have the following structure set up, but the difficulty I'm having is understanding how I can access the passed-in function's parameters so that I can somehow save them, and thus check if they're the same as previously passed-in parameters.
Below is essentially an implementation of _.once, which I don't need to access the passed-in function's arguments for. I'm trying to modify it to make it into _.memoize
_.memoize = function(func) {
var called = false;
//i'd rather this be var storedArgs = {};
var result;
return function() {
//i'd rather be checking if these args have been seen before
if (!called) {
result = func.apply(this, arguments);
called = true;
//i'd rather save the computed results to the storedArg obj here
}
return result;
};
};
func.apply allows me to call a function and pass in the master arguments, i.e., everything after func in my function definition, but how then can I access the parameters/arguments of func? (In this case the parameters are (a, b) as seen above.
Edit: I suspect what I really need to do is somehow access the non-existent parameters here:
return function(/*how can I "capture" what will go here?*/) {...}

You already have your answer, the arguments object inside the inner function is exactly what you're trying to capture.
Both the inner and outer functions have an arguments variable but once you're in the inner function the outer variable is shadowed but the inner one.

You already have access to the arguments via arguments. What you need is a mapping of the arguments to the result. If the map contains an entry for the currently passed arguments, return the result. If not, compute it and store it.
Example:
var results = {};
return function() {
var key = Array.prototype.join.call(arguments);
if (!(key in results)) {
return results[key] = func.apply(this, arguments);
}
return results[key];
};
Note: This does a very simple serialization of the arguments and wouldn't work for complex arguments (objects).

Related

Passing a function as an object property

This is what I would like to do:
function genTxnId(id) {
return id;
}
var ws;
ws = new funcB('A', {evaluator: genTxnId(25)});
But I find that once it goes into funcB, the "evaluator" property of the passed in object is evaluated before going into the function. Is there anyway to keep it as genTxnId(25) until it is used within funcB.
I know that doing this would keep it as a function:
var funcA = function(b) { return b;
Would keep it as a function but then I won't be able to pass in argument b.
Using .bind(), you can bind the parameters of the function to specific values without calling it. .bind() will return a function that you can then call elsewhere, passing in the remaining arguments that haven't been bound.
function getTxnId (id) {
return id;
}
function funcB (str, obj) {
obj.evaluator();
}
var ws = new funcB('A', {
evaluator: genTxnId.bind(this, 25)
});
The one caveat is that bind takes as its first parameter the object to which this will be bound. This means that you can't depend on the value of this in getTxnId to refer to the newly created object, which may seem more intuitive (albeit impossible, to my knowledge). But in general, you won't need to do that, so you can pass in any old object. You could even pass null if you won't ever use this inside that function, i.e. getTxnId.bind(null, 25).

Verifying my understanding of the scope chain

(Question 1)
In Flanagan's JS Definitive Guide, he defines the Function method bind() in case it's not available (wasn't available n ECMAScript 3).
It looks like this:
function bind(f, o) {
if (f.bind) return f.bind(o); // Use the bind method, if there is one
else return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
He illustrates the use of it with an example (which I have modified to change the 3rd line from f.bind(o)):
function f(y) { return this.x + y; } // This function needs to be bound
var o = { x : 1 }; // An object we'll bind to
var g = bind(f, o); // Calling g(x) invokes o.f(x)
g(2) // => 3
When I first saw this, I thought "Wouldn't arguments refer to the arguments variable within the bind function we're defining? But we want the arguments property of the function we eventually apply it to, like g in the example above..."
I verified that his example did indeed work and surmised that the line return f.apply(o, arguments) isn't evaluated until var g = bind(f, o) up above. That is, I thought, when you return a function, are you just returning the source code for that function, no? Until its evaluated? So I tested this theory by trying out a slightly different version of bind:
function mybind2(f, o) {
var arguments = 6;
return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
If it's simply returning tbe unevaluated function source, there's no way that it stores arguments = 6 when later evaluated, right? And after checking, I still got g(2) => 3. But then I realized -- if it's just returning unevaluated code, how is the o in return f.apply(o, arguments) getting passed?
So I decided that what must be happening is this:
The o and the arguments variables (even when arguments equals 6) are getting passed on to the function. It's just that when the function g is eventually called, the arguments variable is redefined by the interpreter to be the arguments of g (in g(2)) and hence the original value of arguments that I tried to pass on was replaced. But this implies that it was sort of storing the function as text up until then, because otherwise o and arguments would have simply been data in the program, rather than variables that could be overwritten. Is this explanation correct?
(Question 2) Earlier on the same page, he defines the following function which makes use the apply method to trace a function for debugging:
function trace(o, m) {
var original = o[m]; // Remember original method in the closure.
o[m] = function() { // Now define the new method.
console.log(new Date(), "Entering:", m); // Log message.
var result = original.apply(this, arguments); // Invoke original.
console.log(new Date(), "Exiting:", m); // Log message.
return result; // Return result.
};
}
Wouldn't the this here refer to the function that we're defining, rather than the object o? Or are those two things one and the same?
Question 1
For your first question, let's simplify the example so it's clear what being done:
function bind(func, thisArg) {
return function () {
return func.apply(thisArg, arguments);
};
}
What happens here is that a closure is created that allows the access of the original function and the value of this that is passed. The anonymous function returned will keep the original function in its scope, which ends up being like the following:
var func = function () {};
var thisArg = {};
func.apply(thisArg, [/*arguments*/]);
About your issue with arguments, that variable is implicitly defined in the scope of all functions created, so therefore the inner arguments will shadow the outer arguments, making it work as expected.
Question 2
Your problem is to your misunderstanding of the late binding of this -- this is one of the more confusing things about JavaScript to people used to more object-oriented languages that also have their own this keyword. The value of this is only set when it is called, and where it is called defines the value of this when it is called. It is simply the value of the parent object from where it is at the time the function is called, with the exception of cases where the this value is overridden.
For example, see this:
function a() {return this;};
a(); // global object;
var b = {};
b.a = a;
b.a(); // object b
If this was set when the function was defined, calling b.a would result in the global object, not the b object. Let's also simplify what happens with the second function given:
function trace(obj, property) {
var method = obj[property]; // Remember original method in the closure.
obj[property] = function () { // Now define the new method.
console.log(1); // Log message.
// Invoke original method and return its result
return original.apply(this, arguments);
};
}
What happens in this case is that the original method is stored in a closure. Assigning to the object that the method was originally in does not overwrite the method object. Just like a normal method assignment, the principles of the this value still work the same -- it will return the parent object, not the function that you've defined. If you do a simple assignment:
var obj = {};
obj.foo = function () { return this; };
obj.foo(); // obj
It does what was expected, returns the parent object at the time of calling. Placing your code in a nested function makes no difference in this regard.
Some good resources
If you'd like to learn more about writing code in JavaScript, I'd highly recommend taking a look at Fully Understanding the this Keyword by Cody Lindley -- it goes into much more detail about how the this keyword behaves in different contexts and the things you can do with it.
But this implies that it was sort of storing the function as text up until then, because otherwise o and arguments would have simply been data in the program, rather than variables that could be overwritten. Is this explanation correct?
No. this and arguments are just special variables which are implicitly set when a function is executed. They don't adhere to normal "closure rules". The function definition itself is still evaluated immediately and bind returns a function object.
You can easily verify this with:
var g = bind(f, o);
console.log(typeof g);
Here is a simpler example which doesn't involve higher order functions:
var arguments = 42;
function foo() {
console.log(arguments);
}
foo(1, 2);
I think you see that the definition of foo is evaluated like you'd expect. Yet, console.log(arguments) logs [1, 2] and not 42.
Wouldn't the this here refer to the function that we're defining, rather than the object o? Or are those two things one and the same?
this never refers to the function itself (unless you explicitly set it so). The value of this is completely determined by how the function is called. That's why this is often called "the context". The MDN documentation provides extensive information about this.
Reading material:
MDN - this
MDN - arguments

If I push a function call into an array or assign as a property does it push/assign the returned value?

Example:
function SomeCall(){
return "Bill";
}
var a = [];
a.push(SomeCall());
var obj = {
name: SomeCall()
};
//is function CALLED here or does this already contain "Bill"?
console.log(obj.name);
console.log(a[0]); //what about here?
Now when I access the value of the object or the item in the array, is it calling the function at that time and giving me the value? Or is it calling the function at the time of the push/assignment and holding the return value?
The function is called right when the () is used. So the array contains the return value of the function. If you want to store the function itself, just use a.push(SomeCall) and call it later (a[0]()). In case you always want to call it with certain params, store an anonymous function containing the call:
a.push(function() {
return SomeCall(your, params);
});
However, in modern JS engines (i.e. not in "old" IE versions) you can define accessors however. This allows you to execute code when a certain property is read (or written):
var obj = {};
obj.__defineGetter__("name", SomeCall);
Then any read access to obj.name will return the result of SomeCall().
JavaScript is a non-lazy language, so when you code a call to a function into a call to another function, the effect is that the result of the inner function call is passed to the outer function.
Thus:
a.push(SomeCall());
is effectively like:
var temp = SomeCall(); // call function, get result
a.push(temp);
Similarly, in an object literal expression the right-hand sides of the property declarations are evaluated, and the results of the evaluations are the values of the properties.

Passing an array of arguments

I'm trying to have a function that is passed a function and calls it with a set of args. For example:
function foo(func, args) {
return func(args);
}
However, I don't know how many elements might be in args, and func should be able to be an arbitrary function that takes any number of args. How do I deal with this?
Also say I wanted to store the functions passed to foo and only call them if they hadn't been called before.
Can I just do something like:
var calledFuncs = [];
function foo(func, args) {
if(calledFuncs.indexOf(func) === -1) {
calledFuncs.push(func);
return func(args);
}
}
Thanks, I'm a bit new to functional programming in JavaScript.
You're looking for func.apply:
the first argument is the context aka this value. You don't want to change this, so pass it through.
the second argument is an array containing the arguments, which can of course be of dynamic length.
So you could do this:
return func.apply(this, args);
You seem to have a fine approach to your second issue (with the func(args) replaced). You may want to store the return value of called functions, though, since foo now returns undefined for any calls except the first.
Use apply:
func.apply(this, args);
apply takes an array of arguments as the second argument.
The first argument to apply will be the value of this in the scope of the function that you are calling. So you can pass in the current this or anything else that you want.
As far as your second question goes, that will only work for named functions. You can use func.name to store the function name in your array:
var calledFuncs = [];
function foo(func, args) {
if(calledFuncs.indexOf(func.name) === -1) {
calledFuncs.push(func.name);
return func(args);
}
}
This won't work for anonymous functions and it doesn't work for named functions in IE or Opera. You're going to have to parse it out, perhaps like so:
var name = func.toString().replace(/\n/g, "").replace(/\s*\(.*$/, "").replace(/^\s*function\s+/, "");
As far as your second question goes, you can do what you're doing right now. But I don't think it would work for the following case:
foo(function() {
}, []);
foo(function() {
}, []);
It will call both of those functions.
You want Function.prototype.apply:
func.apply(this,args);
Set the context (the first argument) to whatever you want, including null to get the window global (as you would get with your current func(...) invocation.
Although not directly related to your question, see also the related Function.prototype.call method that allows you to similarly set the context, but pass explicit parameters.

Calling a JavaScript Function With Arguments That Are Decided Later

I want to store a pointer to a function and then call it later with some arguments, but I'm having trouble with how to pass the arguments to the function.
For example:
var MyObject = (function () {
return {
myMethod: function (a, b) {
return a + b;
}
};
}());
var method = MyObject.myMethod;
var args = [2, 5];
method(args);
So in the last line, which I know doesn't do what I want method(args), I want to essentially call MyObject.myMethod(2, 5), so my array attempt fails and creating an object also fails. How do I pass in the stored arguments to the stored method?
you can use apply which accepts an array of arguments as an argument
var myarray = [2,5,...] //your array of arguments
method.apply(this,myarray); //execute your method
and in the method you have, you can receive the arguments via the "hidden" "pseudo-array" arguments argument.
function method(){
var args = arguments; //is [2,5,...]
}
this has advantage over call since you can pre-build the argument array and don't have to enumerate the arguments in the call's ()
K. Scott Allen has a great old article on this kind of thing. Use call or apply as a way to affect the value of this inside the called function.
http://odetocode.com/blogs/scott/archive/2007/07/05/function-apply-and-function-call-in-javascript.aspx

Categories

Resources