Why did my callback lose its scope? [duplicate] - javascript

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 8 years ago.
When test.testHello(helloWorld.sayHello); runs, it doesn't recognize that I have inserted a new greeting is the greeting is undefined. I can use bind to make sure it runs it in the proper scope, I am not really sure why isn't it running in the proper scope to begin with. Could someone explain why this is happening and show me a better solution?
Solution: test.testHello(helloWorld.sayHello.bind(helloWorld));
http://jsfiddle.net/EzabX/
var HelloWorldClass = function() {
this.greetings = [];
}
HelloWorldClass.prototype.addGreeting = function(greeting) {
this.greetings.push(greeting);
}
HelloWorldClass.prototype.sayHello = function() {
document.getElementById('output').innerHTML += this.greetings;
this.greetings.forEach(function(greeting) {
greeting.execute();
})
}
var TestClass = function() {
this.testHello = function(callback) {
alert("TestHello is working, now callback");
callback();
}
}
var main = function() {
var helloWorld = new HelloWorldClass();
var test = new TestClass();
helloWorld.addGreeting({
execute: function() {
alert("Konichiwa!");
}
});
helloWorld.sayHello();
test.testHello(helloWorld.sayHello);
}
main();

Managing the this variable within the prototype function scope when called as a callback can be complicated for novice users. You don't always need a prototype for all functions on a class. If you really want to use a prototype function look at Javascript .bind(this) implementations. Google and Stackoverflow are your friend on that topic. Example: Preserving a reference to "this" in JavaScript prototype functions
Offending code with this referring to DOM Window object and not a HelloWorld instance:
this.greetings.forEach(function(greeting) {
greeting.execute();
})
A non-prototype version that works just great, easy to use. Fiddle: http://jsfiddle.net/EzabX/2/
var HelloWorldClass = function() {
var greetings = [];
this.greetings = greetings;
this.addGreeting = function(greeting) {
greetings.push(greeting);
};
this.sayHello = function() {
document.getElementById('output').innerHTML += greetings;
greetings.forEach(function(greeting) {
greeting.execute();
})
}; }

Related

Can't Access Class Property From Delegate Function [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 3 years ago.
Long-time .Net developer here, tasked with converting a bunch of old JS code to new ES6 JS modules. I'm trying to run the (you would think) simple code below, but when jumpToVideoNew is called, this.allowVidJump in the delegate function doesn't have access to the class property allowVidJump. I'm trying to set a simple timed delay so calling code can't hammer the jumpToVideoNew function repeatedly. I understand the concept that the variable loses scope, but I've tried setting _this = this; and using _this in the delegate as well with no success. Also tried passing a reference to the variable in to the function and accessing it that way, but also no luck. Spending 2 hours on something this simple is reminding me why I steer clear of javascript when possible.
export class WebUtility {
constructor() {
this.allowVideoJump = true;
this.delay = function () { this.allowVidJump = true; };
}
jumpToVideoNew() {
if (this.allowVideoJump) {
this.allowVideoJump = false;
setTimeout(this.delay, 1000);
}
}
}
Use an anonymous arrow function
The function keyword in JS creates a new scope as well as a new this (the function you just defined === this), so this.allowVidJump is essentially (function() {}).allowVidJump in that scope
Try something like this:
export class WebUtility {
constructor() {
this.allowVideoJump = true;
this.delay = () => { this.allowVidJump = true; }; // anonymous lambda
}
jumpToVideoNew() {
if (this.allowVideoJump) {
this.allowVideoJump = false;
setTimeout(this.delay, 1000);
}
}
}

this keyword and function scope in javascript [duplicate]

This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 6 years ago.
I have the following code (simplified)
var Page = new UI('page.html');
Page.onLoad = function(html){
this.name = 'Page 1';
Util.test(this.run);
};
Page.run = function(){
console.log(this.name); // undefined
console.log(Page.name); // correct
};
var Util = function(){};
Util.prototype.test = function(callback){
// when finished run the callback
callback();
};
My question is why I can't use the this keyword if the execution leaves the object then comes back? Please explain what should I change to be able to access this again.
You can bind "this" to function run, like this.
Page.onLoad = function(html){
this.name = 'Page 1';
Util.test(this.run.bind(this));
};
You can find more information for function "bind". https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
The top of the article references another post regarding 'this', so, I thought I'd just provide the code. If you read the other post and the articles linked within, you should be able to follow this code.
function UI(html) {
this.name = html;
}
UI.prototype = {
onLoad: function () {
util.test(this);
},
run: function () {
console.log(this.name);
}
}
var util = (function () {
return {
test: function (ui) {
if (ui && ui.run) {
ui.run();
}
}
}
})();
var page = new UI("index.html");

