Non Strict
In non-strict mode AND in standard JS environment, ‘this’ is an empty literal object.
1 |
console.log(this) // {} |
Within a standalone function, global object is called global.
The
1 2 3 4 5 6 |
function tryIt() { console.log(this) // global console.log(this===global) // true } tryIt(); |
The only difference is that the ‘this’ in functions, is tied to global. In strict mode,
the ‘this’ in functions is undefined and not attached to anything.
1 2 3 4 5 |
// non-strict, so this is attached to global object function f1() { return this; } console.log(f1() === global); // true |
Strict mode
In strict mode, the global object is called global. And ‘this’ is an empty literal object.
However, when you are inside a standalone function, the ‘this’ is undefined and not attached.
1 2 3 4 5 6 7 8 9 10 |
"use strict" console.log(this) // {} console.log(global) // global object data function tryIt() { console.log(this===undefined) // true } tryIt(); |
or…
1 2 3 4 5 6 |
function f2() { 'use strict'; // see strict mode return this; } console.log(f2() === undefined); // true |
Adding properties to global variable
1 2 3 4 5 6 7 8 |
function tryIt() { console.log(this===global) // true global.haha = "hehe" } tryIt(); console.log(global.haha) // hehe |
all functions inherit call and apply from Function.prototype
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// An object can be passed as the first argument to call or apply and this will be bound to it. var obj = {a: 'Custom'}; // This property is set on the global object global.a = 'Global'; function whatsThis() { return this.a; // The value of this is dependent on how the function is called } console.log(whatsThis()); // global object's 'a' property console.log(whatsThis.call(obj)); // obj's 'a' property console.log(whatsThis.apply(obj)); |
Passing literals instead of objects
If the value passed as this is not an object, an attempt will be made to convert it to an object using the internal ToObject operation.
So if the value passed is a primitive like 7 or ‘foo’, it will be converted to an Object using the related constructor, so the primitive number 7 is converted to an object as if by new Number(7) and the string ‘foo’ to an object as if by new String(‘foo’), e.g.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function add(c, d) { return this.a + this.b + c + d; } var o = {a: 1, b: 3}; console.log(add.call(o, 5, 7)); console.log(add.apply(o, [10, 20])); function bar() { console.log(this) } bar.call(7) // [Number: 7] bar.call("foo") //[String: 'foo'] |
Bind
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
console.log("---- bind -----") function f() { return this.a; } // bind object to function f, pass to g var g = f.bind({a:'qwerty'}); console.log(g()); // bind object to function f, pass to h var h = f.bind({a:'yoo'}); console.log(h()); // however, can't bind twice var x = g.bind({a:'hohoho'}); console.log(x()); // still qwerty // literal object // then attach properties to function definitions var o = { a: 1134, functionF: f, // functionF definition is func f functionG: g, // functionG definition is func f bound to {a:'qwerty'} // so in func f's this.a, it will access from {a:'qwerty'} because they are bound // and not access 'a' from this literal object o. functionH: h // functionH definition is func f bound to {a:'yoo'} // so in func f's this.a, it will access from {a:'yoo'} because they are bound // and not access 'a' from this literal object o. }; console.log(o.functionF()); // 1134 console.log(o.functionG()); // qwerty console.log(o.functionH()); // yoo |
Arrow functions
The idea is that arrow functions, when first created, set its this to its enclosing lexical context.
In other words, it retains the value of the this of its enclosing scope.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
console.log(this) // {} // when created, foo's this is already set // we can't re-set anymore var foo = (() => { console.log("inside arrow function:") console.log(this) // {} // because this retains the value of the // enclosing lexical context's this // outside, it is {}, therefore, its also {} here }) foo(); // call and see the output |
Let’s try to manipulate it and see if we can change it.
We try to do this by creating a literal object. Then use various ways to bind that object to our foo function’s this.
1) Object calls function
In the literal object, we create a property and have its reference pointing to the arrow function.
Then we simply execute the arrow function via the object’s property like so:
1 2 3 4 5 6 7 8 9 10 |
// for example, let's try re-setting it different ways like so: var obj = { func: foo }; // trying to set "this" by having object's property reference function // then calling that function. obj.func() // still uses enclosing lexical context. // not calling object "obj" |
output:
inside arrow function:
{}
As you can see, the “this” in the arrow function does not reference its calling object ‘obj’. It references ‘this’ in its enclosing context.
2) Next, let’s try to bind the “this” in the arrow function to its calling object via call.
We get the reference to the arrow function and execute “call” on it. We would think we can bind the obj in the parameter to foo’s this. However, that is not the case. The ‘this’ in the arrow function is still bound to its enclosing lexical context.
1 2 |
foo.call(obj) // still uses enclosing lexical context's "this". // not call bound literal object "obj" |
3) This time, we’ll try using bind. We get the reference to the arrow function “foo”, call bind on it, and provide “obj” as the “this”. However, when we call foo, it does not work. It till uses its enclosing lexical context’s “this”.
1 2 3 |
// setting foo reference to where we bind the function to object literal 'obj' foo = foo.bind(obj) foo(); |
Therefore, no matter what, foo’s “this”, it is set to what it was when it was created.
Their “this” remains that of the enclosing lexical context.
Returning an arrow function, and evaluating ‘this’
So the point of this example is to show the difference between calling
1 2 |
var fn = obj.bar(); fn(); |
and
1 2 |
var fn2 = obj.bar; fn2(); |
..as both have very different results.
First, let’s define the environment.
We have a literal object obj which has a public property bar. bar is a function that returns
an arrow function that logs “this”.
In the bar function, I also log “this”. That way we can compare.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var obj = { // function reference 'bar' bar: function() { console.log("--- obj calling function bar --- ") console.log("√ bar's this is: ") console.log(this) var x = (() => { console.log(" --- in arrow function --- ") console.log(this) }); // returns a reference to a function return x; } }; // object literal |
First, we simply execute the bar function. Because the calling scope is obj, the “this” will belong to literal object “obj”.
1 2 3 4 |
console.log("\n----- 1 -------\n") // bar returns reference to a function, then called again // to execute that function reference. var x = obj.bar()() |
Therefore, the logging of “this” will be:
√ bar’s this is:
{ bar: [Function: bar] }
We then execute what bar has returned, which is the arrow function. The output is:
— in arrow function —
{ bar: [Function: bar] }
This is because since the “this” in bar references the literal object “obj”, the arrow function’s this references the enclosing context, which is also “obj”.
In the next example, its the same concept, except we just separate the execution of bar and the arrow function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
console.log("\n------ 2 ---------\n") // If you execute the function off of the "obj" // and then store the function into fn, the execution of bar // happens off the calling object "obj". var fn = obj.bar(); console.log("\n------ 3 ---------\n") fn(); // output: // --- in arrow function --- // { bar: [Function: bar] } // as you can see, the this is the "obj" literal object |
Finally, we simply reference the function provided by obj. It does not take into account
of obj. fn2 simply is a reference to the standalone function bar. Whereas before,
variable fn referenced an literal object’s execution of its function.
Thus, if you were call it as a standalone function, your calling scope is global.
Before, in the previous example, the calling scope is the literal object.
1 2 3 4 5 6 7 8 9 |
console.log("---- fn2 references a standalone function ----") var fn2 = obj.bar; // so when we do run it here, the arrow function bar will see: fn2(); // that its enclosing scope is global // the calling scope is global. // where as in obj.bar(), the calling scope is the obj. |
For arrow functions, the “this” will always be what it was set to initially.
As an Object Method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
var o = { prop: 37, // property "prop" is 37 // property f is function that returns "this" object's property "prop" f: function() { console.log(this) // { prop: 37, f: [Function: f] } console.log(this.prop); } }; o.f(); // the this binding is only affected by the most immediate member reference var o2 = {prop: 37}; function independent() { console.log(this) } // append property f to the object o2 and assign it to independent. o2.f = independent; o2.f(); //{ prop: 37, f: [Function: independent] } // It matters only that the function was invoked // from the f member of o // attach property b to object o2 to object o2.b = { g: independent, prop: 42 }; o2.b.g(); //{ g: [Function: independent], prop: 42 } // calls property g, which is func independent. // thus, the 'this' will be attached to // the object referenced by b. And not o2 itself. |
this with a getter or setter
ref – https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
ref – http://chineseruleof8.com/code/index.php/2018/01/15/getter-setter-js/