This question already has an answer here:
Bind variables to callback function
(1 answer)
Closed 6 years ago.
I would like to pass a callback function (that contains parameters) to an object and ensure that it is invoked using specific argument values. I'd like to do this in such a way that these values do not need to be passed in as well. Is this possible?
Here is an (unusable) example of what I'm trying to do:
this.object = new Object(callback(val)); //val is the value I'd like the object to use
The callback could be one of a variety of functions with unique parameters. As such, I'd like to refrain from having to pass in the values separately.
An example:
//Instances of the same object with different callbacks
this.object1 = new Object(callback(val1, val2));
this.object2 = new Object(callback(val));
this.object3 = new Object(callback(val1, val2, val3));
As you can see, it is not feasible to pass in the additional arguments.
I could pass the callback in like this:
this.object = new Object( function() { this.callback.call(val) } );
...but since the callback is outside the scope of Object, it requires me to either pass a reference to the caller to access it, or access the caller in some other manner.
What is my best course of action?
Update:
I am creating a MenuSystem with Buttons. Each Button functions differently according to given callback methods - the primary function being for click events. An object exists in MenuSystem that encapsulates all the different functionality (methods) a button can possibly use. These methods are what's passed in as callbacks to the buttons. The Button may need to "own" the method if it modifies its own attributes. This will require a function.call()
TL;DR:
MenuSystem has [ UIMethods + UIElement ]
UIElement uses [ UIMethods.someMethod() ] for functionality purposes
someMethod() may modify attributes of UIElement which requires function.call()
this.object = new Object(function() { this.callback(val) }.bind(this));
That will bind the scope so this refers correctly.
This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 8 years ago.
// example A (works)
foo(function() {
myObj.save();
});
// example B (doesn't work)
foo(myObj.save);
// example C (works)
foo(myObj.save.bind(myObj));
Why is the this reference correct when myObj.save is called in example A, but not in example B?
I can force this to be correct by using bind, but what is happening in example A that is different from example B?
I don't understand why this would be different.
Functions only have a context, this, when invoked.
When you call x.y(), the this context inside y is x.
When you write x.y, you're referencing just the function y, you're not invoking it and there is no context. When you pass that function elsewhere, such as z = x.y, the context is not passed with it.
You're doing that in your second example. You're passing the function without context into foo, where there is no possible way for foo to know what the context should be. When it's invoked by foo, it's going to be invoked a simple save() call, and when this happens the this context will be window (or null in strict mode).
This is because this refers to the object on which a method was called. When you set the method of a function to a variable, or pass it in as an argument, it is no longer associated with the object it was a method of.
Multiple objects can share the same function, this is a way of dynamically reusing that function to manipulate the object instance it is a method of.
The bind method was added later to address use cases such as these.
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 8 years ago.
Problem:
I have a very simple snippet of code.
I create and object with a member variable "counter" and a member function "start()" which increases the counter variable by 1 every second.
The problem here is that the counter variable does not increase when start() acts on it.
This has been driving my head crazy.
Hypothesis:
I believe this is a problem with the "this" keyword or a scope/closure issue but I'm not sure what's exactly wrong.
Code:
var livePlayer = {
counter : 0,
liveIteration: null,
start: function(){
this.liveIteration = setInterval(this.incCounter, 1000);
},
incCounter: function(){
this.counter++;
alert(this.counter); <-- this should return 0, 1, 2, etc. but returns NAN instead
}
};
livePlayer.start();
JSFiddle:
http://jsfiddle.net/justinwong12337/1wjdr0dh/
Your help is greatly appreciated!
Additional info:
This object and its members are part of an AngularJS service and are to be used by a separate controller file.
The problem is neither with scope or closures. :-) It's with this, which is a slippery concept in JavaScript.
In JavaScript, this during a function call is set almost entirely by how the function is called, not where the function is defined. It's basically a special form of function argument. (This is quite different to its meaning in other languages with similar syntax.) The particular problem in your case is here:
this.liveIteration = setInterval(this.incCounter, 1000);
When the browser's timer code calls your incCounter, it will do so with this set to the global object, not to your object. So this.counter isn't the counter property on your object (because this doesn't refer to your object), and things don't work as expected.
You can solve this several ways, probably the most direct is ES5's Function#bind (which can be shimmed on older browsers):
this.liveIteration = setInterval(this.incCounter.bind(this), 1000);
Function#bind returns a function that, when called, calls the original function with this set to the first argument you give it.
More to explore (on my blog):
Mythical methods
You must remember this
This question already has answers here:
Javascript call() & apply() vs bind()?
(24 answers)
Closed 9 years ago.
var obj = {
x: 81,
getX: function() {
console.log( this.x)
}
};
var getX = obj.getX.bind(obj);//use obj as 'this';
getX();//81
var getX = function(){
obj.getX.apply(obj);
}
getX();//also 81
The use of bind and call/apply look very similar, I want to know what's the difference between them.The two getX Function above is the same?
bind returns a function which will act like the original function but with this predefined. It is usually used when you want to pass a function to an event handler or other async callback.
call and apply will call a function immediately letting you specify both the value of this and any arguments the function will receive.
Your second example defines an anonymous function which calls apply. This is a common pattern; bind provides a standard implementation of that which allows you to do it with a simple function call (thus being quicker and easier to write).
.call() - calls the same function with the specified arguments
.apply() - calls the same function with the arguments specified in an array
.bind() - creates a new function with the same function body, with a preset value of this (the first argument) and returns that function.
In all cases, the first argument is used as the value of this inside the function.
The difference is how you make the call. If you've used bind to get back a function with a bound this value, you just call the function:
getx();
If you don't have a bound function, and you want to set this, you do so with call or apply:
someFunction.call(objectToUseAsThis, arg1, arg2);
// or
someFunction.apply(objectToUseAsThis, [arg1, arg2]);
Note that if you have a bound function (like your getX), using call on it is pointless, because the this you supply will just get overridden by the bound this. (Using apply might still be useful, if you have an array of values you want to ass as arguments.)
thank you if you can help. Source of code http://ejohn.org/apps/learn/#84
1) in line 3 of the program below, where it says return context[name] what does this mean? Im guessing that it means name is bound to the context as a result of the apply function? Is that correct?
2)If my guess in 1 is correct, why does it use the [] brackets? Is that just the syntax. When I look at it, it makes me think array or object?
3) When it says apply(context, arguments) is arguments not the same as name or is arguments both context and name together? to put it another way, in the language of the call bind(Button, "click") is arguments only "click" or is it both button and click?
4) I tried to rewrite line 3 by substituting name for arguments like this
return context[name].apply(context, name);
but it didn`t work anymore, which raises the questions
a)if it is returning name bound to context (i.e. context[name]), why isn`t it sufficient to just have apply(context,name)?
b) if arguments includes both name and context, is the third line of the function essentially
return context[name].apply(context, [context, name]);
c) if my assumption in 4(b) is correct, why would we effectively have to have context passed twice in order to bind name to context? which is to say, I dont understand why line 3 doesnt work if you just write apply(context, name) instead of apply(context,arguments)
function bind(context, name){
return function(){
return context[name].apply(context, arguments);
};
}
var Button = {
click: function(){
this.clicked = true;
}
};
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = bind(Button, "click");
document.getElementById("results").appendChild(elem);
elem.onclick();
assert( Button.clicked, "The clicked property was correctly set on the object" );
Click me!
It may be helpful to understand the basics of JavaScript objects before diving into the specifics. Any JavaScript property can be accessed with the bracket notation, or the dot notation (if it is a valid identifier). It can be confusing since arrays also use this notation. Say there is an object of cars and their makes,
var cars = { Ford: 2007, Honda: 2010, BMW: 2011 };
Then we can access their keys using the dot notation or the bracket notation
cars.Ford // 2007
cars["Honda"] // 2010
Next, remember that functions are first class citizens in JavaScript. So you could use them as ordinary variables including storing them as object property values, in arrays, etc. Let's replace the years in the previous cars example with actual functions,
var cars = {
Ford: function() { alert("2007"); },
Honda: function() { alert("2010"); },
BMW: function() { alert("2011"); }
};
The keys Ford, Honda, and BMW can still be accessed as in the previous example with the dot or bracket notation, with the only difference that this time a function will be returned instead of the integer year.
cars["BMW"] now returns a function which can be directly invoked as
cars["BMW"](); // or
cars.BMW(); // or
var name = "BMW";
cars[name]();
That's not all. There are still two more ways to execute a function - through apply and call. The difference between apply and call is subtle but you should read up more about them.
Finally, arguments represents an array-like object that contains the arguments passed in to a function. This is best demonstrated by an example,
function whatever() {
console.log(arguments);
}
whatever(1, 2); // [1, 2]
whatever("foo", "bar", 23, [4, 6, 8]); // ["foo", "bar", 23, [4, 6, 8]]
whatever(); // undefined
Without giving any names to the function parameters, we were able to log all the arguments passed to the function. Since it is an array like object, access each argument individually as arguments[0], arguments[1], etc.
And now to answer your questions,
1) in line 3 of the program below, where it says return context[name] what does this mean? Im guessing that it means name is bound to the context as a result of the apply function? Is that correct?
context[name] is similar to the cars['Ford'] example above. It is supposed to give a function which is then invoked by calling apply on it. When that function is called, inside the function this will refer to the object - context.
2) If my guess in 1 is correct, why does it use the [] brackets? Is that just the syntax. When I look at it, it makes me think array or object?
Hopefully this was answered above.
3) When it says apply(context, arguments) is arguments not the same as name or is arguments both context and name together? to put it another way, in the language of the call bind(Button, "click") is arguments only "click" or is it both button and click?
arguments has nothing to do with either context or name. It is simply a list of the arguments/parameters that the function was called with. Hopefully the above description cleared this as well.
4) I tried to rewrite line 3 by substituting name for arguments like this
return context[name].apply(context, name);
but it didn`t work anymore
It didn't work because apply expects the second argument to be an Array, and you passed it a String. Try return context[name].apply(context, [name]); instead.
which raises the questions
a) if it is returning name bound to context (i.e. context[name]), why isn`t it sufficient to just have apply(context,name)?
b) if arguments includes both name and context, is the third line of the function essentially
return context[name].apply(context, [context, name]);
arguments has nothing to do with the context, or name. Hopefully this was cleared up in the above examples.
c) if my assumption in 4(b) is correct, why would we effectively have to have context passed twice in order to bind name to context? which is to say, I dont understand why line 3 doesnt work if you just write apply(context, name) instead of apply(context,arguments)
The above answers already answer this part.
1) context[name] just means the property of the "context" object with that name. In the case of:
bind(Button, "click");
that works out to Button["click"], which is the click() function inside the Button object
2) All objects in Javascript are a collection of properties, which can be accessed by their names. Given the definition:
var Button = {
click: function(){
this.clicked = true;
}
};
both Button.click and Button["click"] would refer to the same thing - the function click() inside the Button object.
3) The arguments keyword refers to an array-like object containing all of the arguments passed to a function. In the example, bind() is returning a newly-created function. The "arguments" referred to in that function are whatever arguments that function gets called with. In this case, it's neither context nor name, it's whatever the onclick mechanism passes to the event handler.
Here's a slightly different way to write the code that sets up the event handler:
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
var boundFunction=bind(Button, "click");
elem.onclick=boundFunction;
document.getElementById("results").appendChild(elem);
Maybe this makes it more clear that when you call bind(), it returns a new function. If you were to call the boundFunction like this:
boundFunction("these", "are", "arguments")
The use of arguments is inside the returned function, so arguments would be ["these", "are", "arguments"] in this case. The arguments that were passed to "bind" are used to construct the function that bind returns, so they're no longer relevant when the bound function gets called.
4) Until you understand the basics of how returning a function from another function works, this'll be pretty confusing. The purpose of apply() is to set the "this" keyword for a particular function invocation. Given the definition of Button, you might expect to be able to do this to set up the event handler:
elem.onclick = Button.click;
This doesn't work correctly, because when the event handling code calls the Button.click function, "this" is set to the global context, rather than to the Button instance. The purpose of the bind() function is to make a function that sets "this" appropriately, then calls the function you originally passed to bind().
I have a half-completed blog entry on this which might be a simpler example:
http://codemines.blogspot.com/2010/01/javascript-by-example-functions-and.html