Skip to content
This repository has been archived by the owner on Nov 30, 2024. It is now read-only.

模拟实现call、apply和bind #19

Open
bruce-16 opened this issue Aug 23, 2019 · 0 comments
Open

模拟实现call、apply和bind #19

bruce-16 opened this issue Aug 23, 2019 · 0 comments

Comments

@bruce-16
Copy link
Owner

bruce-16 commented Aug 23, 2019

这三个函数存在于函数的原型链上,callapply用来改变函数的this指向,也就是执行上下文对象。bind函数也是用来修改执行上下文对象,只是它返回一个修改后的一个新函数,不会自动执行。

call

首先看一下这样的代码:

let obj = {
  a: 1
}

function fn() {
  console.log("@@ this.a", this.a)
}

obj.fn = fn
obj.fn() // 1

如上可以看到,如果单独去执行fn函数的时候,this是指向默认执行window对象的。当把fn挂载到obj上,然后通过obj去访问的时候,fn这时候的this就会指向obj了。这种情况是基本知识,应该都是知道的。那么重点就是利用这种方式来模拟call的实现。

function call(fn, obj, ...args) {
  if (typeof fn !== 'function') {
    return
  }
  let ctx = obj || {}
  let key = Symbol()
  ctx[key] = fn
  let rst = ctx[key](...args)
  delete ctx[key]
  return rst
}
  1. 首先判断传入的fn是否为函数
  2. 如果传入的obj为空,这里其实可以默认为window,默认为一个对象是为了好让fn绑定到这个对象的某一个属性上。
  3. 使用Symbol创建一个不会重复的key
  4. fn绑定到对象上,然后执行得到结果,删除刚添加的属性,将值返回。

以上就是怎么实现call的思路了,至于apply的实现,几乎就是一样的,只是因为传入的参数有所不同,所以在执行传参的时候差别,如下:

function apply(fn, obj, args){
  ...
  if (!Array.isArray(args)) {
    args = [args]
  }
  ...
  let rst = ctx[key](...args)
  ...
}

bind

主要重点就是bind是返回一个新的函数,并且可以绑定传入参数。

function bind(fn, obj, ...args1) {
  if (typeof fn !== 'function') {
    return fn
  }
  let self = obj || {}
  return function (...args2) {
    return fn.apply(self, [].concat(args1).concat(args2))
  }
}
  1. 返回一个函数形成一个闭包,将传入的obj进行一下判断复制。
  2. 调用fnapply方法修改this指向,然后拼接两次传入的参数为一个数组。注意这里的拼接顺序

对于bind函数,网上还有对fn函数为构造函数时候的情况进行判断。主要是使用判断当使用new关键字的时候再去new fn(...args)

function bind(fn, obj, ...args1) {
  ...
  return function bindFn(...args2) {
     // 判断是否用于构造函数
     if (this instanceof bindFn) {
          return new self(...args1, ...args2)
      }
    ...
  }
}

小结

理解了这三个函数的作用,就可以利用this指向的原理去模拟这些方法,以上都用使用es6的一些方法,代码看起来简洁不少。

参考文章

  1. awesome-coding-js
@bruce-16 bruce-16 changed the title 模拟实现call、apply 模拟实现call、apply和bind Aug 23, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant