why is this private method in a constructor? - javascript

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.

Related

Confused about when to use `bind` in an Event Handler

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.

Nested .bind not working as expected

Unfortunately .bind has been giving me grief when creating more complex closures.
I am quite interested in why .bind seems to work differently once you nest functions.
For example :
function t(){
t = t.bind({}); //correctly assigns *this* to t
function nested_t(){
nested_t = nested_t.bind({}); // fails to assign *this* to nested_t
return nested_t;
}
return nested_t();
}
//CASE ONE
alert(t());
// alerts the whole function t instead of nested_t
//CASE TWO
aleft(t.call(t));
// alerts the global object (window)
In both cases I was expecting a behavior like this:
function t(){
var nested_t = function nested_t(){
return this;
};
return nested_t.call(nested_t);
}
alert(t.call(t));
If someone could explain the behavior of .bind in the first (and/or) second case it would be much appreciated!
So, i'm not entirely reproducing your issue here (both cases return the global object), but i'll try and explain the code as i see it.
function t(){
t = t.bind({}); //correctly assigns *this* to t
function nested_t(){
nested_t = nested_t.bind({}); // fails to assign *this* to nested_t
return this;
}
return nested_t();
}
//CASE ONE
alert(t());
Let's take it step by step.
First, the function t() is defined. Then, upon call, it gets overwritten with a clean context. However, i don't see any usage of this context.
Now, a nested function (nested_t) is defined. Upon call, it is overwritten with a clean context. It then returns the context it had when it was called.
Back to t(). You then return the result of nested_t(), not nested_t itself. In the original function call, nested_t is still being called with global context.
Therefore, when you run t(), it returns the global object.
How your code works
It's very unclear, what your code is trying to do. You can find the documentation for .bind() here. It looks like you might be somehow misunderstanding what this is and how to use it. Anyway, what happens when you run your code is this:
A t function is created in global scope.
[case one] The t function is called.
Global scope t is replaced with a new value (original t bound to a specific context - anonymous empty object), which doesn't affect the current call in any way. Also, while the global t is overwritten, the local t is behaving as read-only. You can check it out by trying the following code: (function foo () { return (function bar () { bar = window.bar = 'baz'; return bar; })(); })() and comparing return value with window.bar.
The same thing happens with nested_t in the nested context (instead of global context).
Result of nested_t call is returned. nested_t returns the context it was called with, which was window, as no context was specified. Specifically, it was not called with an empty object context, because the .bind() inside didn't affect the call itself.
[case two] The exact same thing happens once again. Now you're just calling t with itself as context. Since t doesn't use its context (this) anywhere in its code, nothing really changes.
What your misconceptions might be
Basically, you're mixing up two things - function instance and function call context. A function is a "first-class citizen" in JavaScript - it's an object and you can assign values to its properties.
function foo () {
foo.property = 'value';
}
foo(); // foo.property is assigned a value
This has nothing to do with function call context. When you call a function a context is assigned to that call, which can be accessed using this (inside function body)
function foo () {
this.property = 'value';
}
var object = {};
foo.call(object); // object.property is assigned a value
When you use .bind(), you just create a new function with the same code, that is locked to a specific context.
function foo () {
this.property = 'value';
}
var fixedContext = {},
object = {};
bar = foo.bind(fixedContext);
foo.call(object); // fixedContext.property is set instead of object.property
But in this case, there are also function instances foo and bar, which can also be assigned properties, and which have nothing to do with contexts of calls of those functions.
Let's look at how bind works. First, one level of nesting:
var foo = function() { return this.x; };
alert(foo()); // undefined
alert(foo.bind({x: 42})()); // 42
Now we can add the next level of nesting:
var bar = function() { return foo.bind(this)(); };
alert(bar()); // undefined
alert(bar.bind({x: 42})());
We pass our this context to foo with - guess what? - bind. There is nothing different about the way bind works between scopes. The only difference is that we've already bound this within bar, and so the body of bar is free to re-bind this within foo.
As a couple of commenters have noted, functions that overwrite themselves are a huge code smell. There is no reason to do this; you can bind context to your functions when you call them.
I highly, highly recommend reading the documentation on bind, and trying to understand it to the point where you can write a basic version of Function.prototype.bind from scratch.

How do the following Javascript functions affect the value of 'this'?

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

Understanding Javascript scope with "var that = this" [duplicate]

