Preface:

If you want to learn the front end well, the basic skills must be solid, prototype and prototype chain, closure, this, then these three mountains must be overcome, today I will say this, not to say more, go~

This:

It is crucial that we understand what this refers to during development

In order to be able to see at a glance what this refers to, we need to determine what its binding rule is? There are four binding rules for this:

  • The default binding
  • Implicit binding
  • According to the binding
  • The new binding
The default binding

Default bindings are applied when no other rules can be applied, often as stand-alone function calls

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

Window.foo (), window can be omitted, so this in foo() refers to the global object Window (in non-strict mode). In strict mode, This refers to undefined, undefined has no this object, raising an error.

Implicit binding

Consider whether the call location has a context object

function foo(){ console.log(this.a); } var obj = { a:2, foo:foo } obj.foo(); / / 2Copy the code

Because foo is called with obj in front of it, implicit binding binds this in the function call to the context object, obj, so this.a and obj.a are the same, but remember: this always refers to the object that last called it

function foo(){ console.log(this.a); } var obj1 = { a:2, foo:foo } var obj2 = { a:3, obj1:obj1 } obj2.obj1.foo(); / / 2Copy the code

As you can see, the result is 2, because this refers to the object that last called it, obj1

A common problem with implicit binding is implicit loss

Implicit loss

That is, if an implicit loss occurs, the default binding rules apply

function foo(){ console.log(this.a); } var obj = { a:2, foo:foo } var baz = obj.foo; Var a = 'I am a of the global object '; baz(); // I am a of the global objectCopy the code

Because baz() is really a stand-alone function call, the default binding is applied

A more common case where implicit loss occurs is when a callback function is passed in:

function foo(){ console.log(this.a); } function baz(fn){fn()} var obj = {a:2, foo:foo} var a = 'I am a global object '; baz(obj.foo); // I am a of the global objectCopy the code

Parameter passing is essentially an implicit assignment

According to the binding

Display, because you can specify the object to bind this directly, we can use apply,call,bind, etc

The use of apply is the same as that of call, except that the method of passing arguments is different and the corresponding functions are executed. However, bind does not execute the functions and needs to be called manually

function foo(){ console.log(this.a); } function baz(fn){ fn() } var obj = { a:2, } foo.call(obj); //2 foo.apply(obj); / / 2Copy the code

However, the problem of missing bindings cannot be solved as follows:

function sayHi(){
     console.log('Hello,', this.name);
 }
 var per = {
     name: 'mengyun',
     sayHi: sayHi
 }
 var name = 'anna';
 var Hi = function(fn) {
     fn();
 }
 Hi.call(per, per.sayHi); //Hello, anna
Copy the code

But we can also hardbind this to fn to solve this problem

function sayHi(){
    console.log('Hello,', this.name);
}
var per = {
    name: 'mengyun',
    sayHi: sayHi
}
var name = 'anna';
var Hi = function(fn) {
    fn.call(this);
}
Hi.call(per, per.sayHi); //Hello, mengyun
Copy the code

Reason: Since per is bound to this in the Hi function, fn binds this object to sayHi’s function. This in sayHi refers to the per object.

Bind is classified as hard binding in js (above).

function sayHi(){
    console.log('Hello,', this.name);
}
var per = {
    name: 'mengyun',
    sayHi: sayHi
}
var name = 'anna';
var Hi = sayHi.bind(per);
Hi.call(per, per.sayHi); //Hello, mengyun
Copy the code
The new binding

Unlike other languages, JS has no classes, so we can use constructors to simulate classes

Calling a function with new automatically does the following:

  1. Create a brand new object
  2. This object will be connected by [[Prototype]]
  3. This new object is bound to the function call’s this
  4. If the function returns no other object, the new object is returned, otherwise the object returned by the constructor is returned
Write a new by hand
function _new(fn,... Var obj = object.create (fn. Prototype); //3, change this to var res = fn. Apply (obj,args); // 4. If the result of the constructor is a reference data type, the result of the run is returned, otherwise the newly created obj if ((res! ==null && typeof res == "object") || typeof res == "function") { return res; } return obj; }Copy the code

So let’s bind this with new,

function Person(name){ this.name = name; } var per = new Person('mengyun'); console.log(per.name); //mengyunCopy the code

At this point per is bound to this in the Person call

priority

Knowing the four binding rules, all we need to do is determine exactly which one it is, but if more than one rule can be applied to a call location, then we need to have a priority. There is no doubt that the default binding is the lowest, so forget about it for a moment. Here is an example:

function f1(){ console.log(this.a); } var obj1 = { a:2, f1:f1 }; var obj2 = { a:3, f1:f1 }; obj1.f1(); //2 obj2.f1(); //3 obj1.f1.apply(obj2); / / 3Copy the code

As you can see, the display binding takes precedence over the implicit binding and you can write the rest of the demo, and the final conclusion is new binding > Explicit binding > Implicit binding > Default binding

Bind the exception

If null or undefined is passed as a binding object to call,apply, or bind, these values are ignored and the default binding rules apply:

var obj= {
    name: 'mengyun'
}
var name = 'Anna';
function bar() {
    console.log(this.name);
}
bar.call(null); //Anna
Copy the code

Note: However, the above rules only apply to normal functions, except for arrow functions, which do not have their own this, but are determined by the outer scope, and the binding of arrow functions cannot be modified.

function foo(){ return (a)=>{ console.log(this.a); } } var obj1 = { a:2 }; var obj2 = { a:3 }; var baz = foo.call(obj1); baz.call(obj2); / / 2Copy the code

Since foo() ‘s this is bound to obj1, baz() is also bound to obj1 and cannot be modified