This introduction

In JavaScript, everything is an object, and the runtime environment is an object, so functions are run under some object, and this is the runtime environment.

JavaScript supports dynamic switching of the runtime environment, which means that the direction of this is dynamic and cannot be determined at the time of function definition, but only at the time of function execution. In fact, this ultimately refers to the object that called it.

The principle behind this dynamism

In JavaScript, the essence of a variable is a memory address pointing to the original object, which is stored in a dictionary structure. Each attribute name corresponds to an attribute description object, where [[value]] corresponds to the value of the attribute.

When the value of a property is a function, the function is stored separately in memory, and the value of the property is the address of the function.

Since functions are stored separately from the original object, they can be executed in different environments, and this is designed to make it easy to get the current operating environment directly from within the function body.

In different scenarios this points to

The global environment

In the browser global environment, this always refers to the global object (Window), whether in strict mode or not;

Normal function, in non-strict mode, pointing to window. In strict mode, point to undefined.

The constructor

In the constructor, this refers to the instance object.

window.identity = "The Window"

function Obj() {
  this.identity = "My Object"

  this.getIdentityFunc = function () {
    console.log(this.identity)
  }

  this.getIdentityFunc1 = () = > {
    console.log(this.identity)
  }
}

const obj = new Obj()  // The new keyword can change this to point to the object obj
obj.getIdentityFunc()  // My Object
obj.getIdentityFunc1() // My Object
Copy the code

Add: The new keyword creates an empty object, and then automatically calls a function apply that points this to the empty object, so that this inside the function is replaced by the empty object.

Object methods

The object has no context of its own. The object’s method contains this, which points to the object on which the method is run.

var obj ={
  foo: function () {
    console.log(this); }}; obj.foo()// obj

// Note 1: The following runtime environment has become global
var bar = obj.foo; bar() // window
(obj.foo = obj.foo)() // window
(false || obj.foo)() // window
(1, obj.foo)() // window

// Note 2: object nesting does not look up
var a = {
  p: 'Hello'.b: {
    m: function() {
      console.log(this.p); }}}; a.b.m()// undefined, because m is actually called by b, p does not exist in b

Copy the code

Array methods

Array methods, such as map and foreach, allow you to supply a function as an argument. This should not be used inside this function.

var o = {
  v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
    this.p.forEach(function (item) { When o.f() is executed, this points to o
      console.log(this.v + ' ' + item); // This is window and cannot fetch p}); }}// Change 1: Fix with intermediate variable
var o = {
  v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
    var that = this;
    this.p.forEach(function (item) {
      console.log(that.v+' '+item); }); }}// Change 2: Use the second parameter to fix the operating environment
var o = {
  v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
    this.p.forEach(function (item) {
      console.log(that.v+' '+item);
    }, this); }}// Change 3: use the arrow function
var o = {
  v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
    this.p.forEach((item) = > {
      console.log(that.v+' '+item); }); }}Copy the code

Prototype chain

The this of the method in the prototype chain still refers to the object that called it.

var o = {
  f : function(){ 
    return this.a + this.b; }};var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); / / 5
Copy the code

Arrow function

The arrow function does not have its own this; the arrow function’s this is defined in the context.

var identity = "window"
var obj = {
    identity: 'object'.testIdentity: this.identity, // point to the context of the object
    getIdentityFunc: () = > {
        console.log(this.identity)
    }
}
console.log(obj.testIdentity)  // window
obj.getIdentityFunc()  // window
Copy the code
  • The arrow function cannot be used as a constructor because it does not have its own this
  • The call/apply/bind methods simply pass in arguments to arrow functions and do nothing to change the direction of this

DOM event handlers

The this inside the event handler refers to the DOM element object that triggered the event.

The timer

This for the timer internal callback refers to the global object Window.

Note: timers, array methods, etc., pass in a callback to a function. If this is in the function, the reference to this is likely to be wrong. You can usually fix the execution environment with the bind method. Some directly support this as an argument.

How do I change this

JavaScript provides call, apply, and bind methods to switch/fix the reference to this.

func.call(thisValue, arg1, arg2, ...)
func.apply(thisValue, [arg1, arg2])
func.bind(thisValue, arg1, arg2)()
Copy the code
  • The call parameters are added one by one and can be used to call the object’s native methods
  • Apply accepts an array as an argument to function execution, making it easier to call the array’s native methods
  • The bind method is used to bind this in the function body to an object and then return a new function with arg1, arg2… Are arguments that are preset into the argument list of the binding function when the target function is called.

How to implement call

We assign the function reference this to the first argument to call, obj, and then call the function via the obj reference.

Function.prototype.myCall = function() {
    let obj = arguments[0] | |window;   // If this is not passed, this points to the window
    let args = [...arguments].slice(1); // Get the second and all subsequent arguments (arg is an array)
    let fn = Symbol(a);// Symbol attribute to determine the uniqueness of fn
    obj[fn] = this;                     // This refers to the function that called myCall and assigns a reference to function A to obj's fn property. Now, when function A is called it's going to point to obj
    letres = obj[fn](... args);// a reference call to obj, the object passed in
    delete obj[fn];                     // The obj attribute cannot be added, so it must be removed
    return res;                         If function A has a return value, there is a return value. If function A has no return value, there is no return value
}
Copy the code

How to implement Apply

Similar to Call, except for the format of the argument.

Function.prototype.myApply = function() {
    let obj = arguments[0] | |window;   // If this is not passed, this points to the window
    let args = arguments[1];            // Get parameters (arg is an array)
    let fn = Symbol(a);// Symbol attribute to determine the uniqueness of fn
    obj[fn] = this;                     // This points to the called function
    letres = obj[fn](... args);// a reference call to obj, the object passed in
    delete obj[fn];                     // The obj attribute cannot be added, so it must be removed
    return res;                         // Returns the return value of function A
}
Copy the code

How to implement BIND

Unlike Call and applay, bind returns a function, not an immediate call.

Function.prototype.myBind = function (object) {
    let obj = object || window;	         // If this is not passed, this points to the window
    let fn = this;                       // This points to the called function
    let arg = [...arguments].slice(1);	 // Get the second and all subsequent arguments (arg is an array)
    const fBind = function () {
        /* If this is an instance of fBind, then fBind is new, otherwise obj */
        let o = this instanceof fBind ? this: obj; fn.applay(o, args.concat(... arguments)); }// Consider the impact of instantiation on the prototype chain
    let temp = function() {};
    temp.prototype = fn.prototype;
    fBind.prototype = new temp();
    
    return fBind;                        // Return a function
}
Copy the code

reference

  1. JavaScript’s This principle
  2. JavaScript this explanation