1
头图

The original text was first published on my official account, and you can subscribe to view my latest articles for the first time!

Hello everyone, I am Nian Nian!

If you have used react and vue, you should have found a problem: vue tells us that methods and life cycles should not be defined with arrow functions; while in react class components, it is more convenient to write methods in the form of arrow functions.

To ask why, most people just take him for granted as a rule. But breaking this question apart, it can actually be a good way to use the rocket built in preparation for the interview when screwing the screw.

This article will allow you to use this pointer, scope chain and prototype in this practical scenario.

this points to missing

Whether it is vue or react, it is emphasized in the official documentation, and it is necessary to pay attention to the loss of this pointer. But what's interesting is that in order to achieve the same purpose, one cannot use arrow functions, and the other can be solved by using arrow functions.

👇react

👇vue

Loss of this in React

Let's first look at react, which is a very common class component writing method:

 class Demo extends React.Component{
    state = {
        someState:'state'
    }
    // ✅推荐
    arrowFunMethod = () => {
        console.log('THIS in arrow function:',this)
        this.setState({someState:'arrow state'})
    }
    // ❌需要处理this绑定
    ordinaryFunMethod(){
        console.log('THIS oridinary function:',this)
        this.setState({someState:'ordinary state'})
    }
    render(){
        return ( 
            <div>
                <h2>{this.state.someState}</h2>
                <button onClick={this.arrowFunMethod}>call arrow function</button>
                <button onClick={this.ordinaryFunMethod}>call ordinary function</button>
            </div>
        )
    }
}
ReactDOM.render(<Demo/>,document.getElementById('root'))

In my component I define two methods: one implemented with arrow functions and the other with normal functions. When calling, print this respectively, and the results are as follows:

This correctly points to the component instance in the arrow function, but points to undefined in the ordinary function. Why?

In fact, this is a js feature that has nothing to do with react, stripping the mental burden brought by react. In essence, the above code is just a "class". Simplify it, it becomes like this👇:

  class ReactDemo {
    // ✅推荐
    arrowFunMethod = () => {
      console.log('THIS in arrow function:', this)
    }
    // ❌this指向丢失
    ordinaryFunMethod() {
      console.log('THIS in oridinary function:', this)
    }
  }
  const reactIns = new ReactDemo()
  let arrowFunWithoutCaller = reactIns.arrowFunMethod
  let ordinaryFunWithoutCaller = reactIns.ordinaryFunMethod
  arrowFunWithoutCaller()
  ordinaryFunWithoutCaller()

Running the above code, you will find that the result is unexpected: the pointer to this is also lost in the ordinary function.

To explain from the perspective of react code running:

The first is the execution of the callback function when the event is triggered. The callback function is not called directly by the instance like this: reactIns.ordinaryFunMethod() , but as in the above code, it does a "proxy", and when it is finally called, the calling object cannot be found: ordinaryFunWithoutCaller() . At this time, there is a situation where this points to undefined.

But why use arrow functions, this can correctly point to the component instance? First, review a simple knowledge point: class is a syntactic sugar, and its essence is just a constructor. Write the above code in its most primitive form:

 'use strict'
function ReactDemo() {
  // ✅推荐
  this.arrowFunMethod = () => {
    console.log('THIS in arrow function:', this)
  }
}
// ❌this指向丢失
ReactDemo.prototype.ordinaryFunMethod = function ordinaryFunMethod() {
  console.log('THIS in oridinary function:', this)
}
const reactIns = new ReactDemo()

It can be seen that the method written as an ordinary function is mounted on the prototype chain; while the method defined using the arrow function is directly assigned to the instance and becomes an attribute of the instance, and the most important thing is: it is Defined in the "constructor's scope".

We know that arrow functions do not have their own this, and when they are used, they can only find the closest one according to the scope chain. Put it here, that is, in the scope of the constructor this the component instance.

This explains why in the react component, the this of the arrow function can correctly point to the component instance.

Loss of this in vue

Write the above components with vue:

 const Demo = Vue.createApp({
  data() {
      return {
          someState:'state',
      }
  },
  methods:{
      // ❌this指向丢失
      arrowFunMethod:()=>{
          console.log('THIS in arrow function:',this)
          this.someState = 'arrow state'
      },
      // ✅推荐
      ordinaryFunMethod(){
          console.log('THIS in oridinary function:',this)
          this.someState = 'ordinary state'
      }
  },
  template:`
  <div>
      <h2>{{this.someState}}</h2>
      <button @click='this.arrowFunMethod'>call arrow function</button>
      <button @click='this.ordinaryFunMethod'>call ordinary function</button>
  </div>`
})
Demo.mount('#root')

Running the code, you will find that the results are reversed: the use of arrow functions causes the loss of this pointer: this points to the window object

This part will be a little more complicated to explain, but it only involves a small piece of Vue source code. The main operation is vue's processing of component methods. The core is three lines. If you are interested, you can go to see the complete code: vue-github

 function initMethods(vm: Component, methods: Object) {
  for (const key in methods) {
    vm[key] = bind(methods[key], vm)
  }
}

Vue will pass us in methods to traverse, and then assign it to the component instance one by one. In this process, the binding of this is processed ( bind(methods[key], vm) ): put each method in The this is bound to the component instance.

Ordinary functions have their own this, so after binding, they can correctly point to the component instance when called. But the arrow function does not have its own this, so there is no way to talk about modification, it can only find this in the parent scope. Who is this parent scope? Is it a component instance? We know that there are only two types of scopes: global scope and function scope. Going back to the vue code we wrote, it is essentially an object (specifically, a configuration object of a component, which has attributes such as data, mounted, methods, etc.) That is to say, we define methods in an object, because Objects do not constitute a scope, so the parent scope of these methods is the global scope. If the arrow function is going to find this, it can only find the this-window object in the global scope.

So much has been said above, to summarize: Vue processes the incoming method methods object, and makes the binding pointed to by this before the function is called. Only ordinary functions with this can be correctly Bind to the component instance. The arrow function will cause the this pointer to be lost.

Epilogue

"Why do you use arrow functions in react and ordinary functions in vue?" This is a very interesting question. In short, this difference is caused by the fact that the react we wrote is a class, and vue is an object.

Only arrow functions defined in the class can find the component instance according to the scope chain; in the object, only the ordinary function with its own this can be modified to point to this, and it is bound to the component instance after being processed by vue.

If you think this article is helpful to you, don't forget to give me a like, your support is my biggest motivation 💗💗💗

Follow my official account to see the latest articles for the first time👇👇👇

Click to see better


前端私教年年
250 声望14 粉丝

鹅厂前端,致力分享说人话的技术文章。