Javascript scope different depending how you pass callback function - javascript

function DoSomething()
{
var scope = this;
}
Consider the following methods of invoking it:
Option 1:
var self= this;
$someElement.change(self.DoSomething);
Option 2:
var self= this;
$someElement.change(function(){self.DoSomething();});
Why is it that the when the change event is triggered, the first line of code results in scope being the element that triggered the event, but the second results in a scope that is the same as self but the second?
Since I don't understand the concept at hand here, it has been difficult for me to Google the correct search term.

jQuery calls the given function within a context of original element.
Option 1:
You have passed self.DoSomething as a function.
jQuery will call self.DoSomething within a context of $someElement:
The following code is executed:
var self = this;
self.DoSomething.call($someElement);
function DoSomething()
{
// this = $someElement
var scope = this;
}
within DoSomething this is equal to callee - $someElement.
Option 2:
You have passed anonymous function as a function.
jQuery will call anonymous function within a context of $someElement:
var self = this;
function() {
self.DoSomething();
}.call($someElement);
function() {
// this = $someElement, as well
self.DoSomething(); // LOOK! You call self's method. Not within any context
}
function DoSomething()
{
// default function call, default this is presented
}

Related

javascript:why anonymous function passed as argument cant access "this" in object? [duplicate]

Hi I have following JavaScript code that I am trying to run. My aim is to grasp the meaning of this in different scopes and different types of invocations in JavaScript.
If you look in code below: I have a inner anonymous function, which is getting assigned to innerStuff variable. In that anonymous function as such this points to window object and not the outer function object or anything else. Event though it still has access to out function's variables.
Anyway, I am not sure, why that would be; but if you look at code below, I pass this in form of that to innerStuff later and it works just fine and prints object with doofus attribute in console.
var someStuff = {
doofus:"whatever",
newF: function()
{
var that = this;
console.log(that);
var innerStuff = function(topThis){
console.log(topThis);
};
return innerStuff(that);
}
}
someStuff.newF();
Now I am changing a code only little bit. And instead of assigning it to innerStuff, I'll just directly return the function by invoking it as shown below:
var someStuff = {
doofus:"whatever",
newF: function()
{
var that = this;
console.log(that);
return function(that){
console.log(that);
}();
}
}
someStuff.newF();
This prints undefined for the inner anonymous function. Is it because there is a clash between a that that is being passed as parameter and a that defined in outside function?
I thought the parameter would have overriden the visibility. Why would the value be not retained?
This is utterly confusing.
On the other hand if I don't pass that, but instead just use it, because visibility is there, the outcome is proper and as expected.
What is it that I am missing? Is it the clash between the variables, present in same scope?
Is there a good reason, that inner functions have this bound to window object?
this in JavaScript refers to the object that you called a method on. If you invoke a function as someObject.functionName(args), then this will be bound to that object. If you simply invoke a bare function, as in functionName(args), then this will be bound to the window object.
Inside of newF in the second example, you are shadowing the that variable in your inner function, but not passing anything into it, so it is undefined.
var that = this;
console.log(that);
return function(that){
console.log(that);
}();
You probably want the following instead, if you want something that is equivalent to your first example (passing that in to the inner function):
var that = this;
console.log(that);
return function(that){
console.log(that);
}(that);
Or the following, if you don't want to shadow it and just use the outer function's binding:
var that = this;
console.log(that);
return function(){
console.log(that);
}();
In your second example, when you invoke the anonymous function, the parameter that is not defined (you aren't passing anything to it.) You can do this:
newF: function()
{
var that = this;
console.log(that);
return function(that){
console.log(that);
}(that); // note that we are passing our 'that' in as 'that'
}
That will keep the proper value of the variable around.
However, since you are scoping var that above, you could just remove the function parameter as well:
newF: function()
{
var that = this;
console.log(that);
return function(){
console.log(that);
}(); // 'that' is referenced above.
}
As far as why anonymous functions have window as their this: whenever you call a function without a context (i.e. somef() vs context.somef()) this will point to the window object.
You can override that and pass a this using .apply(context, argumentsArray) or .call(context, arg1, arg2, arg3) on a function. An example:
newF: function()
{
console.log('Outer:', this);
var innerF = function(){
console.log('Inner:', this);
};
return innerF.apply(this,arguments);
}
In your first code example, the anonymous function, though declared within a function that is a member of the someStuff object, is not a member of the someStuff object. For that reason, this in that function is a reference to the window object. If you wanted to invoke the anonymous function and have control over the this reference, you could do the following:
var someStuff = {
doofus:"whatever",
newF: function()
{
var that = this;
console.log(that);
var innerStuff = function(){
console.log(this);
};
return innerStuff.apply(this);
}
}
someStuff.newF();
In your second example, your actually creating an anonymous function, executing it, and then returning the value that the anonymous function returned. However, your anonymous function did not return anything. Additionally, you have a variable name conflict. You could do:
var someStuff = {
doofus:"whatever",
newF: function()
{
var that = this;
console.log(that);
return function(){
console.log(that);
return true;
}();
}
}
someStuff.newF();
I added the return true because your function should return something, since the function that is executing it is returning the return value of the anonymous function. Whether it returns true or false or a string or an object or whatever else depends on the scenario.

Is there a functional purpose for setting a variable to 'this'? [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 8 years ago.
As in, sometimes when I look at code by other people, they will go var self = this; or in jquery for example, go var $self = $(this);
is there a particular reason for doing so?
It preserves the value of this for use in functions defined inside the current function.
// Contrived example
var myObject = {
func: function () {
var self = this;
setTimeout(bar, 1000);
function bar () {
alert(this); // `window`
alert(self); // `myObject`
}
}
};
myObject.func();
By holding a reference to this in some context, you have the ability to access it in other contexts such as within member functions or forEach loops.
Consider the following example:
function ViewModel() {
var self = this;
self.linksArray = ["link1", "link2", "link3"];
self.linksArray.forEach(function(link) {
// this refers to the DOM window
// and self refers to the parent context (ViewModel)
});
};
As others have mentioned, you could set a variable to $(this) if you wish to use it in another function.
On practical example would be when doing an ajax call tied to an event on the page. Using JQuery:
<script>
$(document).on("click", ".mySelector", function () {
// Where we are in the click event, $(this) refers to whatever
// element has a class of mySelector that was clicked
var self = $(this);
theDiv.html('');
$.ajax({
cache: false,
type: "GET",
url: "/SomeAjaxMethod",
data: { },
success: function (data) {
// Trying to access $(this) here will return undefined, as
// we are technically in the callback method
// Where our event is based on a class, there is likely more
// than one element on the page with the class, so it would be
// difficult to get the exact element again without some other code
self.html(data);
},
error: function (xhr, ajaxOptions, thrownError) {
alert("Ajax failed.")
}
}); // end ajax call
}); // end on mySelector class click
</script>
or:
<script>
$(document).ready(function () {
$('.foo').click(function () {
var self = $(this); // Whatever element that was clicked with foo class
$('.bar').each(function () {
var bar = $(this); // Current iteration of bar element in the loop
var baz = self; // self is still the initial value, but $(this) is not
}); // end bar loop
}); // end foo click
}); // end doc ready
</script>
The particular example (not using JQuery) is the function closure. Referencing this in a function closure refers to the function object, not the context in which the closure was defined. Your example is one way to deal with the closure problem:
var that = this;
function(){
that.something = 1;
}();
Another way to deal with this is with the apply method on the function:
function(arg){
this.something = 1;
}.apply(this, argumentArray);
The first argument in apply is the "this argument" that "this" will refer too.
One purpose of that would be to make this accessible to inner functions. for example:
function clickHandler(){
console.log(this); // this is body
var $self = this;
function inner(){
console.log(this); // this is window
console.log($self); // this is body
}
inner();
}
$("body").click(clickHandler);
Run it in console to get a sense.

JS - called method is not a function

I have object:
var devark = {
init: function() {
var obj = this;
obj.assignHandlers();
},
assignHandlers: function() {
var obj = this;
document.getElementById("menu-toggler").onclick = obj.documentFunctions[0];
},
documentFunctions: [
function() {
toggleClass(this, "opened");
}
]
};
on window.load, I am calling the init method. That works fine but when it calls another object method assignHandlers, it throws an error:
[17:54:33.192] TypeError: obj.assignHandlers is not a function
Why is it?
Like #Bergi said, it's a this value issue that can be solved by doing:
window.onload = function () {
devark.init();
};
The difference between both ways is the value of this within the init function. To determine the natural value of this, look at the left-side of the . in a method call, such as obj.someFn();. Here the value of this within someFn will be obj. However, when you do window.onload = devark.init;, you can imagine the handler will later be invoke like window.onload(). Which means the value of this within the onload function, which is really the init function will be window, not devark.
You can also use Function.prototype.bind to bind a specific this value to a function.
window.onload = devark.init.bind(devark);

Why I cannot access variable through this in Windows 8 JavaScript App

My code is very simple. Ans to me it should work.
var preview = WinJS.Class.define(
function (el, options) {
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
this.preview.addEventListener('click', this.click, false);
//WinJS.Utilities.query("button", this.form)
//this.preview.addEventListener('', this.save, false);
},
{
click: function (e) {
this.form.style('display', 'block');
}
}
);
WinJS.Namespace.define('RegCtrl', { preview: preview });
But when click occurs this.form seems to be undefined of null. Why? I do not want to initialize objects in every method of the class.
New tests
I made additional test very small
var preview = WinJS.Class.define(
function (el, options) {
var test = 1;
this.test = 1;
this.test1();
},
{
test1: function () {
console.log(this.form, test);
}
}
);
WinJS.Namespace.define('RegCtrl', { preview: preview });
This test fails on line this.test1();. What I think now that this class is called RegCtrl.preview() rather than new RegCtrl.preview().
How do I shek inside the function that this called as new but not a simple function?
The other answers aren't explaining what's going on, and as such are giving incorrect advice.
JavaScript has first-class function objects - you can pass them around as values. That's exactly what you're doing when you set up this callback:
this.preview.addEventListener('click', this.click, false);
You're taking the contents of the this.click property, which happens to be a function, and handing it to the addEventListener function to do whatever it wants with it.
I was very specific about terminology there - note I specifically said function, not method. JavaScript doesn't really have a method construct, it just has methods as properties on an object.
So where does the "this" member come from? It's determined at the caller - the object you use on the left side of the '.' is the one that becomes the value of this. For example,
function exampleFunc() { console.log("this.myName = " + this.myName); }
var a = { myName: "Chris", doSomething: exampleFunc };
var b = { myName: "Bob", doSomething: exampleFunc };
Note I've assigned the exact same function to the doSomething properties. What what happens:
a.doSomething(); // Outputs "this.myName = Chris"
b.doSomething(); // Outputs "this.myName = Bob"
The exact same function object, called through two different objects, has a different this pointer.
exampleFunc is a global function, let's call it:
exampleFunc() // Outputs "this.myName = undefined"
So where'd the undefined come from? In a global function, "this" is set to window (the global scope), which didn't have the myName property defined. Which also means that you could do this instead:
myName = "Global Name"; // note, no var - we want this global
exampleFunc(); // Outputs "this.myName = Global Name"
Ok, so what's going on with the original question? Basically, you've passed the function this.click to be the callback, but you haven't passed the "this" pointer that you want it called through. Actually, addEventListener doesn't have a way to pass the this pointer. As a result, when the function is invoked this is not pointing at your object. I don't remember off the top of my head what it's pointing at - it's either window or the element that was clicked on, check the DOM documentation to verify.
To get it to call the right function with the right context (context = the correct "this"), the traditional approach is to use a closure. Capture "this" in a variable, then pass in an anonymous function that calls your actual callback with the right this pointer. The code looks like this:
var preview = WinJS.Class.define(
function (el, options) {
// Capture your current this pointer in a global variable
// Using "that" as the name comes from JavaScript: The Good Parts book
var that = this;
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
// Note what gets passed instead of this.click:
this.preview.addEventListener('click',
function (e) {
// NOTE: Calling through "that": "this" isn't pointing to the right object anymore
// Calling through "that" resets "this" inside the call to click
that.click(e);
}, false);
},
{
click: function (e) {
this.form.style('display', 'block');
}
}
);
This is a common enough pattern that ECMAScript 5 has a utility function to build these wrappers for you - function.bind. Do this:
this.preview.addEventListener('click',
this.click.bind(this),
false);
The construct this.click.bind(this) will construct a new function that, when called, will set the "this" reference to whatever you passed (in this case "this"), and then invoke the function you called it on.
Yes, there are a lot of different values for "this" floating around. Keeping track of what "this" is pointing at is an important part of mastering JavaScript programming.
I think you may want to define a global JavaScript variable as :
var myForm = document.getElementById('perview-form');
or jest define var myForm; and initialize inside function (el, options) as:
myForm = d.getElementById('perview-form');
Now you can use this variable in your function as :
myForm.style('display', 'block');
EDIT: I believe you may define this variable as first line in your WinJS.Class.define to make it instance level variable as below:
var preview = WinJS.Class.define(
var myForm;
function (el, options) {
....
....
myForm = d.getElementById('perview-form');
...
},
{
click: function (e) {
myForm.style('display', 'block');
}
});
This is a really hard thing to research if you don't know what to look for. I added one line and changed another line. That should fix your issue.
In short, the keyword this gets reset every time you enter a new function, this the value of this inside your click function is not the same this of the outer scope. Preserve this this you want. The name of that seems fairly common.
Edited based on the link provided by the OP.
This code is UNTESTED. If using this doesn't work now, then I'd try this2
Sorry I can't test this, but I don't have the framework anywhere so I'm doing
educated guesswork.
var preview = WinJS.Class.define(
function (el, options) {
that = this; // No var should be needed since it is declared already
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
this.preview.addEventListener('click', this.click, false);
//WinJS.Utilities.query("button", this.form)
//this.preview.addEventListener('', this.save, false);
},
// This is the section for instance vars
{
click: function (e) {
that.form.style('display', 'block'); // AND THIS ONE
},
that: null // Added instance variable
},
// And these are static variables
{
that2: null
}
);

I don't understand why the "this" keyword doesn't work as I expect

What I want to do is to execute the create_tag function when a specified condition is satisfied. I am referring to this function as a method of an object, in this case document.body, by setting as its method an external function, "create_tag(..)". The problem is inside this function I have a "this" keyword which I would expect to refer to the method's parent, document.body. Instead it doesn't seem to work. I tried replacing "this" with "document.body" in the function so the problem should be caused by "this".
Here is the code:
xmlDom=xmlhttp.responseXML;
hint_ul=document.getElementById("hint_ul");
personaggi=xmlDom.documentElement.getElementsByTagName("personaggio");
for(i=0;i<personaggi.length;i++){
personaggio=personaggi.item(i);
name=personaggio.childNodes[1].firstChild.nodeValue;
if(name.substr(0, str.length).toLowerCase()==str.toLowerCase()){
document.body.crea_li=create_tag(name);
}
}
}
function create_tag(inner){
a=document.createElement("a");
a.innerHTML=inner;
this.appendChild(a); }
this will be window when called like that.
To get its this as the body element, call it like so...
document.body.crea_li = create_tag.call(document.body, name);
Nowhere in your code is create_tag assigned as a method of document.body. The closest you get is with the line document.body.crea_li=create_tag(name);, but what's actually happening here is that you are executing create_tag as a member of the global object, and the result of that operation is assigned to document.body.crea_li.
You could make a reference to this outside the function body - referencing it within the scope later:
var self = this;
function create_tag(inner){
a=document.createElement("a");
a.innerHTML=inner;
self.appendChild(a);
}
This could be a nice trick. When I make complicated javascript objects involving many objects and functions, at the top of the object I create:
var self = this;
as that will live within the scope, the root object is always accessible.
Here is a working example of how I would implement this:
SomeReallyComplexThing = function() {
var self = this;
var foo = 'bar'
this.fooThing = 'Other thing'
this.setSomeData = function(){
console.log('Some data set', arguments)
}
this.makeMassiveCall = function() {
var completeFunc = function(){};
var url = '/some/endpoint.json';
var requestData = {};
jQuery.get(url, requestData, function(data) {
/*
* Data has come back
*/
self.setSomeData(data)
completeFunc(data);
});
}
}
//outside the scope
s = new SomeReallyComplexThing()
s.fooThing() //visible
s.self //undefined
this in javascript is a sqirrely fellow. The idea is this refers to the current function context.
This means that when your running code inside the function this refers to that function's context, which does not have an appendChild method.
Normally you use a closure to keep a reference to the calling context around, something like this
var _self = this;
var result = func();
function func()
{
// _self is the calling context, this is the current context
}
Or you could pass a reference to the calling context:
document.body.crea_li=create_tag(name,this);
function create_tag(inner, context) { context.body.appendChild(...) }
this is referring to the function's parent, but its parent is actually the window object, not the document object or document.body. this actually refers to wherever context the function is called from, and in my opinion you should avoid using it to call methods just for that reason because it can be difficult to see what this is actually referring to. For example, if you called a function using this from another function, it would refer to the context within that function.
This example might help show what's going on:
var hello = function() {
alert( this.message );
}
window.message = "hello!";
hello()
You could document.body directly in the code like you suggested before, or you could pass another parameter that tells the function where to append the created tag:
function create_tag(inner, elementToAddTag){
a=document.createElement("a");
a.innerHTML=inner;
elementToAddTagTo.appendChild(a);
}

Categories