Fei

JavaScript中apply和bind的关系

2015-09-18

apply 和 call

每个函数都包含两个非继承而来的方法:apply()call()。这两个方法的用途都是在特定的作用域内调用函数,实际上等于设置函数体内this对象的值。其中call()本质上是apply()的语法糖,在参数少于 3 个时使用call()更简洁。

apply

apply()方法接收两个参数,一是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数既可以是 Array 实例,也可以是 arguments 对象。

function sum(num1, num2) {
  return num1 + num2
}

function applySum1(num1, num2) {
  return sum.apply(this, arguments)
}

function applySum2(num1, num2) {
  return sum.apply(this, [num1, num2])
}

console.log(applySum1(10, 20)) // 30
console.log(applySum2(10, 20)) // 30

call

call()方法与apply()方法作用相同,它们的区别主要在于接收参数的方式不同。对于call()方法而言,第一个参数不变,第二个参数变成将传递给函数的参数逐个列举。

function sum(num1, num2) {
  return num1 + num2
}

function callSum(num1, num2) {
  return sum.call(this, num1, num2)
}

bind

通过使用apply()call(),可以极为方便的扩展函数作用域:

window.color = 'red'

var o = {
  color: 'blue',
  sayColor: function() {
    console.log(this.color)
  },
}

function sayColor() {
  console.log(this.color)
}

sayColor.call(this) // red
sayColor.call(window) // red
sayColor.call(o) // blue
o.sayColor() // blue

身为 JavaScript 中的一等公民,函数可以作为结果返回。这个特点使得用现有函数构造新函数成为可能:

var o = {
  color: 'blue',
}

function sayColor() {
  console.log(this.color)
}

o.sayColor = function() {
  return sayColor.apply(this)
}

o.sayColor() // blue

将这种操作封装,便有了bind()方法:

if (typeof Function.prototype.bind !== 'function') {
  Function.prototype.bind = function(scope) {
    var fn = this,
      slice = Array.prototype.slice,
      args = slice.apply(arguments, [1])

    return function() {
      return fn.apply(scope, args.concat(slice.apply(arguments, [0])))
    }
  }
}

此时,bind()不仅可以绑定函数作用域,还可以固化任意参数,构造新函数。