JavaScript: Scope of 'this' within a function - javascript

I'm somewhat new to JavaScript and have a few questions about scope that don't seem to have been explicitly asked about before. At the top of a function I'm working on in angular a variable vm is set equal to this. I understand that anything prefaced with vm going forward will be in scope of this however how is this different from being in the function's scope to begin with? To be more explicit, how would vm.foo = "test" differ from var foo = "test" inside of a function in terms of its scope. Any help would be much appreciated.

If you set vm = this, then properties of vm can persist beyond the scope of the current function invocation. In contrast, a local variable value (e.g., var foo = "test") does not persist past the current function invocation.
Basically, this.foo within a function is not equivalent to var foo within that same function. The first actually references a property value on the this object, whereas the second only references a local variable in the current function invocation scope.
Here's an example to illustrate this difference:
function myFunction(myArg) {
console.log("this.foo = " + this.foo);
console.log("foo = " + foo);
var foo = "test";
console.log("foo' = " + foo);
var vm = this;
console.log("vm.foo = " + this.foo);
vm.foo = myArg;
console.log("vm.foo' = " + this.foo);
}
console.log("window.foo = " + window.foo);
console.log(">>> Test call 1");
myFunction("abc");
console.log("window.foo = " + window.foo);
console.log(">>> Test call 2");
myFunction("xyz");
console.log("window.foo = " + window.foo);
For convenience, here is the console output:
window.foo = undefined
>>> Test call 1
this.foo = undefined
foo = undefined
foo' = test
vm.foo = undefined
vm.foo' = abc
window.foo = abc
>>> Test call 2
this.foo = abc
foo = undefined
foo' = test
vm.foo = abc
vm.foo' = xyz
window.foo = xyz
As you can see, this inside the function actually refers to the global window object. That means that the value of vm.foo that you assign inside the function is actually available anywhere that window is accessible (i.e., everywhere in your script). You can change what object is used as this if you invoke the function using its call method, and explicitly pass a different object as thisArg. You can also get a different object as this if you invoke the function as a method on some object. Here's an example illustrating both of these possibilities:
function myFunction(myArg) {
console.log("this.foo = " + this.foo);
console.log("foo = " + foo);
var foo = "test";
console.log("foo' = " + foo);
var vm = this;
console.log("vm.foo = " + this.foo);
vm.foo = myArg;
console.log("vm.foo' = " + this.foo);
}
var x = { f: myFunction };
var y = { f: myFunction, foo: "YYY" };
var z = { foo: "ZZZ" };
x.f("x"); // "this" is "x"
y.f("y"); // "this" is "y"
myFunction.call(z, "z"); // "this" is "z"
console.log("x.foo = " + x.foo);
console.log("y.foo = " + y.foo);
console.log("z.foo = " + z.foo);
Notice how the calls that use y and z for this have initial values of this.foo, since the corresponding objects are initialized with a value for the foo property. The value of this.foo persists in the object referenced by this, not in the function itself (unless of course this is referencing the function itself).

Inside a function, var foo = 'test' does not necessarily relate foo to this, because the value of this depends on how the function was called (see Function context).
Rather independently of the above, the reason for assigning this to vm is to keep its reference in case you want to have another function inside this function, where you refer to the original context of this. This is usually done within a controller function, see this Angular Style Guide.

Related

"this" is different when function called from local function object

I wrote the following code, where I would expect the calls to getFull() and useGetFull() to do the same thing, since useGetFull just gets a function object for getFull() and calls it.
function Person(name, family) {
this.name = name;
this.family = family;
}
Person.prototype.getFull = function() {
console.log(this.name + " " + this.family);
};
Person.prototype.useGetFull = function() {
const f = this.getFull;
f();
// f.call(this) works as expected
}
p = new Person("Bob", "Smith");
p.getFull();
p.useGetFull();
However, they don't do the same thing, because inside useGetFull(), "this" is the global object. I noticed that using f.call(this) instead of f() works as intended, but I can't wrap my head around why I have to use it. Why is the value of "this" different depending on how/where I call the function?
A simple rule:
a.b() //b called with context a
d.e() //e called with context d
c() // c called with no context ( so the global instead)
Javascripts context depends on how the function was called.
If you haven't noticed, this.getFull() also works. This is because when you invoke the function as a property of an object (any object), that function's this will refer to that object, the object you invoked the function on, in case of foo.bar(), foo, and in case of this.getFull(), this. This is why this example works as expected:
function Person(name, family) {
this.name = name;
this.family = family;
}
Person.prototype.getFull = function() {
console.log(this.name + " " + this.family);
};
Person.prototype.useGetFull = function() {
/* getFull is preceded by something, it is invoked through "this", so
the "this" reference inside of getFull will be set to the "this" part in
the statement "this.getFull()". */
this.getFull();
}
p = new Person("Bob", "Smith");
p.getFull(); prints out Bob Smith
p.useGetFull(); // prints out Bob Smith
However, when a function is invoked not as the property on an object, in other words, when it is not accessed in a way similar to either foo.bar() or foo["bar"](), but in a way like foo(), even if foo is a reference to a variable whose value is x.y, like f() in your example, its this will be bound to the global object, in a browser, that object is window.
function Person(name, family) {
this.name = name;
this.family = family;
}
Person.prototype.getFull = function() {
console.log(this.name + " " + this.family);
};
Person.prototype.useGetFull = function() {
const f = this.getFull;
/* this call is not preceded by any objects, it is a plain
traditional function call, it is a function invokation in its
simplest form. all functions invoked in this manner will have
their "this" reference set to the global object. */
f();
}
p = new Person("Bob", "Smith");
p.getFull();
p.useGetFull();
If you are interested in this (pun not intended), go here.

