This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 6 years ago.
Consider the following code:
foo: function() {
var self = this;
var p1 = p2 = someFunctionThatReturnsAPromise();
Promise.all([p1, p2])
.then(self.bar);
}
bar: function(promises) {
var self = this;
console.log(self);
}
Output:
undefined
But if I do the following instead:
foo: function() {
var self = this;
var p1 = p2 = someFunctionThatReturnsAPromise();
Promise.all([p1, p2])
.then(function(result) {
self.bar(result);
});
}
bar: function(promises) {
var self = this;
console.log(self);
}
Output:
{ foo: [Function],
bar: [Function] }
I don't understand why the first call changes where this points in the bar function. Can someone enlighten me?
When you pass self.bar to the then method, you pass a function reference. Although it looks like you also specify it should be called on the self object, that is actually not what is happening. The self object is not included in that function reference. The this object's value is determined when a function is called, not when it is defined or passed as argument.
In your second example, self is the this object within the function context, because that is where you call the function from.
Another way to get it working is to force the function's this object to always be self, overriding the above described behaviour. You can achieve that with .bind():
Promise.all([p1, p2])
.then(self.bar.bind(self));
Related
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 1 year ago.
I have some code here.
I am aware that this reference wont be carried in anonymous functions.
But here, even when using a function of an object, the this inside that is window.
var MyClass = function (div) {
this.array = [];
};
MyClass.prototype = {
addToArray: function (elem) {
this.array.push(elem);
},
processAndAdd: function(elemArray){
elemArray.forEach(this.addToArray);
}
}
var myObj = new MyClass();
myObj.processAndAdd([1, 2, 3]);
console.log('test');
Error: Line: this.array.push(elem);
push of undefined.
On inspection, the this here is window Object
I want to know why this is window here, and how to restructure my code to properly handle this.
The reference to this object is no more MyClass constructor after you pass the callback function body as callback to forEach. Bind the function body with the current this context and your code should work.
var MyClass = function (div) {
this.array = [];
};
MyClass.prototype = {
addToArray: function (elem) {
this.array.push(elem);
},
processAndAdd: function(elemArray){
elemArray.forEach(this.addToArray.bind(this));
}
}
var myObj = new MyClass();
myObj.processAndAdd([1, 2, 3]);
console.log('test');
I encountered this issue when creating a function for a Javascript library. As far as I can tell, when an object's function uses a combination of a closure and a promise, the most recently created version would be used for all instances. The output of the below code is "3" repeated three times. However, when I add var in front of the line defining logSecret, the output is then 1 2 3, which is what I originally expected.
Why do multiple instances of the object end up using the same value?
Why does adding var in front of the closure fix the issue?
Unfortunately I don't know Javascript quite well enough to understand what exactly is happening here, so I'm hoping someone can shed some light on what is happening here. Thank you!
function MyObject(myValue){
var _this = this;
_this.secret = myValue;
_this.foo = function() {
makePromise = function() {
var promise = $.Deferred();
promise.resolve("done");
return promise;
}
logSecret = function() {
console.log(_this.secret);
}
var promise = makePromise();
$.when(promise).then(function (data) { logSecret(); });
}
}
var obj1 = new MyObject(1);
var obj2 = new MyObject(2);
var obj3 = new MyObject(3);
obj1.foo();
obj2.foo();
obj3.foo();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
In non-strict mode, when variables are not explicitly declared with var, let or const, an implicit global variable is created. ie. such variables behave as if they had been declared in the global context.
In your code, the variables makePromise and logSecret are not explicitly declared and so are shared by every invocation of MyObject.
So, every time MyObject is invoked, the makePromise and logSecret are overwritten with a new function object instance that closes over the lexical environment for the latest invocation of MyObject.
The final invocation of MyObject closes over the secret value of 3, hence the observed behavior.
function MyObject(myValue) {
var _this = this;
_this.secret = myValue;
_this.foo = function() {
makePromise = function() { // global variable
var promise = $.Deferred();
promise.resolve("done");
return promise;
}
logSecret = function() { // global variable
console.log(_this.secret);
}
var promise = makePromise();
$.when(promise).then(function(data) {
logSecret();
});
}
}
I'm reading about ngInfiniteScroll, and i'm a newbie about JS.
As I've read the demo of nfInfiniteScroll, it's hard for me to understand why Reddit.nextPage have been transformed into Reddit.prototype.nextPage and it has been used the bind() method to wrap a part of Reddit.prototype.nextPage body.
Here is the code.
myApp.controller('DemoController', function($scope, Reddit) {
$scope.reddit = new Reddit();
});
// Reddit constructor function to encapsulate HTTP and pagination logic
myApp.factory('Reddit', function($http) {
var Reddit = function() {
this.items = [];
this.busy = false;
this.after = '';
};
Reddit.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
var url = "https://api.reddit.com/hot?after=" + this.after + "&jsonp=JSON_CALLBACK";
$http.jsonp(url).success(function(data) {
var items = data.data.children;
for (var i = 0; i < items.length; i++) {
this.items.push(items[i].data);
}
this.after = "t3_" + this.items[this.items.length - 1].id;
this.busy = false;
}.bind(this));
};
return Reddit;
});
I've just understood: by using this I can have access to properties in Reddit object.
Is it only because var Reddit is assigned an anonymous function and I need to bind this of the anonymous function to this of Reddit.nextPage, so they refer to the same properties?
But I can crearly see it is possible to have access to those properties even without the bind() method. See:
if (this.busy) return;
this.busy = true;
I've read some articles about the topic, but none exaplains it in depth: I'm really confused about.
Lets look at these functions:
Reddit.prototype.nextPage = function() {
// outer function
...
$http.jsonp(url).success(function(data) {
// inner function
}.bind(this));
};
Without binding, this in inner function would have different properties, since it's in another context. But if we call bind(this) we tell inner function to use this from outer function's context.
For more information I recommend this article.
I haven't visited the blog post, but I'm guessing that the reason it has been moved to be declared on the prototype is to have it automatically included in each instance of your "Reddit" service. Every time your service is created it will include this method, as all prototype methods are automatically inherited.
Regarding the bind, whenever you are passing a function as an argument, when the function is to get executed it will lose the main context, meaning it will not be bound to your Reddit service any more, because it will have a new scope of execution. Therefore calls to this.items, this.busy and this.after would all be undefined and would cause errors.
Here's some more info on the bind(), call() and apply().
this is context dependent. An example:
var foo = {
bar: function() {
console.log(this.baz);
},
baz: 3
};
foo.bar(); // logs 3
But in an asynchronous callback the context is gone, an example using setTimeout:
var foo = {
bar: function() {
setTimeout(function() { console.log(this.baz); }, 0);
},
baz: 3
};
foo.bar(); // logs undefined or throws an error in strict mode
'this' is no longer in the context of foo. We can get around that limitation using bind:
var foo = {
bar: function() {
setTimeout((function() { console.log(this.baz); }).bind(this), 0);
},
baz: 3
};
foo.bar(); // logs 3
We've now bound the context to foo (the value of this at the call site) which is what's going on in your example, the binding of this in the callback passed to the success handler of the promise returned by $http.jsonp.
This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 7 years ago.
Sorry for the title, but it's really hard to put a title on this issue (feel free to suggest a better one).
My issue is best explained via this JsFiddle: http://jsfiddle.net/ryLhcn4q/2/
CS = {};
CS.OverviewController = {
foo: "bar",
render: function() {
console.log("Rendering. foo = " + this.foo);
}
};
CS.handleClick = function(callback) {
console.log("handleClick");
if (callback) {
callback();
}
};
$("button").click(function(e) {
CS.handleClick(CS.OverviewController.render);
});
If you click on the button, the console indicates that the value of foo is undefined. How can I have the correct scope of this, so that the console indicates bar as the value for foo?
You should use
CS.handleClick(CS.OverviewController.render.bind(CS.OverviewController));
.bind defines the context of the function. Or in other words it sets its this object to something.
Your example doesn't work because you refer a function out of its context. The this points to the global scope (i.e. the window object). So:
window.foo = 'global';
$("button").click(function(e) {
CS.handleClick(CS.OverviewController.render);
});
will output global -> jsfiddle
PFB the code, this will work
CS = {};
CS.OverviewController = {
foo: "foo",
render: function() {
console.log("rendering. foo = " + this.foo);
}
};
CS.handleClick = function(e, context, callback) {
console.log("handleClick");
if (callback) {
context[callback]();
}
};
$("button").click(function(e) {
CS.handleClick(e, CS.OverviewController, 'render');
});
JsFiddleUpdated
You can also use "apply" or "call"
This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Explanation asked about the value of 'this' in Javascript [duplicate]
(2 answers)
Closed 8 years ago.
Simple question. Why do we have set that = this? If we dont, we are in the global scope...but why?
var myObj = {
specialFunction: function () {
},
anotherSpecialFunction: function () {
},
getAsyncData: function (cb) {
cb();
},
render: function () {
var that = this;
this.getAsyncData(function () {
// this now refers to global scope....why?
that.specialFunction();
that.anotherSpecialFunction();
});
}
};
myObj.render();
Writing that = this doesn't change the scope. The way the anonymous function is called will always end up with this being global object,* because that's exactly what the spec says should happen. Using that = this is just a workaround.
You could make this always point to myObj by using Function.call:
var myObj = {
specialFunction: function () {
},
getAsyncData: function (cb) {
cb.apply(this);
},
render: function () {
this.getAsyncData(function () {
this.specialFunction();
});
}
};
and/or using Function.bind:
var myObj = {
specialFunction: function () {
},
getAsyncData: function (cb) {
cb();
},
render: function () {
function callback() {
this.specialFunction();
}
this.getAsyncData(callback.bind(this));
}
};
* Unless you're in strict mode, in which case this is undefined.
take a look at the this keyword in JavaScript and how it works. I’m sure we’ve all come across this issue:
$("myLink").on("click", function() {
console.log(this); //points to myLink (as expected)
$.ajax({
//ajax set up
success: function() {
console.log(this); //points to the global object. Huh?
}
});
});
this is a variable that is automatically set for you when a function is invoked. The value it’s given depends on how a function is invoked. In JavaScript we have a few main ways of invoking functions. I wont talk about them all today, but just the three ways most people use them; either when a function is called as a method, or on it’s own, or as an event handler. Depending on how a function is invoked, this is set differently:
function foo() {
console.log(this); //global object
};
myapp = {};
myapp.foo = function() {
console.log(this); //points to myapp object
}
var link = document.getElementById("myId");
link.addEventListener("click", function() {
console.log(this); //points to link
}, false);
Doing $("myLink").on("click", function() {}) means that when the element is clicked, the function is fired. But this function is bound as an event handler, so this is set to the reference to the DOM element myLink. The success method you define within the Ajax request is just a regular function, and as such when it’s invoked, this is set to the global object, as it is when any function that’s not an event handler or an object method is.
$("myLink").on("click", function() {
console.log(this); //points to myLink (as expected)
var _this = this; //store reference
$.ajax({
//ajax set up
success: function() {
console.log(this); //points to the global object. Huh?
console.log(_this); //better!
}
});
});
Source: http://tinyurl.com/melbl92
EDIT: in JavaScript the "this" context depends on how your function is called, example:
function helloWorld()
{
console.log(this);
}
And here two ways to call this function:
new helloWorld(); note that if you call your function in this
way, the context of this will be the context of the function +
prototype, so your console will show this: helloWorld {}
helloWorld(); if you call your function without of the "new",
the context of "this" will be global(Window), so your console will show
this: Window about:home
Ok, with this little explanation i will try to explain now why you
have sometimes to use self/that...
Imagine that you want to use this.name inside this.hello function. Like I said before, the context of "this" depends on how your function is called, so if you want to ensure that this.name inside of this.hello function refer to this.name outside is recommended that you use self/that to avoid what happens bellow
function helloWorld(){
var self = this;//or that = this
this.name = "YourName"
this.hello = function(){
console.log(this); //the context of "this" here will be: "hello {}"
return this.name; //undefined, because you don't have name attribute inside hello function
}
new this.hello(); //note how hello is called here...
}
var test = new helloWorld();
And here a good explanation about context x scope:
http://ryanmorr.com/understanding-scope-and-context-in-javascript/