This article is equipped with a personal video explanation, students who want to read the article can read the article, the article is not very clear, combined with the video explanation instantly understand. Learn more about JavaScript’s This Pointer

If you were asked what two things in javascript are confusing, scoped queries and this are at the top of the list. The scope was covered in detail in the previous section. This chapter introduces the This mechanism.

This binding rule

The default binding

  • In a global context, this is bound to window by default
console.log(this= = =window); //true
Copy the code
  • This is bound to the window by default when the function is called independently
function foo(){
    console.log(this= = =window);
}
foo();//true
Copy the code
  • This is bound to window by default when called independently by nested functions
var a = 0;
var obj = {
    a : 2.foo : function (){
        function test(){
            console.log(this);
        }
        test();
    }
}
obj.foo();
Copy the code

Although the test() function is nested within the obj.foo() function, the test() function is a stand-alone call, not a method call. So this is bound to window by default

IIFE

The IIFE immediate function is actually called immediately after the function is declared. The internal this refers to the window

var a = 0;
function foo(){(function test(){
        console.log(this.a); ()}});var obj = {
    a : 2.foo:foo
}
obj.foo();/ / 0
Copy the code

Equivalent to the above example

var a = 0;
var obj = {
    a : 2.foo : function(){
        function test(){
            console.log(this.a);
        }
        test();
    }
}
obj.foo();/ / 0
Copy the code

closure

Similarly, the test() function is called independently, not as a method call, so this is bound to the window by default

Note: there are four methods called in the function

var a = 0;
function foo() {
    function test() {
        console.log(this.a);
    }
    test();
}
var obj = {
    a: 2;
    foo: foo
}
obj.foo();/ / 0
Copy the code

Because the closure’s this is bound to the Window object by default, but you often need to access the nested function’s this, you often use var that = this in nested functions, and then replace this with that in closures, using scoped look-ups to find the nested function’s this value

var a = 0;
function foo(){
    var that = this;
    function test(){
        console.log(that.a);
    }
    return test;
};
var obj = {
    a : 2.foo:foo
}
obj.foo()();/ / 2
Copy the code

Implicit binding

Generally, a function call contained in a direct object, also known as a method-level call, is implicitly bound to this direct object

function foo(){
    console.log(this.a);
}
var obj1 = {
    a : 1;
    foo: foo,
    obj2 : {
        a:2.foo:foo
    }
}
The direct object of foo() is obj1, to which this is implicitly bound
obj1.foo();/ / 1
The direct object of foo() is obj2, to which this is implicitly bound
obj1.obj2.foo();/ / 2
Copy the code

Implicit loss

Implicit loss is when the implicitly bound function loses the bound object and is bound to the Window by default. This situation is error-prone but common

[Function alias]

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a: 1.foo:foo
}
// Assigning obj.foo to the alias bar causes implicit loss, because foo() is assigned to bar, which has nothing to do with obj objects
var bar = obj.foo;
bar();/ / 0
Copy the code
/ / equivalent to the
var a = 0;
var bar = function foo(){
    console.log(this.a);
}
bar();/ / 0
Copy the code

[Parameter transfer]

var a = 0;

function foo() {
    console.log(this.a);
}

function bar(fn) {
    fn();
}
var obj = {
    a: 2.foo: foo
}
Foo = obj. Foo = obj. Foo; fn = obj. Foo; fn = obj
bar(obj.foo);/ / 0
Copy the code
/ / equivalent to the
var a = 0;
function bar(fn){
    fn();
}
bar(function foo(){
    console.log(this.a);
})
Copy the code

[Built-in functions]

Built-in functions, like the above example, also cause implicit loss

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a : 2.foo:foo
}
setTimeout(obj.foo,100);/ / 0
Copy the code

[Indirect call]

“Indirect references” to functions are created unintentionally, most often during assignment, resulting in implicit loss

function foo(){
    console.log(this.a);
}
var a = 2;
var o = {a: 3.foo: foo};
var p = {a: 4};
o.foo();/ / 3;
// Assign the o.foo function to the p.foo function, and execute immediately. Equivalent to just an immediate call to foo()
(p.foo = o.foo)();/ / 2
Copy the code
// Another case
function foo() {
    console.log( this.a );
}
var a = 2;
var o = { a: 3.foo: foo };
var p = { a: 4 };
o.foo(); / / 3
// Assign the o.foo function to the p.foo function, and then the p.foo function executes, which is the execution of foo on the p object
p.foo = o.foo;
p.foo();/ / 4
Copy the code

