call、apply、bind的实现和区别

145 阅读2分钟

call、apply、bind的实现

call/apply

call和apply的区别

调用 call 和 apply改变了函数的 this 上下文后 马上 执行该函数,若指向 undefined 或者 null ,会将 this 指向 window

  • call:可以接收多个参数,第一个参数为函数上下文(this),后边参数为函数本身的参数
  • apply:只能接收两个参数,第一个参数为函数上下文(this),第二个参数为函数本身的参数,但是以数组形式传入

call

var name = '了不起'
let obj = {
  name: '贾正经'
}

function fun() {
  console.log(this)
  console.log(this.name)
}

Function.prototype._call = function (ctx, ...args) {
  //判断ctx,如果为null或undefined,则为window,否则用Object()转为对象
  const context = (ctx === null || ctx === undefined) ? window : Object(ctx)
  //使用Symbol为context添加一个独一无二属性,将this(调用_call的函数)赋值给这个属性
  const key = Symbol()
  context[key] = this
  //立即执行一次
  context[key](...args)
  //删除方法
  delete context[key]
}

测试一下

fun._call(obj)//{ name: '贾正经', [Symbol()]: [Function: fun] }   贾正经
fun._call(null)//window  了不起
fun._call(undefined)//window  了不起

apply

applycall的的唯一区别就是传参不一样,所以改变对参数的处理,其他一致就可以了

var age = 18
let obj = {
  age: 20
}

function fun(b, c) {
  console.log(this.age)
  console.log(this.age + b + c);
}

Function.prototype._apply = function (ctx, arr = []) {
  //判断ctx,如果为null或undefined,则为window,否则用Object()转为对象
  const context = (ctx === null || ctx === undefined) ? window : Object(ctx)
  //使用Symbol为context添加一个独一无二属性,将this(调用_call的函数)赋值给这个属性
  const key = Symbol()
  context[key] = this
  //立即执行一次
  context[key](...arr)
  //删除方法
  delete context[key]
}

测试一下

fun._apply(obj, [1, 2 ]) // 20  23
fun._apply(null, [1, 2 ])//18  22

bind

bind 是返回改变了上下文后的函数, 不执行该函数, bind可以接收多个参数,第一个参数为函数上下文(this),后边参数为函数本身的参数,bind返回一个未执行的函数,可以在执行时再次传入参数

var name = '了不起'
let obj = {
  name: '贾正经'
}

function fun(...args) {
  console.log(this)
  console.log(this.name)
  console.log(...args);
}

Function.prototype._bind = function (ctx, ...args) {
  //先保存一下this
  let self = this
  //bind不会立即执行,创建一个函数
  const fn = (...rest) => {
    //使用call改变this指向
    return self.call(ctx, ...args, ...rest)
  }
  //若没有prototype(如箭头函数),则需要给fn的prototype赋值为源函数的prototype
  if (self.prototype) {
    fn.prototype = Object.create(self.prototype)
  }
  //返回函数
  return fn
}

测试一下

fun._bind(obj,1,2)()//{"name": "贾正经"} 贾正经  1 2
fun._bind(obj,1,2)(3,4)//{"name": "贾正经"} 贾正经 1 2 3 4
fun._bind(null,1,2)(3,4)//window 了不起 1 2 3 4

第一次写文章欢迎指点吐槽or点赞!