preface

In JS, this is an important concept. The meaning of this may be different in different places. A simple operation may change the meaning of this, so how to identify this is very important

Execution context

This is a special attribute of the execution context, so to understand this, you must understand the execution context and the execution context in short is the current code execution environment, first run js code will create a global execution context and pressure into the execution stack (lifo), when the calling function will create a new execution context for this function, The function is removed from the stack after the execution of the corresponding execution context, that is, the execution environment is destroyed, and the variables and functions saved in the environment are destroyed

So far we know that there are two kinds of execution contexts (eval is not discussed here) :

  • Global execution context: The this browser usually refers to window

  • Function execution context: the execution context is created when the function is called, not defined, and this as an attribute of the execution context is determined only when the function is called, so it is usually said that whoever calls this is the one who calls this

Let’s look at some demos:

// This demo knows how to execute last-in, first-out stack
function test() {
    console.log('I am test')
    test1()
    console.log('I am test2')}function test1() {
    console.log('I am test1')
}
test()

// Execution process:
// 1. Create a global execution context and push the execution stack
// 2. Create the test execution context and push the execution stack
If test1 is encountered, create a test1 execution context and push the execution stack
// 4. After test1 is executed, test1 is removed from the stack and destroyed
// 5. The test execution context is removed from the stack and destroyed
// 6. Close the browser, unstack the global execution context, and destroy it

Copy the code
var a = 1
var obj = {
    a: 2.fn: function () { // The function is created, this is undefined, depending on who calls it
        console.log('a--'.this.this.a)
        return function() {
            console.log('c--'.this.this.a)
        }
    }
}
Call a / /
obj.fn() // the function is called by obj
// Result: a-- {a: 2, fn: ƒ} 2

/ / call 2
var fn = obj.fn
fn()// equivalent to window.fn(), in which case this points to window
A -- Window {Window: Window, self: Window, document: document, name: "", location: location,... 1}

/ / call three
obj.fn()()
// Execution result:
// a-- {a: 2, fn: ƒ} 2
// c-- Window {Window: Window, self: Window, document: document, name: "", location: location,... 1}
// fn() is called by obj, so this refers to obj, a is 2, and the second function is actually called by window, so this is window, a is 1
// The function is called by whom. function, who is who, this is who, no who. this is window
Copy the code

Summary: This is the execution context attribute, the new execution context can have a new this reference, the global and function can create the execution context, except this in the function, the other this are window, this is called: who? The function “this” is the same as the function “window” without “who”

There will be some special existence in JS, let’s look at it together

New

New does the following:

  1. Create a new object
  2. The new __proto__ object points to the function’s prototype
  3. This refers to that object
  4. Return a new object

Implementation of new:

function Animal(type) {
    this.type = type
    return
}
Animal.prototype.say = function() {
    console.log('say')}// Customize new
function myNew(. values) {
    let [ fn, ...others ] = values
    let obj = {}
    obj.__proto__ = fn.prototype
    const result = fn.apply(obj, others)
    // fn returns an object if it is an object, or obj if it is not
    return Object.prototype.toString.call(result) == '[object Object]' ? result : obj
}
const animal = myNew(Animal, 'v')
console.log('animal', animal)
// animal Animal {type: "v"}
Copy the code

We can see from the implementation of new that changing Animal this refers to the new object obj

Call, Apply, and Bind

Call, apply, and bind can all change the direction of this, and their implementation is as follows:

// call
Function.prototype.myCall = function(. values) {
    let [ obj, ...others ] = values
    obj = obj ? Object(obj) : window
    obj.fn = thisobj.fn(... others)delete obj.fn
    return
}
// apply
Function.prototype.myApply = function(. values) {
    let [ obj, others ] = values
    obj = obj ? Object(obj) : window
    obj.fn = this
    obj.fn(others)
    delete obj.fn
    return
}
// bind
Function.prototype.myBind = function(. values) {
    let [ obj, others ] = values
    let _self = this
    let bind = function(vl) {
        return _self.apply(obj, [...others, ...vl])
    }
    bind.prototype = Object.create(this.prototype)
    return bind
}
Copy the code

Ok, the above is their implementation, you can see that all three of them have the effect of changing this, again using demo as an example:

var a = 1
var obj = {
    a: 2.fn: function () {
        console.log('a--'.this.this.a)
        return function() {
            console.log('c--'.this.this.a)
        }
    }
}
Call a / /
var fn = obj.fn
fn.call(obj)
} // Result: a-- {a: 2, fn: ƒ} 2

/ / call 2
obj.fn().call(obj)
// Execution result
Fn: ƒ} 2
ƒ} 2
// This is obj when the function is called. This is obj when the function is called
Copy the code

Arrow function

The arrow function is a special existence. It doesn’t have its own this, no prototype, no arguments, and can’t be used as a constructor. It inherits this from the upper scope chain. Let’s watch some demos

var obj = {
    fn: () = > {
        console.log('this'.this)}}Call a / /
obj.fn()
// This Window {Window: Window, self: Window, document: document, name: "", location: location,... }

/ / call 2
obj.fn.call(obj)
// This Window {Window: Window, self: Window, document: document, name: "", location: location,... }
// The result is window because the arrow function was created with this pointing to window
Copy the code

Then look at one

var obj = {
    fn: function() {
        return () = > {
            console.log('this---'.this)}}}Call a / /
obj.fn()()
// Result: this-- {fn: ƒ}

/ / call 2
const fn = obj.fn()
fn()
// Result: this-- {fn: ƒ}

/ / call three
obj.fn.call(window) ()// This Window {Window: Window, self: Window, document: document, name: "", location: location,... }

// The third time the result is different, because the call of fn's this refers to window

Copy the code

conclusion

Execution context also has some concepts, such as: variable promotion, function promotion, scope chain, etc., do not do too much explanation, do not understand the partner to check