I was expecting the 2nd call of the "taco" function to generate a runtime error since I am not calling it with the "this" keyword:
function foo() {
var bar = "baz";
this.taco = function() {
console.log(bar);
};
this.taco();
taco(); // I expected a runtime error here.
}
foo();
However, it does not.
Here is a fiddle of the same code: http://jsfiddle.net/phillipkregg/gdFxU/226/
Is JavaScript using some type of implicit context management here?
Just curious, thanks!
The reason is that when you call foo(), you are invoking it in the scope of the window object. That means that inside of foo(), the value of this is set to window.
Thus, this.taco() is actually window.taco() which is the same as taco(). In other words, taco() is a global function so it works either way you call it as taco(), as window.taco() or as this.taco() when this is window.
If you involve taco() as a new object like this where this is set to a new instance of foo and is not equal to window, then you get the expected run-time error:
function foo() {
var bar = "baz";
this.taco = function() {
console.log(this);
console.log(bar);
};
this.taco();
taco(); // I expected a runtime error here.
}
var x = new foo();
Example: http://jsfiddle.net/jfriend00/3LkxU/
If you are confused about the value of this, there are these javascript rules that determine the value of this:
If you call a function with new like x = new foo(), then a new instance of foo is created and the value of this is set to that object inside the foo() function and that new instance is returned from foo() by default.
If you call any function normally like foo(), then the value of this is set to be the global object which in a browser is window or if in javascript's newer "strict" mode, then this will be undefined. This is what was happening in your original example.
If you call a method with an object reference like obj.foo(), then this will be set to be the obj object.
If you make a function call with .apply() or .call(), then you can specifically control what the value of this is set to with the first argument to .apply() or .call(). For example: foo.call(obj) would call the foo() function and set the this pointer to the obj object.
If you are not in any function call (e.g. at the global scope), then this will be either the Global object (window in a browser) or undefined in strict mode.
As in all of the above rules, this is controlled by how the caller calls you, not how the function/method is defined.
The reason is this in foo(), when it's called just as is, will reference a global object. This means taco function will be introduced in the global scope.
To get the functionality you want use new foo() syntax instead. Then this will refer to a new object, which taco property will be assigned a new value (function). Calling taco directly will get you an error then.
foo(); // equals window.foo() , `this` equals `window` and `this.taco` equals `window.taco` and `window.taco` equals `taco` as it is global
new foo(); //creates a new object. this will give error because here `this.taco` is not `taco`
Related
I'm reading the You Don't Know JS books and am on this and Object Prototypes.
I get that to know what this refers to I need to look at the call-site. And the call site is what this will refer to. What I don't get is why this code isn't working as I think it should (I only wrote it to understand this, not for any working problem or anything).
function foo() {
console.log(foo.hasOwnProperty('a')); // 1
console.log(window.hasOwnProperty('a')); // 2
console.log(this.a); // 3
this.a++;
console.log(window.hasOwnProperty('a')); // 4
}
function bar(){
foo.a = 42;
foo();
}
bar();
If you look at the first line in bar which creates an a property for foo and assigns it the value 42. If I comment this line out, then running console.log(foo.hasOwnProperty('a')); gives me false. And if I have it run, then it returns true. But if this is the case, then calling bar is indeed creating an a property for foo, right? This leads to question 3.
I get that at this point window.a does not exist.
Why does this return undefined? this should resolve to foo.a, right? Because the context of this would be in bar, correct? Bar is the call-site. However, this remains undefined regardless of whether foo.a = 42 is commented out or not.
Why does this return true now, after running this.a++? How and why is a global variable being created?
Note: this is just an easy way of thinking about this. In complicated cases (such as when using .bind() or OOP), the following might not be accurate.
this, by default sorta refers to the object that contains the function being called. For example, if I did
var obj = {
fxn: function() {
console.log(this.bar);
},
bar: 3
}
obj.fxn(); // (1)
var fxn = obj.fxn();
fxn(); // (2)
(1) Will print "3" to the console; Because you are, in a way, calling fxn "through" obj, this will point to obj. In other words, because you are calling the copy of fxn that is stored inside of obj, this will point to obj.
(2) Will (assuming bar has not been defined elsewhere) print undefined. That is because fxn isn't being called "through" anything. As a result, it pretends that fxn is being called through window.
Although in your example, foo is being called in bar, bar does not call a version of foo being... well, stored inside itself. bar does not call foo... "through" foo.
Admittedly, that's pretty confusing. Maybe this will clear up some confusion: If you change bar to this:
function bar(){
foo.a = 42;
foo.foo = foo;
foo.foo();
}
it will print 42 instead of undefined. The important part is that the object that foo is being called through will be this. For instance,
function bar(){
var baz = {};
baz.a = 42;
baz.foo = foo;
baz.foo();
}
Will also print 42.
In foo this refers to the parent object - i.e. window.
so this.a and window.a are referring to the same thing.
Because the context of this would be in bar, correct?
Incorrect. For bar to be "this" in foo you would need to apply or call or bind foo passing your desired "this" as context. i.e.
foo.call({});
or
foo.bind({});
Would make "this" and empty object within foo.
Why does this return true now, after running this.a++? How and why is
a global variable being created?
For the same reason - when you do this.a++ you are creating and incrementing a global variable a by one.
All the articles and books I read say that it doesn't matter where I define my function, it's where I invoke the function that decides which object 'this' is bound to. But with this example, I don't understand why 'this' is bound to global object.
function foo(){
console.log(this);
}
var obj = {
myMethod: function() {
foo();
}
};
obj.myMethod();
As I understand, 'obj' object is the one that invokes the function so 'this' should be bound to 'obj', but the result is window object. Can anyone please explain this for me?
this is a strange beast in JavaScript, so you'd do well to read the overview on MDN.
But in summary, it doesn't matter where the function was originally defined, but what does matter is the location from which it's being executed...
If the function is defined globally and you invoke it using myFunction(x), then it's executing in the global scope, and this will refer to the global object (as you have discovered in your foo() function where this is the Window object)
If a function is contained in an object's property then this will refer to the object that contains the property. (Object properties which contain functions are called "methods")
For example...
myFunction = function() { console.log(this); }
myObject = {
myMethod: myFunction
}
myFunction(x) // `this` == the global window object.
myObject.myMethod(x) // `this` == `myObject`
NOTE 1. You can also invoke functions using 'call', 'apply', and 'bind'
which enable you to specify what this should refer to inside the function when it runs. (Read about them in detail on the link I provided.)
NOTE 2. The ES6 "arrow function" syntax ((x)=>{ ... }) is an exception, as this will always be the same value as this would be wherever the arrow function itself was defined.
When foo() is created it is created with a closure . When it is invoked it still remembers its scope through this closure with which it was created and at that time, this was bound to global object.
You're calling foo from within myMethod, so it isn't obj directly calling foo. To have this point to obj you should assign foo directly to myMethod.
e.g.
function foo() {
console.log(this);
}
var obj = {
myMethod: foo
};
obj.myMethod();
I was under the impression that the "this" keyword represents the current owner that is in scope. Obviously, this is wrong. Let me get to the code:
alert(this); // alerts as [object Window] -- Okay
function p1() {
alert(this);
}
var p2 = function() {
alert(this);
}
p1(); // alerts as undefined -- ???
p2(); // alerts as undefined -- ??
window.p1(); // alerts as [object Window] -- Okay
window.p2(); // alerts as [object Window] -- Okay
The code above first alerts [object Window], as I would expect but then the next two calls to p1() and p2() alert "this" as "undefined". The final two calls to p1() and p2() alert "this" as [object Window].
Isn't it the case that p1() and p2() exist in the global (i.e., window) scope? I thought that calling window.p1() is synonymous with calling p1(), just like calling alert() is synonymous with window.alert().
To my (C#) way of thinking, p1() and p2() are in the global scope. These functions are members of the global window object so when they refer to "this" they should be referring to [object Window]. Obviously, I'm very wrong here.
Becasue you are using strict mode and as per the spec:
If this is evaluated within strict mode code, then the this value is not coerced to an object.
The code you have does alert window in all instances of alert, but because you are in strict mode, it is undefined (as it should be)
UPDATE: chrome dev tools alerts window not undefined, however if you wrap it in a self executing function you get undefined as expected
(function(){
'use strict';
alert(this);
}());
When you call a function like foo(), then the value of this depends on whether the code runs in strict mode or not (the default actually).
In strict mode, this will be undefined (as you already found out).
In "loose mode" this will indeed refer to window, but that's not because the functions are global, i.e. "owned" by the global scope. You get the same behavior for local functions:
(function() {
function foo() {
console.log(this); // logs the window object, but `foo` is not global
}
foo();
}());
This is simply an explicitly defined behavior:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
...
As you can see, in "loose" mode, if thisArg is undefined, which is the case if you call the function "normally" as foo(), then it will be set to window explicitly.
this is what denotes you are referring to that object you are. It is mostly used in Object Oriented Programming to differentiate between an object's self versus input variables.
Example (pseudo code):
var1 //instance variable of object
void Constructor(var1){
this.var1 = var1
}
What this describes is the constructor takes in a var called var1, and sets it's own variable var1 to it.
In your case, I would assume that when in a function:
function p1() {
alert(this);
}
being called as p1();, it is not strictly in an object context, therefore this does not mean anything. By calling it from the window's context window.p1();, you are making the window object call the function, therefore giving this a value.
Suppose I have functions like:
function foo() {
}
function bar() {
}
I can write above as Object Literal notation:
var Baz = {
foo: function() {
},
bar: function() {
}
};
As far as I understand in the later case, an instance of Baz will be created when the script loads regardless if any Baz function is ever called. In the former case, function object is only created when that function is called. Am I correct about these assumptions?
If I am correct then the former would have higher performance (less memory) than the later in application where these functions are rarely called.
But the advantage of the later is that it gives greater modularity and lower global namespace pollution.
What is your take on this from your professional experience?
Is there a speed difference?
In the former case, function object is only created when that function is called.
No, the functions are created regardless.
Note that you can also do this:
function foo() {
}
function bar() {
}
var Baz = {
foo: foo,
bar: bar
};
Or this:
var Baz = (function() {
function foo() {
}
function bar() {
}
return {
foo: foo,
bar: bar
};
})();
The primary purpose of putting the functions on Baz as properties is to make them available as "methods" on Baz. This might be for convenience, for "namespacing", etc. In your first form (and my first form above), if that code is at global scope, foo and bar are added to the global scope, which can get pretty crowded pretty fast (esp. on browsers). In your second example, the only global symbol is Baz because the functions are anonymous. In my final example above, the only global symbol is Baz but the functions aren't anonymous, they have names that debuggers and stack traces can show you (which is a good thing; more here).
In terms of trying to optimize when functions get created, here's how it works: When execution enters a given context (the global context, or the context related to calling a function), these things are done:
A behind-the-scenes execution context object is created.
A behind-the-scenes variable object for that execution context is created.
In the case of a function context:
A property is added to the variable object for arguments (the array-like thing you can use to access arguments)
A property is added to the variable object for each of the function's named arguments, with the value of the argument
If the function has a name, its name is added as a property of the variable object and has the value of the function object.
Properties are created on the variable object for each variable declared with var in the execution context; their values are initially undefined (regardless of whether the var has an initializer on it).
Every function declaration in the context is processed. (Function expressions are not processed yet; more on the difference below.) A property on the variable object for each function name is created and receives the function object as its value.
Step-by-step code execution begins.
Like all expressions, function expressions are evaluated when they're encountered in the step-by-step flow.
var statements that have initializers (e.g., var a = 2;) are treated exactly like assignment statements (a = 2;); the var aspect of it was done much earlier. (var is frequently misunderstood. For instance, we had this question just yesterday.)
You'll note the difference above between function declarations and function expressions. You can tell which is which by looking to see whether you're using the result as a right hand value — that is, are you assigning the result to a variable, using it as the right-hand side of a property definition in an object literal, or passing it into a function. If you are, it's a function expression. If you're not, it's a function declaration.
Function declaration example:
function foo() {
}
Function expression example:
var foo = function() {
};
Another:
var Baz = {
foo: function() { }
};
(The foo line is a property declaration in an object literal that uses a function expression for the value.)
Named function expression example:
var f = function foo() { // <== Don't do this (more below)
};
Named function expressions should be valid, but they're poorly-supported by implementations in the wild (particularly IE) and so for now they must be avoided. More here.
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).