手撕JS内置的call、apply、 bind

107 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

手撕JS内置的call、apply、 bind

手撕call

const fn = function fn(x, y) {
    console.log(this, x, y);
    return x + y;
};
let obj = {
    name: 'obj'
};
/* 
fn() //this -> window
obj.fn(); //Uncaught TypeError: obj.fn is not a function
需求:fn执行的时候让里面的this -> obj
fn首先基于__proto__,找到Function.prototype.call方法,把call方法执行[this->fn  第一个参数obj 其余参数:10/20]
call 方法执行的时候,内部做了一些处理:把fn()执行,并且让fn()中的this指针,指向第一个参数(obj),并且把传递的参数10/20传递给函数,最后接收函数的返回值进行返回
*/
Function.prototype.call = function call(context,...params){
  // this -> obj  context->obj  params -> [10,20]
  if(context == null) context = window
  if(!/^(object|function)$/i.test(typeof context)) context = Object(context)
  let key = Symbol(),
      result
  context[key] = this
  result = context[key](...params)
  delete context[key]
  return result
}
let result = fn.call('AA', 10, 20)
console.log(result)

apply

  • applybind不同的是在于传参的时候aplly可以接收一个参数列表,改动即可
Function.prototype.apply = function apply(context,params){
  // this -> obj  context->obj  params -> [10,20]
  if(context == null) context = window
  if(!/^(object|function)$/i.test(typeof context)) context = Object(context)
  let key = Symbol(),
      result
  context[key] = this
  result = context[key](...params)
  delete context[key]
  return result
}

bind

  • bindcall/apply是不一样的,call/apply处理的时候,是可以把函数立即执行的,但是bind并不会立即执行函数,而只是预处理this和参数
Function.prototype.bind = function bind(context,...params){
  // this -> fn  context -> obj  params -> [10,20]
  let self = this
  return function (...args){
    return self.call(context, ...params.concat(args))
  }
}
const fn = function fn(x, y, ev) {
    console.log(this, x, y, ev);
    return x + y;
};
let obj = {
    name: 'obj'
};
// document.body.onclick = fn; //this->body  x->事件对象  y->undefined
// 需求:点击BODY的时候,把fn执行,但是方法中的this想改成obj,并且传递10/20
document.body.onclick = fn.bind(obj, 10, 20);