当前位置:网站首页>Simple implementation of call, bind and apply

Simple implementation of call, bind and apply

2022-07-07 12:35:00 Xiaoding Chong duck!

Preface :

These three are all functions api, Used to change this The direction of , Enable an object to call another method that does not belong to it , The core idea is through Change the this Point to , Borrow others' methods .

One 、call

adopt Object call implicit binding this Method implementation of , Because execution obj1.getName.myCall(obj), be myCall Medium this It means pointing. obj1.getName Of , Then add the method to the object you want to use .

Function.prototype.myCall = function (context = window, ...args) {
    if (typeof this !== 'function') {
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    //  here this Is refers to [Function: getName], To add methods to context On 
    context.$fn = this;
    // Call the function implicitly 
    const result = context.$fn(...args);
    // Remove added properties 
    delete context.$fn;
    // Returns the return value of a function call 
    return result;
}

  But hang the function on the incoming object (context) in , In order to avoid the problem of duplicate name coverage , have access to symbol rewrite , as follows :

Function.prototype.myCall = function (context = window, ...args) {
   if (typeof this !== 'function') {
     throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
   }
   //  here this Is refers to [Function: getName], To add methods to context On 
   let fn = Symbol('call');
   context[fn] = this;
   // Call the function implicitly 
   const result = context[fn](...args);
   // Remove added properties 
   delete context[fn];
   // Returns the return value of a function call 
   return result;
}

verification :

let obj = {
    name: 'obj'
};

let obj1 = {
    name: 'obj1',
    getName: function (age, hobby) {
        return this.name + ' This year, ' + age + ' year , like ' + hobby;
    }
};

console.log(obj1.getName.myCall(obj, 12, ' Sing a song '));  // obj This year, 12 year , Like singing 

Two 、apply

apply The implementation principle is the same , Only the format of parameter transmission is different , Just put the args Just change it

Function.prototype.myApply = function (context = window, args = []) {
    if (typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    //  here this Is refers to [Function: getName], To add methods to context On 
    context.$fn = this;
    // Call the function implicitly 
    const result = context.$fn(...args);
    // Remove added properties 
    delete context.$fn;
    // Returns the return value of a function call 
    return result;
}

  verification :

let obj = {
    name: 'obj'
};

let obj1 = {
    name: 'obj1',
    getName: function (age, hobby) {
        return this.name + ' This year, ' + age + ' year , like ' + hobby;
    }
};

console.log(obj1.getName.myApply(obj, [12, ' Sing a song ']));  // obj This year, 12 year , Like singing 

3、 ... and 、bind

And call and apply The difference is ,bind() Method creates a new function , stay bind() When called , Of this new function this Is specified as bind The first parameter of , The remaining parameters will be specified as parameters of the new function , For calling .

characteristic :

  • Returns a new function
  • bind As the first parameter of the new function this
  • All other parameters (bind And the second call ) As a parameter of the new function

The first one is :

Unsupported use new Call the newly created constructor

Function.prototype.myBind = function () {
   let fn = this;
   let asThis = arguments[0], args = [...arguments].slice(1);
   if (typeof fn !== 'function') {
      throw new TypeError('Function.prototype.bind - ' + 'what is trying to be bound is not callable');
   }
   return function () {
      let args2 = [...arguments];
      let fnArgs = args.concat(args2);
      return fn.apply(asThis, fnArgs);
   }
}

verification :

let person = {
   name: ' Xiaohong '
}

function Person(value) {
   return this.name + ' like ' +   Array.prototype.slice.call(arguments);
}

let demo = Person.myBind(person, ' Sing a song ');

console.log(demo(' dance '));  //  Xiao Hong likes singing , dance 

But this is the case , If you use new Call the newly created constructor , There will be this Point to the problem of change , as follows :

let person = {
   name: ' Xiaohong '
}

function Person(value) {
   return this.name + ' like ' +   Array.prototype.slice.call(arguments);
}

let Demo = Person.myBind(person, ' Sing a song ');
let demo = new Demo(' dance ');
console.log(demo.name);  // undefined

In fact, at this time, we hope demo.name Or print Xiaohong , But by new after ,this It points to demo example , therefore demo.name Namely undefined

So there is a second way to write , The following writing method supports the use of new Call the newly created constructor :

Function.prototype.myBind = function () {
   let fn = this;
   // console.log(this);
   let asThis = arguments[0], args = Array.prototype.slice.call(arguments, 1);
   if (typeof fn !== 'function') {
      throw new TypeError('Function.prototype.bind - ' + 'what is trying to be bound is not callable');
   }
   let resultFn = function () {
      let args2 = Array.prototype.slice.call(arguments);
      let fnArgs = args.concat(args2);
      let that = this instanceof resultFn ? this : asThis;
      return fn.apply(that, fnArgs);
   };
   //  Enable the instance to inherit the prototype of the binding function 
   function tmp() {};
   
   tmp.prototype = fn.prototype;
   resultFn.prototype = new tmp();
   return resultFn;
}

 

原网站

版权声明
本文为[Xiaoding Chong duck!]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202130617454256.html