this pointing problem in JS

Posted by umbra on Fri, 31 Dec 2021 18:15:02 +0100

1. Arrow function

The arrow function ranks first because its this will not be changed, so as long as the current function is an arrow function, there is no need to look at other rules.

This of the arrow function is the point of this when it was created. There are two key points here:

  1. When you create the arrow function, you have determined its this point.
  2. This in the arrow function points to this in the outer layer.

Therefore, to know this of the arrow function, you must first know the direction of this in the outer layer, and you need to continue to apply the seven step formula in the outer layer.

2. new

When calling a function with the new keyword, this in the function must be a new object created by JS.

Readers may ask, "if you use the new key to call the arrow function, will this of the arrow function be modified?".

Let's try it on the console.

func = () => {}
new func() // throw error

As can be seen from the console, the arrow function cannot be used as a constructor, so it cannot be executed together with new.

3. bind

When binding multiple times, only the value of the first bind is recognized

Error prone point

function func() {
  console.log(this)
}

func.bind(1).bind(2)() // 1

this will not be modified in the arrow function

func = () => {
  // Here, the direction of this depends on the outer layer this. Refer to formula 7 "not in function"
  console.log(this)
}

func.bind(1)() // Window, formula 1 first

bind and new

Error prone point

function func() {
  console.log(this, this.__proto__ === func.prototype)
}

boundFunc = func.bind(1)
new boundFunc() // Object true, formula 2 takes precedence

4. apply and call

The first parameter of apply() and call() is this. The difference is that when called through apply, the arguments are placed in the array, while when called through call, the arguments are comma separated.

this will not be modified in the arrow function

Error prone point

func = () => {
  // Here, the direction of this depends on the outer layer this. Refer to formula 7 "not in function"
  console.log(this)
}

func.apply(1) // Window, formula 1 first

this will not be modified in the bind function

Error prone point

function func() {
  console.log(this)
}

boundFunc = func.bind(1)
boundFunc.apply(2) // 1. Pithy formula 3 is preferred

5. Obi point (obj.)

function func() {
  console.log(this.x)
}

obj = { x: 1 }
obj.func = func
obj.func() // 1

There is no need for code examples here. The arrow function and bind function have higher priority. If you are interested, you can try it yourself.

6. Direct call

When the function does not meet the previous scenario and is called directly, this will point to the Global object. In the browser environment, the Global object is Window, and in node JS environment is Global.

Let's start with a simple example.

function func() {
  console.log(this)
}

func() // Window

For a complex example, the outer func has a confusing purpose.

function outerFunc() {
  console.log(this) // { x: 1 }

  function func() {
    console.log(this) // Window
  }

  func()
}

outerFunc.bind({ x: 1 })()

7. Not in function

Scenes that are not in the function can be divided into the < script / > tag of the browser or node JS module file.

  1. In the < script / > tag, this points to Window.
  2. On node JS Module file, this points to the default export object of Module, that is, Module exports.

Non strict mode

The strict model is proposed in ES5. Before the ES5 specification, that is, in non strict mode, this cannot be undefined or null. Therefore * * in the non strict mode, through the above seven step formula, if it is found that this point is undefined or null, then this will point to the Global object** In the browser environment, the Global object is Window, and in node JS environment is Global.

For example, in the following code, in non strict mode, this points to the global object.

function a() {
  console.log("function a:", this)
  ;(() => {
    console.log("arrow function: ", this)
  })()
}

a()

a.bind(null)()

a.bind(undefined)()

a.bind().bind(2)()

a.apply()

The execution result in non strict mode is:

In strict mode, execute the same code for comparison. Remember to copy and paste all the code into the console at once to run in strict mode (because the first line of "use strict" will take effect for subsequent code).

"use strict"

function a() {
  console.log("function a:", this)
  ;(() => {
    console.log("arrow function: ", this)
  })()
}

a()

a.bind(null)()

a.bind(undefined)()

a.bind().bind(2)()

a.apply()

The execution result in strict mode is:

The seven step formula is complete in strict mode and non strict mode, but null or undefined will be converted to global objects in non strict mode. So I didn't include this in the formula.

Review questions

Recite the formula first and then do the question, "arrow function, new, bind, apply and call, obj., direct call, not in the function".

1. After the following code is executed, func What is the count value?

function func(num) {
  this.count++
}

func.count = 0
func(1)

answer

func. The count value is 0.

According to the formula, when func() is called, it belongs to category 6 "direct call". In non strict mode, this points to the global object. This has nothing to do with func, so func Count remains unchanged. so easy.

2. Who does this point to in the following arrow function?

obj = {
  func() {
    const arrowFunc = () => {
      console.log(this._name)
    }

    return arrowFunc
  },

  _name: "obj",
}

obj.func()()

func = obj.func
func()()

obj.func.bind({ _name: "newObj" })()()

obj.func.bind()()()

obj.func.bind({ _name: "bindObj" }).apply({ _name: "applyObj" })()

answer

// obj
// undefined
// newObj
// undefined
// bindObj