I am trying to understand some basic javascript .
I have the following snippet. We have the name variable which in both cases are referenced over getName Method.
First alert outputs HocusPocus, while the second one outputs GeorgeThomas. But I do not understand how this refers to name in this case
var name = 'Gerorge Thomas';
var obj = {
name: 'Cinderella',
value: {
name: 'HocusPocus',
getName: function() {
return this.name;
}
}
};
alert( obj.value.getName() );
var testing = obj.value.getName;
alert(testing());
To understand this issue, you have to understand how Javascript sets the value of this in a function call.
There's a summary of all the methods here: When you pass 'this' as an argument
For your particular case, when you do this:
var testing = obj.value.getName;
You now have a reference to the getName function. You no longer have any connection at all to obj.value. So, test() just calls getName as an ordinary function. In Javascript when an ordinary function call is made, then the value of this will be either the global object (window in a browser) or in strict mode, it will be undefined.
In your case, this becomes the window object so this.name is window.name which points to the global variable name and thus you get the result 'Gerorge Thomas'.
In fact, if you run your code in strict mode, it will cause an error which is, in fact, one of the benefits of strict mode that it points out accidental errors like this.
On the other hand, if you execute a function with the form of obj.method(), then this is set to be obj. So, when you do:
obj.value.getName()
that is equivalent to:
var o = obj.value;
o.getName()
which is the obj.method() form of calling a function which will set the this pointer to be the object, which in this case is obj.value.
It is possible to work around this issue in a couple of ways. Here's an example of working around it using .bind().
var name = 'Gerorge Thomas';
var obj = {
name: 'Cinderella',
value: {
name: 'HocusPocus',
getName: function() {
return this.name;
}
}
};
document.write( obj.value.getName() + "<br>");
var testing = obj.value.getName.bind(obj.value);
document.write(testing());
Well it only needs some reflection, let's analyse it:
In the first alert we are calling the getName() method of the
obj.value object so it returns obj.value.name which is
"HocusPocus".
alert( obj.value.getName() ); we call this.name where this refers to obj.value so name is HocusPocus
But in the second alert we are creating a new function testing()
with the body of the getName() method, so it will be attached to
the global window object and if we call it we will get the global
name value wich is 'Gerorge Thomas'.
alert(testing()); we are dealing with this.name where this refers to the global scope so name is 'Cinderella'.
First, you're calling a method from inside the obj object. The 2nd time, you're making a copy of the function (not really, it's just a reference) in the global scope. So you could also write window.testing() for the 2nd call. I think that makes it clear.
Related
So, I've got some code that looks like this (genericised from a closed-source project).
UserWizard = {
init: function(name) {
this.firstInit.call(name);
this.secondInit.call(name);
},
firstInit: function(name) {
// ...
},
secondInit: function(name) {
// ...
}
}
It's the first time I've ever seen the call method used in JS and it appears to be exactly the same as just calling the function with brackets, e.g.
this.firstInit(name);
So what is call doing here? Does it act any differently?
When calling
UserWizard.init('some name');
The this object of the firstInit and the secondInit functions will be the string 'some name' and the parameter name value will be undefined
UserWizard.firstInit('some name')
Is the same as:
UserWizard.firstInit.call(UserWizard, 'some name');
Hope I was clear
call() is not doing what you think it's doing here. It's actually changing the context of this within both firstInit and secondInit.
Function.prototype.call() is the link to the mozilla docs, quoting from there there:
A different this object can be assigned when calling an existing function. this refers to the current object, the calling object. With call, you can write a method once and then inherit it in another object, without having to rewrite the method for the new object. - by Mozilla Contributors
There's another function Function.prototype.bind() which I'd encourage you to look at. I find generally I use this more often, but it's a similar sort of idea, used to assign this to the function when it may get called later on. This is good for preventing issues like:
var person = {
name: 'First Last',
getName: function (){
return this.name;
}
};
var getName = person.getName;
getName(); // returns undefined as `this` is the global window object
I saw the code below in a text book on closures:
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function () {
return function () {
return this.name;
};
}
};
alert(object.getNameFunc()()); // "The Window"
A detailed explanation on why the this object in the inner function cannot access the object scope but rather accesses the global scope could be of great help.
Thanks for the help.
In Javascript, every single function call causes a new value of this to be set and if it is just a plain function call, then this is set to either the global object (which is window in a browser) or undefined (if running in strict mode). That's what is happening for you. In this line:
object.getNameFunc()()
object.getNameFunc() makes the first method call and returns the internal anonymous function. Then, the last () is just a regular function call of that function and that regular function call causes this to get set to the global object (window in your case). Any plain function call resets the value of this to window or undefined. This is how Javascript works.
Here's a simple demo that show you how this is reset:
See this answer for the five ways that the value of this is controlled.
You can work around this issue in several ways by either saving the value of this in the parent function or using one of the methods to explicitly control the value of this.
One of the simpler mechanisms to understand is to just save the value you want in the parent function:
var myName = "The Window";
var object = {
myName: "My Object",
getNameFunc: function () {
var self = this;
return function () {
return self.myName;
};
}
};
document.write(object.getNameFunc()()); // "My Object"
Or, you can use .bind():
var myName = "The Window";
var object = {
myName: "My Object",
getNameFunc: function () {
return function () {
return this.myName;
}.bind(this);
}
};
document.write(object.getNameFunc()()); // "My Object"
FYI, I changed your variable name to myName so there could never be any confusion in following what was happening with window.name which already exists in the browser environment.
Break it down into separate function invocations. Instead of
object.getNameFunc()()
let's break that down, call the first function, then invoke the result of that.
var theFunc = object.getNameFunc(); // invocation 1
theFunc(); // invocation 2
in invocation 1 we call getNameFunc(), and we specify a context (we have something before the . ) So within that invocation "this" has a specified value, object
In the second case we are calling the function "bare", no specified context, so we have the global context.
In getNameFunc(), this refers to object. It returns a function which is not bound to a context, just to the containing scope. The context (what this refers to) is not inherited (although scopes are).
Inner functions do not inhert the context which they are defined in, although they do inherit the scope they are defined in.
One way to work around this is to .bind() the returned function to the context:
getNameFunc: function () {
return function () {
return this.name;
}.bind(this);
}
I'm learning JavaScript and going through this tutorial on jQuery's website.
In following example
// A function being attached to an object at runtime
var myName = "the global object";
var sayHello = function ()
{
console.log("Hi! My name is " + this.myName);
};
var myObject = {
myName: "Rebecca"
};
var secondObject = {
myName: "Colin"
};
myObject.sayHello = sayHello;
secondObject.sayHello = sayHello;
sayHello(); // "Hi! My name is the global object"
myObject.sayHello(); // "Hi! My name is Rebecca"
secondObject.sayHello(); // "Hi! My name is Colin"
I don't see expected output when sayHello() is invoked. Instead variable is undefined. But if I define global variable by assigning it to window.myName it works.
I'm using Chrome Version 25.0.1364.152 m.
Is tutorial incorrect or am I missing something ?
Full HTML is here: http://pastebin.com/4M27vDB4
UPDATE: Accepted answer explains what happened. I also want to mention possible solution - declaring global variable above without var. Because of following:
Furthermore, variables that are declared inside a function without the
var keyword are not local to the function — JavaScript will traverse
the scope chain all the way up to the window scope to find where the
variable was previously defined. If the variable wasn't previously
defined, it will be defined in the global scope, which can have
unexpected consequences.
You have put this code inside
$(document).ready(function ()
// ...
});
closure. In this case context inside it will not be window (it will be document object) and you will get undefined as you describe.
in your program you have used this.myName. this keyword is used to point current object. when you call only sayHello() then in that case "this" means "window" because default current object is window. Now you have not defined window.myName then it will give "undefined".
I've been reading through quite a few articles on the 'this' keyword when using JavaScript objects and I'm still somewhat confused. I'm quite happy writing object orientated Javascript and I get around the 'this' issue by referring the full object path but I don't like the fact I still find 'this' confusing.
I found a good answer here which helped me but I'm still not 100% sure. So, onto the example. The following script is linked from test.html with <script src="js/test.js"></script>
if (!nick) {
var nick = {};
}
nick.name= function(){
var helloA = 'Hello A';
console.log('1.',this, this.helloA);
var init = function(){
var helloB = 'Hello B';
console.log('2.',this, this.helloB);
}
return {
init: init
}
}();
nick.name.init();
What kind of expected to see was
1. Object {} nick.name, 'Hello A'
2. Object {} init, 'Hello B'
But what I get is this?
1. Window test.html, undefined
2. Object {} init, undefined
I think I understand some of what's happening there but I would mind if someone out there explains it to me.
Also, I'm not entirely sure why the first 'console.log' is being called at all? If I remove the call to the init function //nick.name.init() firebug still outputs 1. Window test.html, undefined. Why is that? Why does nick.name() get called by the window object when the html page loads?
Many thanks
Also, I'm not entirely sure why the first 'console.log' is being called at all?
nick.name = function(){
// ...
}();
Here you define a function, call it immediately (hence ()) and assign its return value ({init: init}) to nick.name
So the execution is:
Create a variable called nick if there isn't one with a non-falsey value already
Create an anonymous function that…
Creates a variable called helloA in its own scope
Outputs data using console.log containing "1" (as is), this (the window because the function is executing in the global context instead of as a method), and this.helloA (window.helloA, which doesn't exist.
Defines a function called init
Returns an object which gets assigned to nick.name
Then you call nick.name.init() which executes the init function in the context of name.
This defines helloB
Then it console.logs with "2" (as is), this (name), and this.helloB (nick.name.helloB - which doesn't exist)
So the first output you get is from console.log('1.',this, this.helloA);
I think your main problem is that you are confusing this.foo (properties on the object on which a method is being called) with variable scope (variables available to a function)
It's much simpler if you think about this as a function, not as a variable. Essentially, this is a function which returns current "execution context", that is, the object the current function was "applied" to. For example, consider the following
function t() { console.log(this)}
this will return very different results depending upon how you call it
t() // print window
bar = { func: t }
bar.func() // print bar
foo = { x: 123 }
t.apply(foo) // print foo
this is defined on a per-function basis when the function call is made. When you call a function as o.f(), this will be o within the function, and when you call it as f(), this will be the global object (for browsers, this is the window).
You wrote nick.name = function(){...}(); and the right-hand part is of the form f(), hence the Window.
var foo = bar; defines a local variable. It may not be accessed as this.foo (well, except when you're at global scope, but that's silly). To define a member, you usually write this.foo = bar; instead.
This is what your code does:
It creates an object and assigns to the variable nick.
It creates an anonymous function.
It calls the function (in the window scope).
It assigns the return value (an object containing the init property) to the name property of the object.
It gets the value from the init property, which is a method delegate, and calls the method.
The anonymous function does this:
It declares a local variable named helloA and assigns a string to it. (Creating a local variable doesn't add it as a property to the current object.)
It logs this (window) and the helloA property (which doesn't exist).
It creates an anonymous function and assignes to the local variable init.
It creates an object with the property init and the value from the local variable init.
The anonymous function assigned to the init property does this:
It declares a local variable named helloB and assigns a string to it. (Creating a local variable doesn't add it as a property to the current object.)
It logs this (the object from the name property, not the nick variable), and the helloB property (which doesn't exist).
this is one of most mystery feature in JavaScript, after assigning the object method to other variable, the binding (this keyword) is lost
var john = {
name: 'John',
greet: function(person) {
alert("Hi " + person + ", my name is " + this.name);
}
};
john.greet("Mark"); // Hi Mark, my name is John
var fx = john.greet;
fx("Mark"); // Hi Mark, my name is
my question is:
1) what is happening behind the assignment? var fx = john.greet;
is this copy by value or copy by reference?
fx and john.greet point to two diferent function, right?
2) since fx is a global method, the scope chain contains only global object. what is the value of this property in Variable object?
john.greet("Mark") actually calls a function. When you do var fx = john.greet;, you're getting a reference to the function. So when you call it, this is not bound to john. What you're actually doing is window.fx("Mark") and so this is the window object. You were on the right track when you said that it was in the global context. In this particular instance, the global object is window, and so fx is actually window.fx.
When you have a function reference you should use call or apply if you want to set the value of this. Try doing this:
fx.call(john, "Mark");
The first argument in call or apply is the value used for this in the context of the function call.
EDIT
Some people mentioned that the real issue here might be confusion surrounding an object literal vs. an instance of an object. You're creating an object literal which also behaves kind of like a singleton. You cannot create a new instance of that object. In this case john is a reference to that object literal. In that context, this in the function greet refers to the object literal itself. Hence when you call john.greet("Mark"), this is bound to john.
When you grab a reference to john.greet just by itself and assigning it to a global variable, you're essentially doing this:
var fx = function(person) {
alert("Hi " + person + ", my name is " + this.name);
}
In this scenario, this is window, because fx is basically window.fx (since the global object here is window. Assuming this code was wrapped inside another function, then the global object would refer to that function.
If you want to create multiple instances of an object, you can do something like this:
var Person = function(name) {
var self = this; //maintains a reference to the instance
this.name = name;
this.greet = function(name) {
alert("Hi " + name + ", my name is " + self.name);
}
}
var john = new Person("John");
john.greet("Mark"); // alerts "Hi Mark, my name is John"
var fx = john.greet;
fx("Mark"); // also alerts "Hi Mark, my name is John"
Here, the self variable (which is local to the function) maintains a reference to the actual instance because you're binding it to this when you create the object.
There are many best practices associated with OOP in Javascript. You can Google and find out (there are many links). I recommend reading stuff from Douglas Crockford especially.
1) fx and john.greet are referring to the same function object, the assignment operation for objects, works by reference.
For primitive values, like String, Number, Boolean undefined or null, a copy of the value will be made.
2) The this value refers to the global object.
The this value is not a property of the Variable Object and it has nothing to do with the scope chain, is a special reserved word, and it is determined implicitly when a function is called (you can also set it explicitly via call or apply).
JavaScript internally handles a Reference type, which consists of two components, the base object and the property name, when a function is invoked, the this value is determined implicitly by getting the base object (by the internal GetValue operation).
And finally, the last case where this is set implicitly is when you invoke a function with the new operator, the this keyword will refer to a newly created object.
So in brief, here is how this works implicitly:
1- When a function is called as a method (the function is invoked as member of an object):
obj.method(); // 'this' inside method will refer to obj
2- A normal function call:
myFunction(); // 'this' inside the function will refer to the Global object
// or
(function () {})();
3- When the new operator is used:
var obj = new MyObj(); // 'this' will refer to a newly created object.
As I understand it, you're only assigning that method to the variable "fx." The context of the john object doesn't come along with it.
Off the top of my head, "this" in the context of fx will refer to the global object, which in the context of a browser is (I believe) equivalent to your window object.
(editing to clarify global object. Sort of)
Because you're only setting fx to the greet method and not the entire john object, it has no concept of it's parent and becomes globally scoped. So in essence, it's passing by value in that in only copies the method.
Since the function is now globally scoped, "this" becomes the Window object.
If you instead set fx to john, you get what's expected.
var john = {
name: 'John',
greet: function(person) {
alert("Hi " + person + ", my name is " + this.name);
}
};
john.greet("Mark"); // Hi Mark, my name is John
var fx = john;
fx.greet("Mark"); // Hi Mark, my name is John
inspired by #Vivin Paliath answer, actually I come out something new. As to me, I always try my best to make javascript programming the same way as java, especially in OOP.
So my suggestion is to avoid using this as possible as we can , when we first do
var self = this;
we should use self instead of this in all function (prototype function, whatsoever), but if we write something like this:
function MyObject = {
var self = this;
};
MyObject.prototype = {
method1 = function(data){
self.data = data;
}
}
This is not gonna work, because prototype is an object in MyObject, It can not access private member self owned by MyObject. My solution for this is simple:
function MyObject = {
var self = this;
MyObject.prototype.method1 = function(data){
self.data = data;
};
}
This takes the advantage of prototype's efficiency and also we do not have to care about all the this issues. Though we gonna type a lot of MyObject.prototype.xxxx thing.
If this helpful to your guys, please give me some thumb up, so I can gather 15 reputation to thumb up others, thanks.