One: Start with the this pointer

1: This refers to the classification

This refers to default binding, implicit binding, explicit binding, new operation, and arrow functions.

(1) default binding:

Declare a function:

function test1 () {
    console.log(this);     // window
};
test1();
Copy the code

In non-strict mode of Window: this refers to window. Undefined in strict mode

(2): Implicit binding: When a function reference has a context, it is implicitly bound to that context.

(3): important: display binding:

Show binding means the user can manually bind this to point to.

A common way to change the reference to this is call, apply, bind, more on that later.

(4): The new operation, using the new operator to make this point on the instance.

(5): Arrow function: this of the arrow function points to this of its outer normal function.

function foo () {
    var vm = () => {
        console.log(this);
    };
    vm();           // Window
};
Copy the code

2: the Function. The prototype. The call

The call method calls a function with a specified this value and one or more arguments given separately. —MDN

function.call(thisArg, arg1, arg2, ...)
Copy the code

(1): Common use

Inheritance can be implemented through the call method:

function Father () {
    this.books = ['js', 'vue'];
    this.name = 'yhq';
};
Father.prototype.sayBooks = function () {
    console.log(this.books);
};
function Son () {
    Father.call(this);
};
var son = new Son();
son.books      //  ["js", "vue"]
son.sayBooks    //  Uncaught TypeError: son.sayBooks is not a function 
Copy the code

This inheritance can only inherit properties and methods from the parent class, not from the parent class stereotype.

Calling an anonymous function with the call method:

var animals = [
  { species: 'Lion', name: 'King' },
  { species: 'Whale', name: 'Fail' }
];

for (var i = 0; i < animals.length; i++) {
  (function(i) {
    this.print = function() {
      console.log('#' + i + ' ' + this.species
                  + ': ' + this.name);
    }
    this.print();
  }).call(animals[i], i);
}
Copy the code

(2): Implement a call

fn.call(obj, args1, args2,…) ;

The call method accepts one argument as obj. Let’s make this point to obj. The idea is to add fn’s attributes to the obj and delete the new attributes after the call operation.

First version code:

Function.prototype.myCallOne = function (ctx) { var args = [...arguments].slice(1); Ctx. fn = this; ctx.fn = this; ctx.fn = this; Var result = ctx.fn(... args); // delete ctx.fn; // delete ctx.fn; return result; }Copy the code

Let’s test it out:

var obj = {
    name: 'yhq'
};
function funcs(age) {
    console.log('name:'+ this.name + 'age:' + age);
};
funcs.myCallOne(obj, 24);           // name:yhq age:24
Copy the code

ok ! No problem.

Optimize:

There are a few minor issues: what if no call is passed to obJ? What if the original object has fn on it?

Handle these two issues: if there is no default window, ensure that fn is unique.

Version 2:

Function.prototype.myCallTwo = function (ctx) { ctx = ctx || window; var fn = Symbol(); // Symbol attribute to determine the uniqueness of fn. var args = [...arguments].slice(1); ctx[fn] = this; var result = ctx[fn](... args); delete ctx[fn]; return result; }Copy the code

Test it out:

var obj2 = {
    name: 'yhq',
    fn: function () {}
};
function practive(age) {
    console.log('name:'+ this.name + 'age:' + age);
};
funcs.myCallTwo(obj, 24);           // name:yhq age:24
Copy the code

Ok..

Three: the Function prototype. The apply

The apply() method calls a function with a given this value and an argument, MDN, in the form of an array (or array-like object)

The scenarios for apply() and call() are basically the same, except that they accept different arguments. 1. Apply accepts an array and call accepts a list of arguments.

Implement an apply:

fn.apply(this, []); 

The idea is similar to call, but note that you can accept arguments by passing an array directly instead of using arguments

The code is as follows:

Function.prototype.myBind = function (ctx, array) { ctx = ctx || window; var fn = Symbol(); var result; if (! Result = CTX [fn](); } else { result = ctx[fn](... array); } delete ctx[fn]; return result; };Copy the code

Test it out:

var obj2 = {
    name: 'yhq',
    fn: function () {}
};
function practive(age) {
    console.log('name:'+ this.name + 'age:' + age);
};
funcs.myBind(obj, [24]);           // name:yhq age:24
Copy the code

OK!

Four: the Function prototype. The bind

The **bind()** method creates a new function. When bind() is called, this of the new function is specified as the first argument to bind(), and the remaining arguments will be used as arguments to the new function to be called –MDN

fn.bind(obj, args1, args2…) ; Returns an execution context function that needs to be called manually by the user to execute.

The bind function has the following features:

1. Change the this pointer

2. Accept the parameter list

Return a function

4. When new takes precedence over bind, changing this will not work.

ok! So let’s implement that.

Function.prototyep.myBind = function (ctx) { var self = this; / / because after to return a function that here to save the this prevent chaos var args = Array. The prototype. Slice. The call (the arguments, 1); / / to get parameters of var fb = function () {var bindArgs = Array. Prototype. Slice. The call (the arguments, 1); return self.apply( this instanceof fb ? this : ctx, args.concat(bindArgs)) }; var fn = function () {}; fn.prototype = this.prototype; Fb.prototype = new fn(); return fb; }Copy the code

Analyze some questions:

self.apply( fb instance this ? this : ctx, args.concat(bindArgs));

If this is a constructor, this refers to an instance, and this instanceof fb === true. Or CTX.

The new operation requires the instance to inherit from the stereotype. So there’s an inheritance relationship here.

The stereotype is inherited through a FN mediation.

That’s basically what the implementation looks like, and I’ve been refining it since then.