Can you turn an anonymous method (function) into a named method dynamically?

Given this method:
var obj = {}, obj.foo = function () {};
Is it possible to assign the method a name after it's created so it looks similar to:
var obj = {}, obj.bar = function bar() {};
The foo method is anonymous and assigned to a property. The bar method is named and assigned to a property.
Can you turn the anonymous foo method into a named foo method dynamically? Is there a property on the function object which can be set or something similar to:
obj.foo.<name> = Object.keys(obj)[0];
Without using jquery, please. Also, this is in a node server application so cross browser issues won't matter.
EDIT: The answer that worked for me is Daniel's link: How to dynamically set a function/object name in Javascript as it is displayed in Chrome. This approach also handles parameters for the function.
You cannot renane that precise instance of your anonymous function, but you can use a closure to redefine it changing its name, like this:
var obj = {};
// Anonymous function
obj.foo = function() {/* whatever */};
// Renaming...
obj.foo = (function(fn) {
return function foo() {
return fn.apply(this, arguments);
}
})(obj.foo);
// Now try in the console:
obj.foo
> function foo() {
return fn.apply(this, arguments);
}
Summarizing the two solutions found in other posts referenced above:
1) Dynamic function name in javascript?
2) How to dynamically set a function/object name in Javascript as it is displayed in Chrome
var foo, bar, name = "bar", renameFunction;
foo = function () {return "baz";}
bar = foo;
console.log(bar.name + " / " + bar()); // / baz;
//*************** Solution #1 **********************************************
renameFunction = function (name, fn) {
return (new Function("noName", "return function " + name +
"(){ return noName() };")(fn));
}
bar = renameFunction(name, foo);
console.log(bar.name + " / " + bar()); // bar / baz;
//*************** Solution #2 **********************************************
renameFunction = function (name, fn) {
return (new Function("return function (call) {return function " + name +
" () { return call(this, arguments) }; };")())(Function.apply.bind(fn));
};
bar = renameFunction(name, foo);
console.log(bar.name + " / " + bar()); // bar / baz;
They both work. The second solution is more complex, but I'm not sure yet what it adds.
UPDATE: The 2nd solution is the better one. It can handle arguments to the function which the first solution does not.

Javascript Function object call() in NODEJS

I have read through call() method and wondering on the global output below. Shouldnt it be this.name where in this case "Michael". However the output displayed is undefined.
---NEW INFORMATION : THIS IS RUNNING THROUGH NODEJS---
function sayNameForAll(label) {
console.log(label + ":" + this.name);
}
var person1 = {
name: "Nicholas"
};
var person2 = {
name: "Greg"
};
var name = "Michael";
sayNameForAll.call(this,"global"); //ouput global:undefined
sayNameForAll.call(person1,"PersonName1"); //PersonName1:Nicholas
sayNameForAll.call(person2,"PersonName2"); //PersonName2:Greg
The reason is probably that in this context, this is not the same scope. This can happen because of several reasons:
You're running this in a function (even a (function() { self invoking function })())
You're running this in nodejs and not in the browser, in node, in the global scope, this is undefined as opposed to window in the browser.
Works fine here (click Run Snippet).
Is your code running inside another function? If it is, then the variables you declare would not be on the global object and depending on how you are invoking the function, this might not refer to the global object.
function sayNameForAll(label) {
console.log(label + ":" + this.name);
}
var person1 = {
name: "Nicholas"
};
var person2 = {
name: "Greg"
};
var name = "Michael";
sayNameForAll.call(this,"global"); //ouput global:undefined
sayNameForAll.call(person1,"PersonName1"); //PersonName1:Nicholas
sayNameForAll.call(person2,"PersonName2"); //PersonName2:Greg

JavaScript, MooTools - Variable scope in Class/overwrite global variable

Can somebody explain me, why I am able to overwrite a method value of a global instance by just setting its value locally and why I am not able to do something similar with variables?
Is the only way to access the variable to use the window object hierarchy? Or is there maybe a shorter way?
(function() {
console.log(this);
var someVar = this.someVar = false;
var subClass = new Class({
test: false,
setValue: function(value) {
this.test = value
}
});
var subPub = this.subPub = new subClass();
var MainClass = new Class({
rewriteVar: function() {
console.log("someVar = " + someVar); // returns global value
console.log("subPub.test = " + subPub.test); // returns global value
someVar = true;
console.log("someVar local: " + someVar); // returns new local value
console.log("someVar global: " + window.someVar); // returns old global value
subPub.setValue(true);
console.log("subPub.test local: " + subPub.test); // returns new local value
console.log("subPub.test global: " + window.subPub.test) // returns new global value
}
});
/* var someObj = this.someObj = {};
var someVar = someObj.someMeth = false;
// And why is this possible?
var MainClass = new Class({
rewriteVar: function() {
someObj.someMeth = true;
console.log(window.someObj.someMeth); // returns new global value
}
}); */
window.addEvent("load", function() {
var test = new MainClass();
test.rewriteVar()
})
})()
This is to do with variable scope.
Javascript has functional scope.
So by doing:
var someVar = this.someVar = false;
You are declaring a local variable someVar and a global variable (which gets hoisted to the window object ie window.someVar), as this within your closure referes to the global scope ie window.
So when you write:
someVar = true;
You are overwriting the local variable with this new value.
Variables declared within a function definition are local to that function if you use the var key word:
(function () {
var name = 'Mark';
})();
// Out here you cannot access name
console.log(name);
(if i understood correctly the problem)
It has nothing to do with Mootools or the clases, #Felix_Kling already gave you the answer but I will ilustrate it with a simple example:
var aObj = bObj = {}; //since bObj is an 'object', aObj will store the objects reference
aObj.foo = "bar";
console.log(aObj.foo);
console.log(bObj.foo);
// output:
// "bar"
// "bar"
var a = b = 1; //since 'b' is primitive, 'a' will not store a reference of 'b', it will only copy it's value
a = 0;
console.log(a);
console.log(b);
// output:
// 0
// 1
i'm not really sure if this is what you were asking =) Hope this helps

Scope of self-invocation function in Javascript

Why does self-invocation function inside a function don't get the scope of the outer function in JavaScript?
var prop = "global";
var hash = {
prop: "hash prop",
foo: function(){
console.log(this.prop);
(function bar(){
console.log(this.prop);
})();
}
};
var literal = {
prop: "object"
};
hash.foo();
// hash prop
// global
hash.foo.call(literal);
// object
// global
Looks like altering the scope of the outer function has no effect on the scope of the inner self-invocation function.
PS: The question is not about how to alter the scope of the inner function. But what is the proper explanation in the "Javascript language" perspective? Does all self executing functions have 'global' scope by default? If so, why?
Your problem is the this and what it references:
foo: function(){
console.log(this.prop);
(function bar(){
console.log(this.prop); <--- this does not reference to foo here, but instead it refers to the window object
})();
}
You need to keep a reference to the outer this:
foo: function(){
console.log(this.prop);
var that = this;
(function bar(){
console.log(that.prop); <--- tada!
})();
}
Update
Some explanation. It's all about how JavaScript determines the context when invoking a function.
function Test() {
this.name = "Test";
this.bar = function() { console.log("My name is: "+ this.name);}
}
function Blub() {
this.name = "Blub";
this.foo = function() { console.log("My name is: " + this.name);}
}
var a = new Test();
var b = new Blub();
// this works as expected
a.bar(); // My name is: Test
b.foo(); // My name is: Blub
// let's do something fun
a.foo = b.foo; // make an educated guess what that does...
a.foo() // My name is: Test
Huh? Aren't we referencing the method of Blub? No we're not. We are referencing the unbound function of Blub.
JavaScript binds on . (dots) and based on that it decides waht the value of this should be.
Since you're not calling your anonymous function on an object (therefore no .) it will make this reference to the global object, which is - in case of the browser - the window object.
Another example (one might think this would work):
var str = "Hello World";
var ord = str.charCodeAt; // let's make a little shortcut here.... bad idea
ord(0) // no dot...
Instead of the char codes that are in str we get the ones that are in the global object, of course that's not a string so charCodeAt calls toString on which results in "[object DOMWindow]"
You are not applying any object as the this context when you call the inner function, so it gets this set to window by default. If you wanted to call the closure with the same this as the outer function, you would have to do:
(function bar(){
console.log(this.prop);
}).call(this);
Or:
var that = this;
(function bar(){
console.log(that.prop);
})();

Categories