preface

JavaScript this point to the problem was originally a beginner will be a problem, but for the class this point to the problem, found many people still confused. I hope this article can make it clear to you.

The binding priority of this

There’s a lot of talk about this. Some people say this refers to whoever calls it. Some people say this has nothing to do with scope, only the execution context. They seem to mean the same thing.

1. New creates the instance to call the method. This refers to the current instance

class Cat {
    jump() {
        console.log('jump',this)
    }
}
const cat = new Cat()
cat.jump() // jump Cat {}
Copy the code

2. Explicit binding

Use call, apply, bind

Function jump() {console.log(this.name)} const obj = {name: 'jump ', jump,} jump = jump(obj) jump() // jumpCopy the code

3. Method binding in objects

Function jump() {console.log(this.name)} const obj = {name: 'jump ', jump,} obj.jump() // jumpCopy the code

4. Default binding

In strict mode, this is undefined, otherwise it is a global object.

Binding of a property to a method in a Class

class Cat { constructor(name) { this.name = name } jump() { console.log('jump', this) } static go() { console.log(this) } } Cat.drink = function() { console.log('drink', this) } Cat.prototype.eat = function() { console.log('eat', this) } Cat.prototype.walk = () => { console.log('walk', This)} let cat = new cat (' bean ')Copy the code

As you can see in the figure above, Cat creates instances whose methods are mounted on __proto__ of the instance, that is, on the prototype object. Since cat.proto and cat. prototype refer to the same object, when the original method is mounted or overridden on cat.__proto__, all instances created by cat will share the method. All instances are chained to the prototype object to find methods via the __proto__ attribute.

But static methods are not shared with the instance because they are not mounted on the prototype object.

Properties are mounted on instances, meaning that each created instance has its own properties with different values.

Class this binding

When we print typeof Cat, we know that Cat is a function type, and the class itself refers to the constructor. The class in ES6 is just a syntactic sugar, which can be implemented in ES5. The instance Cat created by the constructor Cat is an object. When initializing a cat instance, properties on this are mounted to the instance object in constructor.

class Cat { constructor(name, age) { this.name = name } run() { console.log('run', This)}} let cat = new cat (' 小 小 ') cat. Run () // run cat {name: '小 小 '}Copy the code

When cat.run() is called, the current context is cat, so its this refers to an instance of cat.

class Cat { constructor(name) { this.name = name this.jump = this.jump.bind(this) this.drink = () => { console.log('drink',this) } } run() { console.log('run', this) } jump() { console.log('jump',this) } static go() { console.log('go',this) } } Cat.prototype.walk = () => { Console. log('walk',this)} let cat = new cat (' sprouts ') let run = cat.run let jump = cat.jump let go = cat.go let walk = Cat.walk let drink = cat.drink run() // run undefined Jump () // jump Cat {name: "jump ", jump: ƒ} Cat. Go () // go class Cat {} go() // go undefined Cat. Walk () // walk Window Cat Cat {name: "ƒ ", jump: ƒ, drink: ƒ}Copy the code

Resolution:

Run method: When an instance method is assigned to a variable, but only a reference to the method is assigned, so when the variable executes the method, it actually changes the context in which the method is executed. The context used to be instance cat, and then after assignment, the context becomes global. This is bound by default. Class is in strict mode. In this mode, the global this is bound to undefined by default. Otherwise, if executed in a browser, this is bound to Window by default.

Jump method: Because at the time of constructor execution, the context cat instance of jump execution is explicitly bound. So the execution context for jump is still a CAT instance.

Go method: The go method uses static method definition, cannot share instance cat and can only be called directly on the constructor cat.

Walk and drink methods: These two methods are defined using the arrow function. The arrow function’s this is bound when the function is defined, not during execution. Simply put, when a function is defined, this inherits the object from which the function was defined. Walk is defined in cat.prototype. walk, where this points to window. Whatever variable is assigned later is just a reference to the function, so this is still the window. Similarly, when drink is defined, this points to the constructor.

Note:

(1) Strict mode

Inside classes and modules, the default is strict mode, so there is no need to use strict to specify the runtime mode. As long as your code is written in a class or module, only strict mode is available. Considering that all future code will actually run in modules, ES6 actually upgrades the entire language to strict mode.

(2) The direction of this

Class methods that contain this inside point to instances of the class by default. You must be careful, however, that you may get an error if you use this method alone.

class Logger {
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }

  print(text) {
    console.log(text);
  }
}

const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
Copy the code

In the code above, this in the printName method points to an instance of the Logger class by default. However, if we extract this method and use it alone, this will refer to the environment in which the method was run (because the class is in strict mode, so this actually refers to undefined), and we will fail to find print.

An easy solution is to bind this to the constructor so that the print method won’t be missing.

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
  // ...
}
Copy the code

Another solution is to use arrow functions.

class Obj {
  constructor() {
    this.getThis = () => this;
  }
  getVal = () => this;
}

const myObj = new Obj();
myObj.getThis() === myObj // true
myObj.getVal() === myObj // true
Copy the code

The this inside the arrow function always points to the object at which it was defined. In the above code, the arrow function is inside the constructor, and its definition takes effect when the constructor executes. In this case, the arrow function must be running in an instance object, so this will always point to an instance object.

(3) Static method

A class is the prototype of an instance, and all methods defined in a class are inherited by the instance. If you prefix a method with the static keyword, it means that the method is not inherited by the instance but is called directly from the class. This is called a “static method.”

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
Copy the code

In the code above, the static keyword precedes the classMethod method of class Foo, indicating that the method is a static method that can be called directly on class Foo (foo.classmethod ()), not on an instance of class Foo. If a static method is called on an instance, an error is thrown indicating that the method does not exist.

Note that if a static method contains the this keyword, this refers to the class, not the instance.

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello
Copy the code

In the code above, the static method bar calls this.baz, where this refers to class Foo, not an instance of Foo, the same as calling foo. baz. Also, as you can see from this example, static methods can have the same name as non-static methods.

Static methods of a parent class that can be inherited by subclasses.

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'
Copy the code

In the code above, the parent class Foo has a static method that the subclass Bar can call.

Static methods can also be called from super objects.

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"
Copy the code

Reference Documents:

es6.ruanyifeng.com/#docs/class

Developer.mozilla.org/zh-CN/docs/…