I have some javascript code that takes a function and invokes it again:
var obj = {
// returns the function with prevent default prepended.
run: function(functor, context){
return function(e){
e.preventDefault();
context.call(functor, e);
};
}
}
var myContext = this;
var returnedFunction = obj.run(function(e){alert(e.target)}, myContext);
var returnedFunction(...);
Here is the problem:
On the line where it says context.call(functor, e);
context is always null because the function has gone out of scope.
What do I have to do inside that function so that I can use context?
context has not gone out of scope -- it's probably just null to begin with (and even if it weren't, it likely wouldn't have a call method).
Instead, you probably meant to use functor.call(context, e).
Functions have a call method, which accepts a this/context argument and variable-length following arguments. Objects in JavaScript generally don't have a call method that accepts a function and variable-length following arguments (unless you've added such a method and expect context to have it).
Related
The following successfully prints 'foo'.
var obj = {
name: 'foo',
printName: function printName() {
console.log(this.name);
}
};
var printButton= document.getElementById('printIt');
printButton.addEventListener('click', function(){
obj.printName();
});
The following doesn't, however:
printButton.addEventListener('click', obj.printName() );
I know the solution... simply use bind so that we're referencing the obj object. i.e:
printButton.addEventListener('click', obj.printName.bind(obj) );
Why then don't we need to use bind in the first example. I don't know why wrapping obj.printName() function call in the anonymous function results in the console.log correctly referencing and printing this properly, but when called directly after click, you needs to use bind
Alright, I commented with some good information on this question so I might as well answer!
Functions are first class
Okay, let's starts with some fundamentals of javascript that is very dissimilar to some other programming languages: in javascript functions are first class citizens--which is just a fancy way of saying that you can save functions into variables and you can pass functions into other functions.
const myFunction = function () { return 'whoa a function'; }
array.map(function () { return x + 1; });
And because of this wonderful feature, there is a big difference between the expressions:
Expression 1
obj.printName
and
Expression 2
obj.printName();
In expression 1: the function isn't being invoked so the value of the expression is of type function
In expression 2: the function is being invoked so the value of the expression is what the function returns. In your case, that's undefined
addEventListener
The method addEventListener takes in two arguments:
a string of the type of event
a function that will be run when the event fires.
Alight, so what does that mean?
When you call
// doesn't work
printButton.addEventListener('click', obj.printName() );
you're not passing a value of type function to the addEventListener method, you're actually passing undefined.
// works
printButton.addEventListener('click', obj.printName.bind(obj) );
then works (for one reason) because the second argument is actually of type function.
What does bind do? Why does it return a function?
Now we need to discuss what bind actually does. It related to the pointer* this.
*by pointer, I mean a reference identifier to some object
bind is a method that exists on every function object that simply binds the this pointer of a desired object to the function
This is best shown by an example:
Say you have a class Fruit that has a method printName. Now that we know that you can save a method into a variable, let's try that. In the example below we're assigning two things:
boundMethod which used bind
unboundMethod that didn't use bind
class Fruit {
constructor() {
this.name = 'apple';
}
printName() {
console.log(this.name);
}
}
const myFruit = new Fruit();
// take the method `printName`
const boundMethod = myFruit.printName.bind(myFruit);
const unboundMethod = myFruit.printName;
boundMethod(); // works
unboundMethod(); // doesn't work
So what happens when you don't call bind? Why doesn't that work?
If you don't call bind in this case, the value of the function that gets stored into the identifier unboundMethod can be thought to be:
// doens't work
const unboundMethod = function() {
console.log(this.name);
}
where the contents of the function is the same contents of the method printName from the Fruit class. Do you see why this is an issue?
Because the this pointer is still there but the object it was intended to refer to is no longer in scope. When you try to invoke the unboundMethod, you'll get an error because it couldn't find name in this.
So what happens when you do use bind?
Loosely bind can be thought of as replacing the this value of function with the object you're passing into bind.
So if I assign: myFruit.printName.bind(myFruit) to boundMethod then you can think of the assignment like this:
// works
const boundMethod = function() {
console.log(myFruit.name);
}
where this is replaced with myFruit
The bottom-line/TL;DR
when to use bind in an Event Handler
You need to use Function.prototype.bind when you want to replace the thises inside the function with another object/pointer. If your function doesn't ever use this, then you don't need to use bind.
Why then don't we need to use bind in the first example?
If you don't need to "take the method" (i.e. taking the value of type of function), then you don't need to use bind either Another way to word that is: if you invoke the method directly from the object, you don't need bind that same object.
In the wrapper function, you're directly invoking the method of the object (as in expression 2). Because you're invoking the method without "taking the method" (we "took" the methods into variables in the Fruit example), you don't need to use bind.
printButton.addEventListener('click', function(){
// directly invoke the function
// no method "taking" here
obj.printName();
});
Hope this helps :D
Note: You need to call printButton.addEventListener('click', obj.printName() ); without parenthesis in obj.printName() since you want to pass the function.
The answer lies in the way this is bound in Javascript. In JS, the way a function is called decides how this is bound. So when you provide the callback function like below:
printButton.addEventListener('click', function(){
obj.printName();
});
Notice, printName is being called via dot notation. This is called implicit binding rule when this is bound to an object before dot, in this case obj. Clearly in this case, you get the expected output.
However, when you call it like this:
printButton.addEventListener('click', obj.printName );
Notice that, all you are passing is the address of the function that is inside obj. So in this case info about obj is lost. In other words, the code that calls back the function doesn't have the info about obj that could have been used to set this. All it has is the address of the function to call.
Hope this helps!
EDIT:
Look at this crude implementation I call bind2 that mimics native bind. This is just to illustrate how native bind function returns a new function.
Function.prototype.bind2 = function (context) {
var callBackFunction = this;//Store the function to call later
return function () { //return a new function
callBackFunction.call(context);//Later when called, apply
//context, this is `obj` passed
//in bind2()
}
};
function hello() {
alert(this.name);
}
obj = {
name:'ABC'
};
var f = hello.bind2(obj);
f();
Notice: How function f() is hard bound here. f() has hard bound this with obj. You cannot change this to other than obj now. This is another thing with bind that probably will help you knowing.
I have an object which has two functions within it, and as I guessed each one has a different value for this:
custom_controls : {
play_pause : function () {
console.log(this); // object
videoPlayer.getIsPlaying(function (video_is_playing) {
if (video_is_playing) {
console.log(this); // window
videoPlayer.pause(true);
} else {
videoPlayer.play();
}
});
}
},
Then the function is invoked like this:
custom_controls.play_pause()
I've heard that the way you invoke a function denotes the value of this.
So my question is: What is happening here? What kind of function invocations am I using? And how does each one affect this?
When calling obj.func(), this inside the function will be equal to obj. If there is no obj, the global object (window) is used instead. Or if you are running in Strict Mode, undefined is used.
The first log is your object because you call the function like this:
custom_controls.play_pause() // custom_controls will be 'this'
The second log is window because the function passed as parameter to getIsPlaying is not called with any this:
videoPlayer.getIsPlaying = function(callback) {
callback(); // this inside callback will be window
}
You can control what the value of this will be when you invoke a function by using call or apply. You can create a new function which will always have the this value set to whatever you want by using the bind function:
videoPlayer.getIsPlaying(function (video_is_playing) {
if (video_is_playing) {
console.log(this); // my obj
videoPlayer.pause(true);
} else {
videoPlayer.play();
}
}.bind(this)); // magic!
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Each function is actually executed within a context. That context is denoted as the current this for which you call the function.
Given your code:
If you call custom_controls.play_pause() you are saying "Take the field of the object custom_controls named play_pause and execute it within the context of the object custom_controls".
Later on calling videoPlayer.getIsPlaying() means pretty much the same. Except you're giving it a callback function. How that callback function is executed later on depends on how videoPlayer.getIsPlaying is implemented.
If I have to guess I'd say that getIsPlaying has a callback.call(window, video_is_playing) somewhere in it.
call is a method of all function objects in javascript.
There are a few ways to work around this "issue" if you want to reference a this in some callback.
var self = this;
call_me_maybe(function() {
console.log(this); //the this that call_me_maybe chose to call your function with
console.log(self); //the this from the upper scope
});
or if you don't care about the object in which context call_me_maybe will call your function:
call_me_maybe((function(){
console.log(this); //the this from the upper scope
}).bind(this));
What bind does is it returns a wrap[per of the function which will always be called in the context of the object to which it is bound.
bind can also bind arguments as well as the this object for the function, creating a sort of curry.
Your first this is referring to play_pause.
Your second this could either be referring to the Window or your videoPlayer object. In JavaScript, closures and regular functions are 'generally' attached to window, and calling this returns window. In certain cases, e.g. if you attach a function to the click handler of an HTML element, this refers to the element...
element.onclick = function(){
this // -> element
}
But generally if you just create a function(), or have an anonymous one like yours this refers to window.
function hello(){
this // -> window
}
The this that you've discovered is the object is what you would expect because the function is operating on that object.
I'm not familiar with your videoPlayer, but as the value of this is the "window", I would imagine that either A the video player is a function of the browser itself, or B its scope was not properly closed.
this refers to the "proprietary" of the function, or the object from the function is a method of.
When you define a basic function, the "proprietary" is the page (or the window) itself.
You can check the callback documentation for workarounds
When you call videoPlayer.getIsPlaying it accepts a callback fn. Callback is invoked directly like cb() and hence the context is global (window).
To achieve the callback happening in context of your object. You can use
var fn = cb.bind(customControl);
videoPlayer.getIsPlaying(fn);
As a rule of thumb when a function is called like object.function this is set to object. If function is called directly, this is set to window. Function.bind return a function after binding object (this value) optionally along with parameters.
Read: MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
I'm a bit puzzled by this coding pattern I've run into even though I've been studying up on 'this.' The following (simplified) code shows the pattern:
var MyConstructor = function MyConstructor() {
this._handlers = {
action: this.handleAction.bind(this)
};
};
MyConstructor.prototype.start = function(someObj) {
this.someObj.on(’some event’, this._handlers.action); //<--here
}
MyConstructor.prototype.handleAction = function() {
//do stuff
}
module.exports = MyConstructor;
My question is, why is the private method in the constructor required? Is this pattern avoid some common problem? Could the line commented //<--here simply be:
this.someObj.on(’some event’, this.handleAction);
No, they are different. The difference is in context, which means the value of this within the function.
this.handleAction passes the function to on without any context. There is no value of this specified. The value will be determined when the function is executed. It is very likely that the value will not be the a MyConstructor object, so this.start, for instance, will not refer to the right object, or indeed perhaps any object at all.
The solution is to bind context. This sets the context forever, so this will always refer to the right value. You see this line of code:
action: this.handleAction.bind(this)
This means that, when the code later refers to this._handlers.action, it will be sending the function to on with the appropriate context, so this will always point to the correct value.
The difference between the following lines
this.someObj.on(’some event’, this.handleAction.bind(this));
this.someObj.on(’some event’, this.handleAction);
... is that the first handleAction will run with this being the instance of MyConstructor, while the second will run in whatever context the event handling mechanism decides. If it is something like this, it will run with this being the global object:
function on (a, callback) {
callback(); // callback is run with this as the global object
// UNLESS it was bound to something else
}
The 'private' _handlers property is just an object that holds references to callbacks bound to the instance. If you were to call bind twice, two functions would be created. The _handlers property makes it so that a single bound function is created which can be used as a handler for any number of events.
I've got an array of functions and looking for a concise way to call each one in order.
fns = [
function a() { console.log('a') },
function b() { console.log('b') },
function c() { console.log('c') },
]
this works:
fns.map(function (f) { f() })
and so does this:
fns.map(function (f) { Function.call.call(f) })
however this raises a TypeError:
fns.map(Function.call.call)
Why doesn't the latter example work?
for (var i = 0, len = fns.length; i < len; i++) {
fns[i].call();
};
Here's the working fiddle.
Use Function.prototype.call and Function.prototype.apply as above to call a function. With call and apply you can pass execution scope and arguments to the function call. If you don't need that, you can simply do:
for (var i = 0, len = fns.length; i < len; i++) {
fns[i]();
};
About your code:
Array.prototype.map is a function that takes callback and scope as parameters. In the first two examples, you are using an anonymous function as the callback and you call the parameter passed to it automatically by Array.prototype.map. To expand, your code is the equivalent of this:
function single(element) {
element();
};
fns.map(single);
So the above is entirely correct. Following the same principle by using Function.call.call, you are calling the function f passed as a parameter by map.
But in your third example you are forcing a direct call via Function.call.prototype.call, however in this case, f no longer gets passed as a parameter, which means your Function.call.call will attempt to call undefined, hence you get the TypeError. When you put Function.call.call inside map(), you are NOT passing a callback as an argument.
The call will be immediately. Function.call.call is the exact same thing as Function.call.call(), or Function.call.call(undefined), which will be immediately evaluated when used as you did in your third example.
This simplified code exhibits a similar issue:
var x = Function.call.call;
x(alert);
In this case, once Function.call.call gets called, it won't remember the context from which it originated (i.e. Function.call). To save this context, you could use this unholy construct trick:
Function.call.bind(Function.call)
It returns a new function whereby the context of Function.call is bound to itself, thus saving the context. You can save this expression in a new variable:
var callFn = Function.call.bind(Function.call);
Now, callFn(alert) is identical to alert.call(). Note that any additional arguments will be passed along as is, so callFn(alert, window) will invoke alert.call(window). Understanding this behaviour is important in situations when callFn gets called as part of a callback such as Array.forEach, whereby three arguments are passed in each iteration.
fns.forEach(callFn);
In your case, none of the functions inside fns are using the arguments that are passed, but behind the scenes they're called like this:
fns[0].call(0, fns)
So this equals the element's index (i.e. Number(0)) and arguments[0] equals the array of functions. The keen observer may have noticed that the element's value falls in between the cracks, though it may still be referenced using arguments[0][this] or, alternatively, arguments.callee (deprecated).
The TypeError occurs because Function.prototype.call internally invokes this (with the given context and parameters, but that's not important in the discussion).
Let's rewrite the working
fns.map(function (f) { Function.call.call(f) })
as the equivalent
fns.map(function (f) {
var invoker = Function.call;
invoker.call(f);
})
It is now obvious that invoker is invoked with f as this. When it internally tries to invoke this in turn your function is called, as expected.
Now look at this:
fns.map(Function.call.call);
and the equivalent form
fns.map(function (f) {
var invoker = Function.call;
invoker.call();
})
It should be obvious that here, when invoker is invoked this is undefined; therefore it cannot be invoked itself and this gives rise to the TypeError.
I want to understand about variables, that has been used in returning function.
This is example code
Prototype = {}
Prototype.F =
{
bind: function()
{
var args = arguments, __method = args.shift(), object = args.shift();
return function()
{
return __method.apply(object, args.concat(arguments));
}
}
}
function ObjectA()
{
...
this.addListener = Prototype.F.bind(this.eventSource.addListener,
this.eventSource);
...
}
var a = ObjectA();
a.addListener(this); // assuming 'this' here will point to some window object
As I understand the returning function in bind() is not evaluated until it's called in the last line. It's ok to accept. So addListener will hold a function body containing 'apply'.
But what I don't understand, when addListener is called, what kind of parameters it is going to have? particularly _method and args will always be uninitialized?
The function that bind returns is a closure over the arguments to the bind function, and so the __method argument will be the first argument to bind (in your example call, that will be the this.eventSource.addListener function).
Closures are basically functions that have data bound into them intrinsically. Here's a simpler example:
function makeAlert(msg) {
return function() {
alert(msg);
}
}
var myalert = makeAlert("Hi there!");
myalert(); // Alerts "Hi there!"
The function returned by makeAlert "closes over" (retains access to) the things in scope within the makeAlert function call that created it, including the msg argument. That's why when we call the function later, it still has msg even though the call to makeAlert has long since completed. More about closures here.
A key thing to remember about closures is that they retain access to everything that's in scope where they're defined, not just the things they they're obviously using. So for instance:
function init() {
var data;
data = /* ...build some really big array of data...*/;
document.getElementById('foo').onclick = function() {
this.style.display = "none";
};
}
Even though the event handler has nothing to do with the big data array, it keeps a reference to it, and so keeps that data in memory after the call to init has completed. This is because the link that it has is to a behind-the-scenes object (loosely called the "variable object") that is a container for all of the arguments and local variables in scope where it's defined. (In this particular case, if you don't need all that data, just set data to undefined at the end. The event handler will still have a reference to data, but that reference isn't holding the array anymore, so the array's memory can be reclaimed.)
_method and args will be always initialized, because you are defining them when you first call
this.addListener = Prototype.F.bind(this.eventSource.addListener, this.eventSource);
There, you'll get that _method will be that this.eventSource.addListener, and args will be those both arguments.
in the scope of a function, arguments is an array-like object which contains the values provided when the function is called, whether or not the function definition has parameters defined.
so for this call:
Prototype.F.bind(this.eventSource.addListener, this.eventSource);
which leads to this:
var args = arguments, __method = args.shift(), object = args.shift();
arguments contains 2 items: whatever this.eventSource.addListener and this.eventSource point to when the function is called. that collection of 2 items is copied to args, and then the items are moved from the colleciton to __method and object.
since the call bind actually generates another function, the arguments instance in the new function will be different- it'll have the parameters provided at the time of that call. the original arguments from the call to bind are saved in args and combined with arguments from the later function call.