[Other Circumstances]

Inside the javascript engine, obj and obj.foo are stored in two memory addresses, called M1 and M2 for short. When only obj.foo() is called like this, M2 is called from M1, so this points to obj. However, in each of the following cases, M2 is taken directly and the result is executed in the global environment (again M2), so this refers to the global environment

var a = 0;
var obj = {
    a:2.foo:foo
}
function foo(){
    console.log(this.a);
}
(obj.foo = obj.foo)();/ / 0
(false || obj.foo)();/ / 0
(1,obj.foo)();/ / 0
Copy the code

According to the binding

Binding objects to this using the call(), apply(), and bind() methods is called show binding. For the function being called, this is called an indirect call

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a : 2
};
foo();/ / 0
foo.call(obj);/ / 2
Copy the code

Plain display binding does not solve the implicit loss problem

var a = 0;
function foo(){
    console.log(this.a);
}
var obj1 = {
    a : 1
};
var obj2 = {
    a : 2
}
foo.call(obj1);/ / 1
foo.call(obj2);/ / 2
Copy the code

[Hard binding]

Hard binding is a variant of explicit binding that prevents this from being modified

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a:2
};
var bar = function (){
    foo.call(obj);
}
// Manually call foo.call(obj) inside the bar function. So, no matter how bar is later called, it will always manually call foo on obj, right
bar();/ / 2
setTimeout(bar,2000);/ / 2
bar.call(window);/ / 2
Copy the code

[API]

There are many new built-in functions in javascript that have explicit binding capabilities, such as the five iterating methods for groups of numbers: map(), forEach(), filter(), some(), every()

var id = 'window';
function foo(el){
    console.log(el,this.id);
}
var obj = {
    id: 'fn'
};
[1.2.3].forEach(foo);//1 "window" 2 "window" 3 "window"
[1.2.3].forEach(foo,obj);//1 "fn" 2 "fn" 3 "fn"
Copy the code

The new binding

If a function or method call is preceded by the keyword new, it constitutes a constructor call. For this binding, it is called a new binding

[1] Constructors usually do not use the return keyword. They usually initialize new objects and return explicitly when the body of the constructor is finished executing. In this case, the constructor call expression evaluates to the value of the new object

function fn(){
    this.a = 2;
}
var test = new fn();
console.log(test);//{a:2}
Copy the code

[2] If the constructor uses a return statement but does not specify a return value, or returns a primitive value, then the return value is ignored and the new object is used as the result of the call

function fn(){
    this.a = 2;
    return;
}
var test = new fn();
console.log(test);//{a:2}
Copy the code

[3] Use constructors to explicitly return an object from a return statement, then the value of the calling expression is that object

var obj = {a:1};
function fn(){
    this.a = 2;
    return obj;
}
var test = new fn();
console.log(test);//{a:1}
Copy the code

Note: Even though the constructor sometimes looks like a method call, it still uses the new object as this. That is, in the expression new o.m(), this is not o

var o = {
    m:function(){
        return this; }}var obj = new o.m();
console.log(obj,obj === o);//{} false
console.log(obj.contructor === o.m);//true
Copy the code

Strict mode

[1] In strict mode, this of a function called independently points to undefined

function fn(){
    'use strict';
    console.log(this);//undefined
}
fn();

function fn(){
    console.log(this);//window
}
fn();
Copy the code

[2] In non-strict mode, null or undefined values are converted to global objects when the call() or apply() methods of functions are used. In strict mode, the function’s this value is always the specified value

var color = 'red';
function displayColor(){
    console.log(this.color);
}
displayColor.call(null);//red

var color = 'red';
function displayColor(){
    'use strict';
    console.log(this.color);
}
displayColor.call(null);//TypeError: Cannot read property 'color' of null
Copy the code

conclusion

  • The four binding rules for this: default binding, implicit binding, explicit binding, and new binding correspond to the four calls to the function: independent, method, indirect, and constructor calls.

  • Distinguishing these four binding rules is not difficult, but it requires developing a sharp eye for implicit missing cases.

  • Ultimately, the reason JavaScript is so complex is because the functions are so powerful. Because functions are objects, the prototype chain is complicated; Because functions can be passed as values, the execution environment stack is complicated; Similarly, the binding rules for this are complicated because functions can be called in multiple ways

  • You don’t understand javascript until you understand functions