not able to change a value assigned to this object using .bind - javascript

I'm using angular to develop a web app and I'm trying to use a function's .bind method to assign the this value on a method of one of my controllers. It looks like this:
var welcomeCtrl = function (userManager) {
this.partnerName = userManager.getName('partner');
this.yourName = userManager.getName('you');
this.beginScan = false;
var self = this;
};
welcomeCtrl.prototype.scanThumb = function (callback) {
function doScan() {
alert(this);
alert(this.beginScan);
this.finishedScanning = callback;
this.beginScan = true;
}
doScan.bind(welcomeCtrl)();
};
So what happens is that a directive passes the scanThumb method to a service which executes it, which should then trigger another directive waiting for this.beginScan to be true.
Since its the service that calls the method and it is not called from the welcomCtrl class, I need to bind this back to welcomeCtrl so I use .bind and pass in welcomeCtrl
This should work, when I do alert(this) the welcomeCtrl function definition alerts fine, but when I do alert(this.beginScan) I get Undefined
Am I not understanding how the .bind method works in this circumstance?

Whenever you are using an object's inner function (in this case, welcomeCtrl) this refers to the current object.
Take the following example:
var Foo = function(){
this.thing = 'bar';
}
Foo.prototype.setThing = function(newthing){
//our inner function
function doInnerThing(){
//this is now bound to our current Foo instance
console.log(this);
//setting our current Foo.thing to new value
this.thing = newthing;
};
//fire function, bound to current object scope (Foo)
doInnerThing.bind(this)();
};
Foo.prototype.doThing = function(){
alert(this.thing);
};
var newFoo = new Foo();
var newFoo2 = new Foo();
newFoo.setThing('newFoo');
newFoo.doThing(); //alerts 'newFoo', the overridden value
newFoo2.doThing();//alerts 'bar', the base value

As #Jesse Kernaghan suggested I was simply was passing the uninitiated constructor as the the thisParam. I fixed this by modifying my service to take 2 parameters, a callback, and a thisParam. I had then had to pass in the scope as the thisParam from my directive, and in my service call the callback with a .bind(thisParam) and now everything works.

Related

Reference to a function call

As we can assign a function call to a new reference in javascript for example we have a function display and when we call it we use display(). And we know in javascript we can also create a reference for this for example var call = display(),So can i invoke this function using this call() reference ? below is my code it is not working it is showing outerDisplay is not a function why is this happening ?
<script>
var obj = {
name: 'Saurabh',
display: function () {
console.log(this.name); // 'this' points to obj
}
};
var outerDisplay = obj.display(); // Saurabh
outerDisplay();
</script>
outerDisplay is not a function because it already called obj.display().
However if you changed code like the below one, it won't work:
var outerDisplay = obj.display;
outerDisplay(); // it won't work!
Because it lost the "owner" of this function. You have to tell who is the owner with call.
var obj = {
name: 'Saurabh',
display: function(){
console.log(this.name); // 'this' points to obj
}
};
var outerDisplay=obj.display; // Saurabh
outerDisplay.call(obj);
MDN web docs link: Function.prototype.call()
there are 2 things which you are getting wrong.
First when you want to assign a function to a variable you should not call it.
calling a function while assigning will evaluate to the return value of your function;
so if you do
var outerDisplay = obj.display; // this will assign the function and you may call it as
outerDisplay(); // will call your assigned function
if you do
var outerDisplay = obj.display(); // this will call it immediately and assign what is returned
outerDisplay(); // throw an error as outerDisplay is not a function
Second thing you missed;
when you assign a function to another variable then calling the variable will run in a different context.
and here this point to the window object;
window.name = "Prabhat"
var outerDisplay = obj.display;
outerDisplay(); // print Prabhat

binding an instance method vs wrapping in an anonymous function