Angular get/set variable on scope with this from function [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 7 years ago.
I am adapting a controller to the "controller as" syntax.
But while transforming the code, I remarked that I can't access the "scope" variables with this out of a function.
If I understand the concept of this, then this does access the object it is used in.
As example this code:
this.scopeVariable = undefined;
this.fooFunction = function () {
resource.get()
.$promise.then(function (result) {
this.scopeVariable = result.foo;
});
};
So when I try to set the scopeVariable with this, I actually try to access an object from fooFunction, right?
If this is the case, how can I grab an object outside of the function within the function?
Thanks for your answers!
You have this problem because the this keyword changes definition when in a different scope. That means that if you have a simple object e.g.
var a = {
b: function() {
console.log(this); // => will log the value of "a"
setTimeout(function() {
console.log('just some rubbish');
console.log(this); // => will log inner function scope instead of "a"
}, 200)
}
}
a.b(); // to see the results
To prevent this issue from happening you can reassign this to a variable which will stay intact through deeper scopes like this:
this.scopeVariable = undefined;
var self = this;
this.fooFunction = function () {
resource.get()
.$promise.then(function (result) {
// using self. instead of this. makes the difference here
self.scopeVariable = result.foo;
});
};
Capturing the this being the scope in a variable: var that = this:
var that = this;
this.fooFunction = function () {
resource.get()
.$promise.then(function (result) {
that.scopeVariable = result.foo;
});
};

setInterval, this, again [duplicate]

This question already has answers here:
JavaScript setInterval and `this` solution
(9 answers)
Referencing "this" inside setInterval/setTimeout within object prototype methods [duplicate]
(2 answers)
How to access the correct `this` inside a callback
(13 answers)
Closed 5 months ago.
I have a problem in regard to setInterval that I can't figure out.
There is the problem with the scope when calling setInterval or timeout from within an object, but still I can't wrap my head around it.
I tried to put my stuff inside an anonymous function, it won't work.
This is basicly my problem, simplified to the bare bones:
function Scenario(){
var ships = [];
this.ini = function(){
for (var i = 0; i < ships.length; i++){
timeoutID1 = setTimeout(ships[i].ding, 1000);
timeoutID2 = setTimeout(ships[i].bing, 1000);
}
}
this.setShips = function(){
var ship = new Ship("ship");
ships.push(ship);
}
function Ship(name){
this.name = name;
this.ding = function(){
intervalID1 = setInterval(function(){
console.log("ding");
}, 500)
}
this.bing = function(){
var _this = this;
intervalID2 = setInterval(function(){
console.log(_this.name);
}, 500)
}
}
this.setShips();
}
var scenario = new Scenario();
scenario.ini();
http://jsfiddle.net/ancientsion/xkwsn7xd/
Basicly, console.log("ding") works, console.log(_this.name) doesn't.
Why?
This is your problem simplified to bare bones:
var ship = {
name: 'Sheep',
ding: function() {
console.log(this.name);
}
}
setTimeout(ship.ding, 1000); // doesn't work correctly
It may help to see another example to understand why the above doesn't work:
var ding = ship.ding;
ding(); // doesn't work either
In JavaScript this depends on how you call your function. ship.ding() will set this to the sheep object. Bare ding() call will set this to the window object.
You can bind the function to the object you want by using .bind() method. (Function.prototype.bind())
var ding = ship.ding.bind(ship);
ding(); // works
ding is now permanently bound to the sheep object. You can use exactly the same approach with setTimeout:
setTimeout(ship.ding.bind(ship), 1000);
By the time setTimeout() gets around to call your method, it only sees the function and not the invocation context (i.e. the object to bind it to); much like this:
var bing = ships[i].bing;
bing(); // inside bing(), this == window
Basically, you need to provide setTimeout() with a prewired method invocation:
var bound_bing = ships[i].bing.bind(ships[i]);
timeoutID2 = setTimeout(bound_bing, 1000);
The "magic" happens with .bind() as it returns a new function that will have this set up properly.
You should define the value _this in Ship function:
function Ship(name){
this.name = name;
this.ding = function(){
intervalID1 = setInterval(function(){
console.log("ding");
}, 500)
}
var _this = this;
this.bing = function(){
intervalID2 = setInterval(function(){
console.log(_this.name);
}, 500)
}
}
Hoist _this out of the bing function.
you should have
_this = this,
this.bing = function() {
intervalID2 = setInterval(function(){
console.log(_this.name);
}, 500)
}
Javascript is imperative so you need to follow the execution path carefully. In function Ship, this points to a Ship object, in function bing, this points to the global scope (window). You need to save a reference to this so that you can refer back to it in these types of functions.
You put ships[i].bing in setTimeout turns out that the caller of bing is not ships[i] but global, so _this is pointing to global actually.

More elegant way to call method from ajax callback function [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 8 years ago.
I have a problem with javascript namespaces.
I want to call secondMethod from ajax callback function but I don't know how to get reference to it.
I have done it like that yet. But variable thisReference seems to me awkward. And whole construction is difficult to read.
So I'm writing there for help and answer how to rewrite it better.
var testObject = new TestObject();
testObject.firstMethod("hello world!");
function TestObject() {
var thisReference = this;
this.firstMethod = function(firstParam) {
ajaxFunc(firstParam, function(ajaxResult) {
thisReference.secondMethod(ajaxResult);
});
};
this.secondMethod = function(secondParam) {
alert("secondMethod " + secondParam);
};
}
function ajaxFunc(hello, callBack) {
callBack(hello);
}
Thanks a lot.
Ondra
Something like what you're doing is a common way to do it. Using:
var that = this;
or:
var self = this;
are common names to keep a hold of your original scope.
Another option (which I prefer) is to bind the this object to the method you're calling so that you have access to it in the callback. That would look like this:
this.firstMethod = function(firstParam) {
ajaxFunc(firstParam, function(ajaxResult) {
this.secondMethod(ajaxResult);
}.bind(this));
};
Hope that helps.

Categories