In javascript, there are two data types in javascript, Primitive Types and Reference Types.
Let see the primitive types first. Primitive types are data that are not an object and has no methods, and they are immutable.
number, null, boolean, string, undefined, and symbol are primitive types.
'use strict';
// here are all Primitive Types
var a = 123;
var b = 45.6;
var c = NaN;
var d = true;
var e = "this is string";
var f = 'this is another string';
var g = undefined;
var h = null;
var i = Symbol("foo");
How about Reference Types. Reference Types are different from Primitive Types; they are mutable, no fixed size.
'use strict';
// here are all Reference Types
var a = {};
var b = new Object();
var c = [0,1];
var d = new Array;
var e = new String('this is string');
var f = function(){return true};
var g = new Function('return false');
// these are mutable
function foo(){
console.log("I am function");
}
var bar = "I am string"
a.foo = foo; a.bar = bar;
b.foo = foo; b.bar = bar;
c.foo = foo; c.bar = bar;
d.foo = foo; d.bar = bar;
e.foo = foo; e.bar = bar;
f.foo = foo; f.bar = bar;
g.foo = foo; g.bar = bar;
// output should be "I am string" and "I am function" for all line
console.log(a.bar); a.foo();
console.log(b.bar); b.foo();
console.log(c.bar); c.foo();
console.log(d.bar); d.foo();
console.log(e.bar); e.foo();
console.log(f.bar); f.foo();
console.log(g.bar); g.foo();
Reference Types Calls Function
If there is reference type calls function, and inside the function (not its inner function) contain this keyword.
Then this keyword will reference to the who invokes the function.
'use strict';
function foo(para){
console.log(this === para);
}
foo(undefined); // window/global; it is similar to window/global.foo(); if in strict mode, undefined
var obj = new Object();
obj.foo = foo;
obj.foo(obj); // obj; obj invokes foo();
var obj_2 = obj.foo;
obj_2(undefined); // window/global; it is similar to window/global.obj_2(); if in strict mode, undefined
console.log(foo === foo.prototype.constructor); // true;
foo.prototype.constructor(foo.prototype); // foo.prototype invokes constructor()
For this part, a better way to figure out this keyword is using the following image.
fig 1. easy way to figure out "this" keyword
Calling Function With Grouping Operator
There is a little bit tricky when using grouping operator () such as invoking a function by using closure.
Dmitry Soshnikov's post gives a good example,
And I will show my mix example below.
"use strict";
(function (){
console.log(this === undefined) // window/global; if in strict mode, undefined
})();
var closure_1 = (function (para){
console.log(this === para)
}).prototype;
closure_1.constructor(closure_1); // true
var foo = {
bar: function (para) {
console.log(this === para);
}
};
// also is a good example for "This In Inner Function"
var foo2 = {
bar: function (para) {
console.log(this === para);
return function(para2){
console.log(this === para2);
}
}
};
// also is a good example for "This In Inner Function"
var foo3 = {
bar: function (para) {
console.log(this === para);
return {
func:function(){
console.log(this);
}
}
}
};
var foo4 = {
bar:{}
}
foo4.bar.com = foo.bar;
foo.bar(foo); // 'this' points to foo, obvious
(foo.bar)(foo); // 'this' points to foo too, errrrrr
foo2.bar(foo2)(undefined); //foo2, window/global; if in strict mode, undefined
(foo2.bar(foo2))(undefined); //foo2, window/global; if in strict mode, undefined
foo3.bar(foo3).func(); //foo3, object of {func:......};
(foo3.bar(foo3)).func(); //foo3, object of {func:......};
(foo3.bar(foo3).func)(); //foo3, object of {func:......};
foo4.bar.com(foo4.bar); // 'this' points to foo4.bar, no doubt
(foo4.bar.com)(foo4.bar); // 'this' points to foo4.bar, errrrrr
(foo.bar = foo.bar)(undefined); // window/global; if in strict mode, undefined
(false || foo.bar)(undefined); // window/global; if in strict mode, undefined
(true && foo.bar)(undefined); // window/global; if in strict mode, undefined
(foo.bar, foo.bar)(undefined); // window/global; if in strict mode, undefined
In Dmitry Soshnikov's post, he gave a pseudo-code of getting the real value of an object from a value of Reference type
and applied this pseudo-code to explain assign operator, logic expression, comma operator situation.
I would conclude that using
closure, assign operator, logic expression, and comma operator
inside a grouping operator cause this points to window/global;
others reference types expression in grouping operator would just get back itself.
This In Inner Function
"use strict";
// common case of getting backing another function from a function call
function fooOrigin(){
return function bar(para){
console.log(this === para);
}
}
var objOrigin = {
func:{}
}
objOrigin.func = fooOrigin();
objOrigin.func(objOrigin); // 'this' points to objOrigin
///////////////////////////////////////
// common case of inner function call
function fooOrigin_inner(para){
function bar(para){
console.log(this === para);
}
bar(para); // can assume as window/global.bar(para),
}
fooOrigin_inner(undefined) // window/global; if in strict mode, undefined
function fooOrigin_inner2(para){
function bar(para){
function tim(para){
console.log(this === para);
}
tim(para); //can assume as window/global.tim(para),
}
bar(para);
}
fooOrigin_inner2(undefined) // window/global; if in strict mode, undefined
///////////////////////////////////////
// function in an object
var foo = {
bar: function (para) {
function tim(para){
function mee(para){
console.log(this === para);
}
mee(para); // can assume as window/global.mee(para),
}
tim(para);
}
};
foo.bar(undefined); // window/global; if in strict mode, undefined
// link function to an object property
var foo1 = {
obj:{
func:{}
},
bar:function (para) {
foo1.obj.func = function tim(para){
console.log(this === para);
}
foo1.obj.func(para); // foo1.obj calls func(para)
}
};
foo1.bar(foo1.obj) // 'this' points to foo1.obj
var foo2 = {
obj:{
func:{}
},
bar:function (para) {
function tim(para){
foo2.obj.func = function mee(para){
console.log(this === para);
}
foo2.obj.func(para) // foo2.obj calls func(para)
}
tim(para);
}
};
foo2.bar(foo2.obj) // 'this' points to foo2.obj
var foo3 = {
obj:{
func:{}
},
bar:function (para) {
foo3.obj.func = function tim(para){
function mee(para){
console.log(this === para);
}
mee(para) // can assume as window/global.mee(para),
}
foo3.obj.func(para);
}
};
foo3.bar(undefined) // window/global; if in strict mode, undefined
var foo4 = {
bar: function (para) {
console.log(this === para);
return function(para2){
console.log(this === para2);
}
}
};
foo4.bar(foo4)(undefined) // foo4, window/global; if in strict mode, undefined
var foo5 = {
bar: function (para) {
console.log(this === para);
return {
func:function(){
console.log(this);
}
}
}
};
foo5.bar(foo5).func() // foo5, object of {func:......};
From fooOrigin_inner() and fooOrigin_inner2() we can see this points to window/global,
so do foo,foo3, foo4.
In contrast, this refers to foo1.obj and foo2.obj in foo1 and foo2;
so does foo5.
Dmitry Soshnikov explains in Activation Object way; I will conclude upper code in another way.
The difference between this referring window/global or obj would be
what function is directly wrapping this and who is directly calling the function.
This in Object Constructing
Talking with function called as constructor, I will combine Dmitry Soshnikov's example and his explanation with my undersding in the code below.
"use strict";
var x = 10;
function Something() {
console.log(this);
console.log(this.x);
this.x = 200;
}
var st = new Something(); // Something, undefined (since x haven't assigned value at Creation Stage),
console.log(st.x); // 200
Something(); // window/global, 10; if in strict mode, 'this' will point to undefined and error ccour
function SomethingElse(){
console.log(this === SomethingElse); // false
console.log(this.__proto__ === SomethingElse.prototype); // true
console.log(this.__proto__.constructor === SomethingElse.prototype.constructor); // true
}
var ste = new SomethingElse();
When using new in object creating, new will call the internal construct function.
After it created, this will point to the created object.
Binding This With Objects
Ok, I am tired to figure out where this refers to.
Fortunately, Function.prototype.call() and Function.prototype.apply() can bind this without care
where it original points to.
"use strict";
var foo = function() {
return function(para){
console.log(this === para);
}
}
var tom = {};
var jim = {};
foo()(undefined); // window/global; if in strict mode, undefined
foo().call(tom, tom); // "this" binds to "tom" object, true
foo().call(jim, tom); // "this" binds to "jim" object, false
foo().apply(jim, [jim]) // "this" binds to "jim" object, true
foo().apply(jim, [tom]) // "this" binds to "jim" object, false
If you are unsure what is the value of this in your code,
just simply use Function.prototype.call() and Function.prototype.apply()
to make sure this points to the proper object.