[This is related to Bound function instead of closure to inject extra arguments, but that was neither clearly asked nor answered.]
I'm calling a function that expects a function as its argument. I want to pass a method from my class, bound to an instance of my class. To make it clear, assume my class looks like:
var MyClass = function() {}
MyClass.prototype.myMethod = function() { ... }
var my_instance = new MyClass();
Is there any substantive difference between using bind:
doSomething(my_instance.myMethod.bind(my_instance))
and wrapping the call in an anonymous function:
doSomething(function() { my_instance.myMethod(); })
?
If a prototype within your class needs to generate a callback, it may not know its instance's name. As a result, you'll need to use this, but the value of this depends on where the callback was executed.
Consider the following example:
var MyClass = function (x) { this.something = x; };
MyClass.prototype.makeCall = function () {
var myBadCallback = function() { console.log(this.something); };
var myGoodCallback = function() { console.log(this.something); }.bind(this);
// When called, the value of "this" points to... we don't know
callMeBack( myBadCallback );
// When called, the value of "this" points to this instance
callMeBack( myGoodCallback );
};
function callMeBack( callback ) { callback(); };
var foo = new MyClass('Hello World!');
var bar = new MyClass('Goodbye!');
// Probably prints "undefined", then prints "Hello World!"
foo.makeCall();
// Probably prints "undefined", then prints "Goodbye!"
bar.makeCall();
In the above example, the first output probably prints undefined because the context (what this refers to) has changed by the time the callback has executed.
This example may seem contrived, but these sort of situations do arise, a common case being AJAX callbacks.

JavaScript scope issue (this)

I have tried to organize my code in an object oriented way (as explained in MDN). In my case however, this refers to the window object. Because of that I get the error
Uncaught TypeError: Cannot read property 'render' of undefined
in
this.renderer.render(this.stage);
Why does this refer to the window object when it doesn't on MDN?
var GAME = GAME || {};
GAME.Application = function() {
this.renderer = PIXI.autoDetectRenderer(800, 600,{backgroundColor : 0x1099bb});
document.getElementById("game").appendChild(this.renderer.view);
this.stage = new PIXI.Container();
requestAnimationFrame(this.render);
}
GAME.Application.prototype.render = function() {
this.renderer.render(this.stage);
}
var app = new GAME.Application();
You need to bind your render function. This is probably the most straight forward solution.
requestAnimationFrame(this.render.bind(this));
Or instead, you could do
var context = this;
requestAnimationFrame(function() {
context.render();
});
Or you could avoid creating the free variable and use an IIFE
requestAnimationFrame((function(context) {
context.render();
})(this)));
Or, if you're using ES6, you can use an arrow function
requestAnimationFrame(() => this.render());
Another easy improvement you could make is passing the render element into your Application constructor
function Application(elem) {
this.renderer = ...
elem.appendChild(this.renderer.view);
}
new Application(document.getElementById("game"));
Lets talk about this, context, and functions
A good way to think about it, is that this refers to the object on the left of the . of the method that calls it.
var someObj = {
name:'someObj',
sayName: function(){
console.log(this.name);
}
};
someObj.sayName(); // prints someObj
Functions that are not methods of an object are bound to the window object.
window.name = 'window';
function sayName(){
console.log(this.name);
}
sayName(); //prints window
The above is equivalent to
window.sayName(); // window is on the left of the dot, so it is `this`
When you pass a method of an object as a parameter, or assign it to a variable, it loses its original context. Below, the sayName method of someObj loses someObj as the context and gains someOtherObj.
var someOtherObj = {
name:'someOtherObj'
};
someOtherObj.sayName = someObj.sayName;
someOtherObj.sayName(); // prints someOtherObj
To get around it, You can bind a context to a function
var yetAnotherObj = {
name: 'yetAnotherObj'
};
var sayYetAnotherObj = sayName.bind(yetAnotherObj);
sayYetAnotherObj(); // prints yetAnotherObj
Or pass an anonymous function that calls the the method on the object itself
var OneLastObj = function(){
var self = this;
this.someValue = aFunctionTakingAcallback(function(){
return self.doSomeStuff();
});
}
Something to remember when passing functions around as a parameters is that you are passing a reference to the function. The function itself is not bound to the object that it may be a method of.

Get right object in callback

