1. Introduction

Hi, I’m Wakawa. Welcome to add my wechat account ruochuan12 and pay attention to my public account Ruochuanvision for long-term communication and learning. A long time ago I wrote the Interviewer’s Questions series to help readers improve their JS basics, including new, Call, apply, this, and inheritance. The interviewer asks: this points to the article. I got good feedback on the nuggets and other platforms.

Recently, a friend of mine read my Vuex source code article and mentioned that there was a “this” point that I couldn’t understand (finally someone read my source code article and was moved to tears). So I wrote an article to answer the question, briefly talk about this point and how to deal with the loss of this point in Vuex source.

2. This in the object points to

var person = {
  name: 'if the sichuan'.say: function(text){
    console.log(this.name + ', '+ text); }}console.log(person.name);
console.log(person.say('Writing an article')); // Wakawa is writing an article
var say = person.say;
say('Writing an article'); // The this pointer is lost, pointing to the window. (Non-strict mode)
Copy the code

3. This in the class points to

3.1 ES5

// ES5
var Person = function(){
  this.name = 'if the sichuan';
}
Person.prototype.say = function(text){
  console.log(this.name + ', ' + text);
}
var person = new Person();
console.log(person.name); / / if sichuan
console.log(person.say('Writing an article'));
var say = person.say;
say('Writing an article'); // The this pointer is lost, pointing to the window.
Copy the code

3.2 ES6

// ES6
class Person{
  construcor(name = 'if the sichuan'){
     this.name = name;
  }
  say(text){
    console.log(`The ${this.name}.${text}`); }}const person = new Person();
person.say('Writing an article')
/ / deconstruction
const { say } = person;
say('Writing an article'); // This is an error because ES6 has strict mode enabled by default, which points to undefined
Copy the code

4. How does Utah deal with Vuex source code

Look at the code

class Store{
  constructor(options = {}){
     this._actions = Object.create(null);
  // bind commit and dispatch to self
      // Bind yourself commit and dispatch
      const store = this
      const { dispatch, commit } = this
      // Why do I bind this way?
      To call commit and dispach, this is not necessarily a store instance
      // This ensures that this in both functions is an instance of store
      this.dispatch = function boundDispatch (type, payload) {
        return dispatch.call(store, type, payload)
      }
      this.commit = function boundCommit (type, payload, options) {
        return commit.call(store, type, payload, options)
      }
  }
  dispatch(){
     console.log('dispatch'.this);
  }
  commit(){
     console.log('commit'.this); }}const store = new Store();
store.dispatch(); // What is the output of this?

const { dispatch, commit } = store;
dispatch(); // What is the output of this?
commit();  // What is the output of this?
Copy the code

Conclusion: Call is very cleverly used to forcibly bind the this pointer of the Dispatch and commit functions to store instance objects. An error is reported if you do not bind this way.

4.1 Actions Deconstruct the Store

Const {dispatch, commit} = store; In the writing. Think about how we normally write actions. The first argument to a custom function in Actions is actually a Store instance.

When we read the actions under document: https://vuex.vuejs.org/zh/guide/actions.html

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')}}})Copy the code

You can also write it as a deconstructed assignment.

actions: {
  increment ({ commit }) {
    commit('increment')}}Copy the code

With the call binding in the Vuex constructor, the this reference has been fixed. This move, you can learn to walk for free ~

Then we ask why context is a store instance with dispatch and commit methods. Keep reading.

4.2 Why the first argument to a custom function in actions is a Store instance?

The following is a simple source, there are reduced, interested can see my article Vuex source article

class Store{
 construcor(){
    // Initialize the root module
    // Also register all submodules recursively
    // And collect all module getters in this._wrappedgetters
    installModule(this, state, [], this._modules.root)
 }
}
Copy the code

Next we look at the traversal registration Actions implementation in the installModule function

function installModule (store, rootState, path, module, hot) {
    // Omit some code
    // Loop over the registration action
    module.forEachAction((action, key) = > {
      const type = action.root ? key : namespace + key
      const handler = action.handler || action
      registerAction(store, type, handler, local)
    })
}
Copy the code

Next, look at the registration Actions function that implements registerAction

/** * register mutation *@param {Object} Store object *@param {String} Type type *@param {Function} Handler User-defined function *@param {Object} Local Local object */
function registerAction (store, type, handler, local) {
  const entry = store._actions[type] || (store._actions[type] = [])
  Payload is the second parameter of the Actions function
  entry.push(function wrappedActionHandler (payload) {
    /** * This is why the first argument to a user-defined actions function is * {dispatch, commit, getters, state, rootGetters, rootState} * Actions: { * checkout ({ commit, state }, products) { * console.log(commit, state); *} *} */
    let res = handler.call(store, {
      dispatch: local.dispatch,
      commit: local.commit,
      getters: local.getters,
      state: local.state,
      rootGetters: store.getters,
      rootState: store.state
    }, payload)
    // The source code has been deleted
}
Copy the code

It is easy to see that the call order is new Store() => installModule(this) => registerAction(Store) => let res = handler.call(Store).

Handler is the user-defined function, that is, increment function corresponding to the above example. The store instance object is passed all the way down to the handler, which also uses the call function, to force the binding and the first argument is the Store instance object.

actions: {
  increment ({ commit }) {
    commit('increment')}}Copy the code

This is why the first argument to a custom function in an Actions object is a Store object instance.

Well, the article is basically finished here ~ relatively brief. It should be easier to understand.

5. Finally, summarize the direction of this

Interviewer: This points to the end of the article.

If you want to determine the this binding of a running function, you need to find where the function is called directly. Once found, we can apply the following four rules in sequence to determine the object bound to this.

  1. newCall: bind to the newly created object, note: displayreturnA function or object whose return value is not the newly created object, but an explicitly returned function or object.

  2. callorapply(orbind) call: in strict mode, bound to the specified first argument. In the non-strict mode,nullandundefined, pointing to the global object (yes in the browserwindow), and the remaining values point to benew Object()The object of the wrapper.

  3. Function calls on objects: bind to that object.
  4. Normal function calls: bound in strict mode toundefinedOtherwise, bind to the global object.

Arrow functions in ES6: Instead of using the four standard binding rules above, this is determined based on the current lexical scope. Specifically, the arrow function inherits the outer function, calling the this binding (whatever this is bound to), and without the outer function, binding to the global object (window in the browser). This is actually the same mechanism as self = this in previous ES6 code.


about

Author: Often in the name of ruochuan mixed traces in rivers and lakes. The front road lovers | | PPT know little, only good study. Public number ruochuan vision blog segmentfault front view column, opened a front view column, welcome to follow ~ dig gold column, welcome to follow ~ github blog, beg a star^_^~