javascript bind函数的实现

既然讲到bind,我们就不得不说call 和 apply 。在Javascript中,涉及到函数式语言风格的代码,都离不开call 和apply。那么我们在讲bind之前,就先好好分析一下call 和 apply。

call和  apply 方法是ECMAScript 3 给Function 的原型定义的2个方法。分别是Function.prototype.call 和 Function.prototype.apply。

1: call 和apply 的作用

call 的mdn定义:   call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

apply的mdn定义:apply()方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象提供的参数

通过以上的定义,我们可以看出,call 和 apply 的相同点都是执行一个函数,并指定this。

唯一的区别就是:

  • apply 函数第二个参数为一个带下标的集合,这个集合可以是数组,也可以是类数组。apply 把这个集合中的元素作为参数传递给被调用的函数。
  • call 函数的参数数量不固定,从第一个参数以后, 每一个参数被依次传入函数。

请看下面2个🌰

var func = function (a,b,c) {
 console.log([a,b,c]) // 输出 [1,2,3]
}

func.apply(null, [1,2,3])
var func = function (a,b,c) {
  console.log([a,b,c]) // 输出[1,2,3]
}
func.call(null, 1,2,3)

其实Javascript参数在内部就是用一个类数组来表示的。我们可以通过arguments类数组来接受。call其实是包装了apply上的一个语法糖。如果我们明确的知道函数接受多少个参数,而且想一目了然的表达形参和实参的对应关系,可以使用call 来传递参数。

有一点需要注意的是:当使用call 或apply时,第一个参数为null, 函数体内的this会指向默认的宿主对象。

// 在浏览器环境下
var func = function (a,b,c) {
  console.log(this === window)  // true
}
func.apply(null, [1,2,3])

Math.min.apply(null, [1,2,3,34,4]) // 1

2: 说清楚apply和call ,我们开始讲讲bind。

bind 用来改变this的指向,并返回一个改变了this指向的新函数。

我们首先实现一个比较简单的bind。

Function.prototype.bind = function (context) {
  // this  指的是需要绑定的函数
  let self = this
  return function () {
    // context 指需要绑定的this
    return self.apply(context, arguments)
   }
}

来一个🌰演示一下

let obj = {name: 'jack'}

var func = function () {
  console.log(this.name) // jack
}.bind(obj)

func()

以上代码的实现原理是,我们先把func函数的引用保存了起来。然后返回一个新函数。当我们将来执行func函数时。实际上是执行这个返回的新函数。在新函数内部,我们执行了self.apply(context, arguments)这一句才是执行了原来func函数。并指定了context为func函数体内的this。

说清楚了原理,我们实现一个更完善的bind实现:

Function.prototype.bind  = function () {
 let self = this
  // 获取bind第一个参数,即需要绑定的对象。
 context = [].shift.call(arguments)
 // 获取bind剩余的参数,并转换为数组
 args = [].slice.call(arguments)
 // 返回一个函数,当执行的时候,执行self.apply
 return function () {
     // arguments 是执行返回的函数时,传入的参数。
     return self.apply(context, [].concat.call(args, [].slice.call(arguments)))
 }
}

我们再给一个🌰演示下:

var obj = {
 name: 'jack',
}
var func = function (a,b,c,d) {
 console.log(this.name) // jack
 console.log([a,b,c,d])  // 输出 [1,2,3,4]
}.bind(obj, 1,2)

func(3,4)

我们用es6 来改造下这个写法:

Function.prototype.bind  = function (context, ...res) {
  let self = this
   // 获取bind第一个参数,即需要绑定的对象。
  context = context
  // 获取bind剩余的参数,并转换为数组
  args = res
  // 返回一个函数,当执行的时候,执行self.apply
  return function () {
      // arguments 是执行返回的函数时,传入的参数。
      return self.apply(context, [...args, ...arguments])
  }
}

几行代码就可以解决,是不是简洁了许多😄

posted @ 2021-03-09 19:39  eastsae  阅读(143)  评论(0编辑  收藏  举报