I have target object
function Foo() {
this.someVar = 'some var';
};
Foo.prototype.callback() {
console.log(this);
};
And object, that will call this callback
function Bar(callback) {
this.callback = callback;
};
Bar.prototype.onSomeAction = function() {
this.callback();
};
And initial code
foo = new Foo();
bar = new Bar();
bar.callback = foo.callback;
bar.onSomeAction();
Result: i have logged to console Bar()'s context instead of Foo().
How can i get context of Foo() in the Foo() callback?
PS: I tried closures
Foo.prototype.callback() {
var foo = this;
return function(foo) {
console.log(foo);
};
};
but it does nothing. I have not fully understanding of the closures :(
The reason your original code didn't work is that the value of this inside of a method call is the value of the object it's being called on. That means when you say:
bar.callback = foo.callback;
And then you call:
bar.callback();
The code defined here:
Foo.prototype.callback = function () {
console.log(this);
};
gets called with this being a reference to bar because bar is to the left of the . on the method call. So whenever you assign a function as an object property, calling it on that object will call it with the object as this.
You could also have written:
function callback() {
console.log(this);
}
bar.callback = callback;
bar.callback();
And you would find that this still references bar.
In fact, if you call the plain function callback(); as defined above, you'll find that this is a reference to the global object, usually window in web browsers. That's because all global variables and functions are properties of window, so callback(); is implicitly window.callback();
The fact that the value of this depends on what object is calling a function can be a problem when passing callbacks around, since sometimes you want this to reference the original object the function was a property of. The bind method was design to solve this problem, and Yuri Sulyma gave the right answer:
bar.callback = foo.callback.bind(foo);
However, the way you would do this using closures is to capture an instance of Foo within an anonymous function that calls the correct method on the correct object:
foo = new Foo();
bar = new Bar();
bar.callback = function () {
foo.callback();
};
bar.onSomeAction();
Which is essentially what bind does. In fact, we call write our own naive version of bind using a closure:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.call(obj);
};
};
call let's you call a function with the value of this explicitly defined. This allows you to "set the context" the function is called in so that it's the same as calling obj.fn() when you call bar.callback(). Since when we call foo.callback.bind(foo);, obj is foo and fn is foo.callback, the result is that calling bar.callback() becomes the same as calling foo.callback().
That's where Dalorzo's answer comes from. He uses call to explicitly set the context.
There's also another function for setting the context called apply that also takes an array representing the arguments for the function as its second argument. This allows us to write a more complete version of bind by taking advantage of the special arguments variable:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.apply(obj, arguments);
};
};
bar.callback = foo.callback.bind(foo);
You can polyfill Function.prototype.bind() if necessary: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
Try using these changes:
Use call to set context:
bar.onSomeAction.call(foo);
And I think your callback function needs to change to:
Foo.prototype.callback=function() {
console.log(this);
};

What is a best practice for ensuring "this" context in Javascript?

