preface

In a previous article on the four binding rules for this, we looked at four binding rules for this. But that’s all if only one rule applies at the call location. This article focuses on how to determine which rule is applied when multiple rules are applied at the call location. This requires us to filter them according to their priority.

priority

To be clear, the default binding has the lowest priority of the four rules. The reason, as mentioned in the previous article, is that the default binding rule can be thought of as the default rule when no other rule can be applied.

Priority comparison of implicit and explicit binding

Case code

function getNum() {
    console.log("this.a--->".this.a);
    console.log("this--->".this);
}

var obj1 = { a: 2.getNum: getNum };
var obj2 = { a: 3.getNum: getNum };

obj1.getNum();
console.log("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
obj1.getNum.call(obj2);
Copy the code

The results show

By looking at the results of the code above, we can see that explicit binding takes precedence over implicit binding, so we should consider whether explicit binding can be applied first when judging the this binding rule.

Priority comparison between new binding and implicit binding

Case code

function getNum(num) {
    this.a = num;
    console.log("this.a--->".this.a);
    console.log("this--->".this);
}

var obj1 = { getNum: getNum };

obj1.getNum(2); / / 2

console.log("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");

var bar = new obj1.getNum(4); // Note that this is the new binding used with implicit binding
console.log('obj1.a--->', obj1.a);
console.log('bar.a--->',bar.a);
Copy the code

The results show

Note that the code above compares the priority of the new binding with that of the implicit binding by using the line new obj1.getnum (4), which uses both together.

As a result, the new binding takes precedence over the implicit binding.

New binding and show binding priority comparison

Since new and call(or apply) can’t be used together, we can’t directly test their priority with new foo.call(obj1). Fortunately, we also have a hard bind — bind. If you’ve read my previous description of the this binding rule, you already know that hard binding is also a display binding, so we can use hard binding to test the new binding and the priority of the display binding.

Case code

function getNum(num) {
    this.a = num;
    console.log("this.a--->".this.a);
    console.log("this--->".this);
}

var obj = {
    a: ""};var fn = getNum.bind(obj);
fn(2);

console.log("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");

var instance = new fn(10);
console.log("obj.a--->", obj.a); / / 2
console.log("instance.a--->", instance.a); / / 10
Copy the code

In the above code, fn is hardbound to obj, but after executing new fn(10), new modifies this in the call fn() that is hardbound to obj.

The results show

It is important to note that new can only change this if it is hardbound to the built-in — function.prototype. bind method in ES5. In other words, The hard binding we implement through Call or apply cannot modify this. For example, the following code:

function getNum() {
    console.log("this.a--->".this.a);
    console.log("this--->".this);
}

// Auxiliary functions
function bind(fn, obj) {
    return function () {
      fn.apply(obj, arguments);
    };
}

var obj = { a: 2 };
var bar = bind(getNum, obj);

bar();
console.log('-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --');
var ins = new bar();
Copy the code

The results show

As a result, it is clear that new does not modify this in the call bar() that is hardbound to obj.

To determine this

By comparing the precedence of the this binding rule above, we can determine which rule is applied to a function at a call location in the following order:

  1. The new binding

Is the function called in new? If so, this binds to the newly created object.

var bar = new foo()
Copy the code
  1. Explicitly bound

Is the function called through call, apply, or hard binding? If so, this binds to the specified object.

var bar = foo.call(obj)
Copy the code
  1. Implicit binding

Is the function called in a context object? If so, this binds to that context object.

var bar = obj1.foo()
Copy the code
  1. If none of the above is true, the default binding is used, which is this bound to the global object or undefined (depending on whether strict mode is enabled in the code).
var bar = foo()
Copy the code

In terms of normal function calls, understanding the above is enough to understand how this bindings work. Of course, there are always exceptions to the rule.

Bind the exception

  1. When you pass null, undefined, or null to the first argument of call, apply, and bind, these values are ignored during the call and the default binding rules apply.
function fn() { 
    console.log( this.a );
}

var a = 2;

fn.call(); / / 2
fn.call( null ); / / 2
fn.call( undefined ); / / 2
Copy the code
  1. An “indirect reference” to a function is inadvertently created, in which case the default binding rules apply when the function is called.
function getNum() {
    console.log(this.a);
}

var a = 20;
var obj = {
    a: 2.getNum: getNum,
};

obj.getNum(); / / 2

var fn = obj.getNum;
fn(); / / 20
Copy the code

Reference source

  • You Don’t Know JavaScript, vol. 1