preface

This paper adopts the total score to narrate:

conclusion

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. Called by new? Bind to the newly created object

  2. Called by call or apply (or bind)? Binds to the specified object

  3. Called by a context object? Bind to that context object

  4. Default: bind to undefined in strict mode, otherwise bind to global object

  5. The arrow function in ES6 inherits the this binding from the outer function call

A picture is worth a thousand words:

A description

The correctness of the summary will be verified with pseudocode below

bynewCall? Bind to the newly created object

So, by the way, what does the new operation do?

  • Create (or construct) a brand new object
  • The new object will be executed[[prototype]]The connection
  • This new object will be bound to the function callthis
  • If the function returns no other object, thennewThe function call in the expression automatically returns the new object
function Man(n, a) {
  this.name = n
  this.age = a;
}
const xueyue = new Man('snow month'.18);
console.log( xueyue.name ); / / month on snow
console.log( xueyue.age ); / / 18
Copy the code

bycallorapply(orbind) call? Binds to the specified object

const { log } = console
const name = 'snow month'
const age = 18
const obj = {
  name: 'Anne'.objAge: this.age,
  fun: function (like, dislike) {
    log(this.objAge)
    log(this.name + 'this year' + this.age + ', '.'like' + like + 'Don't like' + dislike)
  },
}
const a = { name: 'Flying Tigers'.age: 19 }
const b = { name: 'Night Rose'.age: 20 }
const c = { name: 'Avengers'.age: 21 }
obj.fun.call(a, 'thor'.'the black dragon') // Undefined flying Tigers are 19 years old and prefer Thor to Black Dragon
obj.fun.apply(b, ['Fire Unicorn'.'无影']) // Undefined night rose is 20 years old and prefers fire Unicorn to shadow
obj.fun.bind(c, 'Barrett'.'AWM') ()// Undefined Avenger 21 years old like Barrett doesn't like AWM
Copy the code

Called by a context object? Bind to thatContext object

Many functions in third-party libraries, as well as many new built-in functions in the JavaScript language and host environments, provide an optional argument, often called context, that serves the same purpose as bind(..) Also, make sure your callback uses the specified this

function foo(el) {
  console.log(el, this.id);
}
var obj = {
  id: 'awesome'
};
/ / calls foo (..) Bind this to obj
[1.2.3].forEach(foo, obj);
// 1 awesome 
// 2 awesome 
// 3 awesome
Copy the code

Default: in strict mode bound toundefinedOtherwise, bind toGlobal object

function foo() {
  console.log(this.a);
}
a = Awesome!;
foo(); / / 666
Copy the code
function foo() {
  'use strict';
  console.log(this.a);
}
a = Awesome!;
foo(); // TypeError: Cannot read property 'a' of undefined
Copy the code

Note: If you pass null or undefined as a binding object for this to call, apply, or bind, these values will be ignored and the default binding rules will apply:

function foo() {
  console.log(this.a);
}
var a = 2;
foo.call(null); / / 2
Copy the code

So when do you pass null?

A very common approach is to use apply(..) To “expand” an array and pass it as a parameter to a function. Similarly, bind(..) Parameters can be currified (pre-set some parameters), which is sometimes very useful

function foo(a,b) {
  console.log('a:' + a + ', b:' + b);
}

// Expand the array into parameters
foo.apply(null[2.3]); // a:2, b:3

/ / use the bind (..) Let's do that
var bar = foo.bind(null.2);
bar(3); // a:2, b:3
Copy the code

However, always using NULL to ignore the this binding can have some side effects. If a function does use this (such as a function in a third-party library), the default binding rule binds this to the global object (which in the viewer is a Window), which can have unpredictable consequences (such as modifying the global object).

Obviously, this approach can lead to many bugs that are difficult to analyze and track

A “safer” approach is to pass in a special object (DMZ empty object) to which binding this won’t cause any side effects to your program

function foo(a,b) {
  console.log('a:' + a + ', b:' + b);
}

// Our DMZ empty object
var _dmz = Object.create(null);

// Expand the array into parameters
foo.apply(_dmz, [2.3]); // a:2, b:3

/ / use the bind (..) Let's do that
var bar = foo.bind(_dmz, 2);
bar(3); // a:2, b:3
Copy the code

ES6The arrow function inherited from the outer function callthisThe binding

function foo() {
  setTimeout(() = > {
    // This in this method inherits from foo()
    console.log(this.a);
  }, 100);
}

var obj = {
  a: 18
};

foo.call(obj); / / 18
Copy the code

Equivalent to ES5 below

function foo() {
  var self = this; // lexical capture of this
  setTimeout( function (){
    console.log(self.a);
  }, 100);
}

var obj = {
  a: 18
};

foo.call(obj); / / 18
Copy the code

The end of the

Let’s do a little bit of a test

function Parent() {
  this.a = 1
  this.b = [1.2.this.a]
  this.c = { demo: 5 }
  this.show = function () {
    console.log(this.a, this.b, this.c.demo)
  }
}

function Child() {
  this.a = 2
  this.change = function () {
    this.b.push(this.a)
    this.a = this.b.length
    this.c.demo = this.a++
  }
}

Child.prototype = new Parent()

var parent = new Parent()
var child1 = new Child()
var child2 = new Child()

child1.a = 11
child2.a = 12

parent.show() // 1 [1, 2, 1] 5
child1.show() // 11 [1, 2, 1] 5
child2.show() // 12 [1, 2, 1] 5

// This involves knowledge of the prototype chain
child1.change() // b = [1, 2, 1, 11] a = 5 demo = 4
child2.change() // b = [1, 2, 1, 11, 12] a = 6 demo = 5
parent.show() // 1 [1, 2, 1] 5
child1.show() // 5 [1, 2, 1, 11, 12] 5
child2.show() // 6 [1, 2, 1, 11, 12] 5
Copy the code