Here's a sample of a simple Javascript class with a public and private method (fiddle: http://jsfiddle.net/gY4mh/).
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction();
}
}
ex = new Example;
ex.publicFunction();
Calling the private function from the public one results in "this" being the window object. How should I ensure my private methods are called with the class context and not window? Would this be undesirable?
Using closure. Basically any variable declared in function, remains available to functions inside that function :
var Example = (function() {
function Example() {
var self = this; // variable in function Example
function privateFunction() {
// The variable self is available to this function even after Example returns.
console.log(self);
}
self.publicFunction = function() {
privateFunction();
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Another approach is to use "apply" to explicitly set what the methods "this" should be bound to.
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo
Yet another approach is to use "call":
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo
both "apply" and "call" take the object that you want to bind "this" to as the first argument and an array of arguments to pass in to the method you are calling as the second arg.
It is worth understanding how the value of this in javascript is determined in addition to just having someone tell you a code fix. In javascript, this is determined the following ways:
If you call a function via an object property as in object.method(), then this will be set to the object inside the method.
If you call a function directly without any object reference such as function(), then this will be set to either the global object (window in a browser) or in strict mode, it will be set to undefined.
If you create a new object with the new operator, then the constructor function for that object will be called with the value of this set to the newly created object instance. You can think of this as the same as item 1 above, the object is created and then the constructor method on it is called.
If you call a function with .call() or .apply() as in function.call(xxx), then you can determine exactly what this is set to by what argument you pass to .call() or .apply(). You can read more about .call() here and .apply() here on MDN.
If you use function.bind(xxx) this creates a small stub function that makes sure your function is called with the desired value of this. Internally, this likely just uses .apply(), but it's a shortcut for when you want a single callback function that will have the right value of this when it's called (when you aren't the direct caller of the function).
In a callback function, the caller of the callback function is responsible for determining the desired value of this. For example, in an event handler callback function, the browser generally sets this to be the DOM object that is handling the event.
There's a nice summary of these various methods here on MDN.
So, in your case, you are making a normal function call when you call privateFunction(). So, as expected the value of this is set as in option 2 above.
If you want to explictly set it to the current value of this in your method, then you can do so like this:
var Example = (function() {
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction.call(this);
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Other methods such as using a closure and defined var that = this are best used for the case of callback functions when you are not the caller of the function and thus can't use 1-4. There is no reason to do it that way in your particular case. I would say that using .call() is a better practice. Then, your function can actually use this and can behave like a private method which appears to be the behavior you seek.
I guess most used way to get this done is by simply caching (storing) the value of this in a local context variable
function Example() {
var that = this;
// ...
function privateFunction() {
console.log(that);
}
this.publicFunction = function() {
privateFunction();
}
}
a more convenient way is to invoke Function.prototype.bind to bind a context to a function (forever). However, the only restriction here is that this requires a ES5-ready browser and bound functions are slightly slower.
var privateFunction = function() {
console.log(this);
}.bind(this);
I would say the proper way is to use prototyping since it was after all how Javascript was designed. So:
var Example = function(){
this.prop = 'whatever';
}
Example.prototype.fn_1 = function(){
console.log(this.prop);
return this
}
Example.prototype.fn_2 = function(){
this.prop = 'not whatever';
return this
}
var e = new Example();
e.fn_1() //whatever
e.fn_2().fn_1() //not whatever
Here's a fiddle http://jsfiddle.net/BFm2V/
If you're not using EcmaScript5, I'd recommend using Underscore's (or LoDash's) bind function.
In addition to the other answers given here, if you don't have an ES5-ready browser, you can create your own "permanently-bound function" quite simply with code like so:
function boundFn(thisobj, fn) {
return function() {
fn.apply(thisobj, arguments);
};
}
Then use it like this:
var Example = (function() {
function Example() {
var privateFunction = boundFn(this, function() {
// "this" inside here is the same "this" that was passed to boundFn.
console.log(this);
});
this.publicFunction = function() {
privateFunction();
}
}
return Example;
}()); // I prefer this order of parentheses
VoilĂ  -- this is magically the outer context's this instead of the inner one!
You can even get ES5-like functionality if it's missing in your browser like so (this does nothing if you already have it):
if (!Function.prototype.bind) {
Function.prototype.bind = function (thisobj) {
var that = this;
return function() {
that.apply(thisobj, arguments);
};
}:
}
Then use var yourFunction = function() {}.bind(thisobj); exactly the same way.
ES5-like code that is fully compliant (as possible), checking parameter types and so on, can be found at mozilla Function.prototype.bind. There are some differences that could trip you up if you're doing a few different advanced things with functions, so read up on it at the link if you want to go that route.
I would say assigning self to this is a common technique:
function Example() {
var self = this;
function privateFunction() {
console.log(self);
}
self.publicFunction = function() {
privateFunction();
};
}
Using apply (as others have suggested) also works, though it's a bit more complex in my opinion.
It might be beyond the scope of this question, but I would also recommend considering a different approach to JavaScript where you actually don't use the this keyword at all. A former colleague of mine at ThoughtWorks, Pete Hodgson, wrote a really helpful article, Class-less JavaScript, explaining one way to do this.

Categories

Resources