This question already has answers here:
In Javascript, why is the "this" operator inconsistent?
(8 answers)
Closed 9 years ago.
Say I have the following property method in an object:
onReady: function FlashUpload_onReady()
{
Alfresco.util.Ajax.jsonGet({
url: Alfresco.constants.PROXY_URI + "org/app/classification",
successCallback: {
fn: function (o) {
var classButtonMenu = [],
menuLabel, that = this;
var selectButtonClick = function (p_sType, p_aArgs, p_oItem) {
var sText = p_oItem.cfg.getProperty("text");
that.classificationSelectButton.set("label", sText);
};
for (var i in o.json.items) {
classButtonMenu.push({
text: o.json.items[i].classification,
value: o.json.items[i].filename,
onClick: {fn: selectButtonClick}
});
}
this.classificationSelectButton = new YAHOO.widget.Button({
id: this.id + "-appClassification",
type: "menu",
label: classButtonMenu[0].text,
name: "appClassification",
menu: classButtonMenu,
container: this.id + "-appClassificationSection-div"
});
},
scope: this
},
failureMessage: "Failed to retrieve classifications!"
});
It took me some guess work to figure out that in the selectButtonClick function that I needed to reference that instead of this in order to gain access to this.classificationSelectButton (otherwise it comes up undefined), but I'm uncertain as to why I can't use this. My best guess is that any properties in the overall object that gets referenced within new YAHOO.widget.Button somehow looses scope once the constructor function is called.
Could someone please explain why it is that I have to reference classificationSelectButton with var that = this instead of just calling `this.classificationSelectButton'?
The most important thing to understand is that a function object does not have a fixed this value -- the value of this changes depending on how the function is called. We say that a function is invoked with some a particular this value -- the this value is determined at invocation time, not definition time.
If the function is called as a "raw" function (e.g., just do someFunc()), this will be the global object (window in a browser) (or undefined if the function runs in strict mode).
If it is called as a method on an object, this will be the calling object.
If you call a function with call or apply, this is specified as the first argument to call or apply.
If it is called as an event listener (as it is here), this will be the element that is the target of the event.
If it is called as a constructor with new, this will be a newly-created object whose prototype is set to the prototype property of the constructor function.
If the function is the result of a bind operation, the function will always and forever have this set to the first argument of the bind call that produced it. (This is the single exception to the "functions don't have a fixed this" rule -- functions produced by bind actually do have an immutable this.)
Using var that = this; is a way to store the this value at function definition time (rather than function execution time, when this could be anything, depending on how the function was invoked). The solution here is to store the outer value of this in a variable (traditionally called that or self) which is included in the scope of the newly-defined function, because newly-defined functions have access to variables defined in their outer scope.
Because this changes its value based on the context it's run in.
Inside your selectButtonClick function the this will refer to that function's context, not the outer context. So you need to give this a different name in the outer context which it can be referred to by inside the selectButtonClick function.
There's lexical scope: variables declared in functions and arguments passed to functions are visible only inside the function (as well as in its inner functions).
var x = 1; // `1` is now known as `x`
var that = this; // the current meaning of `this` is captured in `that`
The rules of lexical scope are quite intuitive. You assign variables explicitly.
Then there's dynamic scope: this. It's a magical thing that changes it's meaning depending on how you call a function. It's also called context. There are several ways to assign a meaning to it.
Consider a function:
function print() { console.log(this); }
Firstly, the default context is undefined in strict mode and the global object in normal mode:
print(); // Window
Secondly, you can make it a method and call it with a reference to an object followed by a dot followed by a reference to the function:
var obj = {};
obj.printMethod = print;
obj.printMethod(); // Object
Note, that if you call the method without the dot, the context will fall back to the default one:
var printMethod = obj.printMethod;
printMethod(); // Window
Lastly, there is a way to assign a context is by using either call/apply or bind:
print.call(obj, 1, 2); // Object
print.apply(obj, [ 1, 2 ]); // Object
var boundPrint = print.bind(obj);
boundPrint(); // Object
To better understand context, you might want to experiment with such simple examples. John Resig has very nice interactive slides on context in JavaScript, where you can learn and test yourself.
Storing it in a variable lets you access it in other scopes where this may refer to something else.
See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this, http://www.quirksmode.org/js/this.html and What is the scope of variables in JavaScript? for more information about the this keyword.
The this reference doesn't work when a method of a class is called from a DOM event. When a method of an object is used as an event handler for onclick, for example, the this pointer points to the DOM node where the event happened. So you have to create a private backup of this in the object.
this is a keyword in javascript, not a default variable defined within every function, hence, as Gareth said, this will refer to the context in which the function is invoked, or the global object if there's no context.

In javascript functions, can you set this.function = function?

I have run into this jquery plugin and i quite understand how this works:
$.functionone = function(){
function setOptions(newOptions){
...
}
this.setOptions = setOptions;
}
What i dont understand is what does this actually do? this.setOptions = setOptions can you call a function without parenthesis? What is the relationship between this.setOptions and setOptions by itself?
Functions in JavaScript are objects like (nearly) everything else. When you do this:
this.setOptions = setOptions;
you're not calling the setOptions function, you're just assigning a reference to the function to a property, exactly like setting a property to any other object, like this:
var dt;
dt = new Date();
this.today = dt;
With functions, you'd do this so you can later call the function via the property (which sets up the this value to be the object the property's on, which is handy). It's a bit clearer what's going on if you use a different name for the property than for the function:
function functionName() { ... } // Declares the function
this.propertyName = functionName; // Sets the property
functionName(); // Calls the function (with `this` = the global object ["window", on browsers])
this.propertyName(); // Also calls the function (but with `this` = the object the property's on)
The pattern you identified, declaring a function and then separately setting a reference to it on an object, is frequently used to make sure the resulting function has a name. You can create a function and bind it to a property like this:
this.setOptions = function() {
...
};
...but then the function doesn't have a name (the property does, but not the function), which can be an issue when you're trying to debug because debuggers show you the names of functions in various contexts (call stacks, for instance). If a lot of your functions don't have names, even though the properties referring to them do, it makes debugging difficult. More about anonymous vs. named functions here. (There's also a difference in terms of when the function is instantiated, but going into it here would just complicate things.)
You'd think you could combine things, like this:
this.setOptions = function setOptions() { // <=== DON'T DO THIS
...
};
...but although that mostly works, it triggers a bug in Internet Explorer / JScript (it creates two different functions for that code, which is at best a memory waste and at worst a very subtle and time-wasting problem, as it was in this question).
The function setOptions is only called if you add parenthesis: setOptions(). If you do not add parenthesis, you have a reference to a function. This is just like a normal variable, only it contains a function reference instead of some other value.
If you set this.setOptions = setOptions, you make a function setOptions on the this object, which points to the same function as setOptions. That is, if you call it using this.setOptions(), the referenced function will be called.

Categories

Resources