Function direct call vs func.call() vs func.bind() in JavaScript

JavaScript 有三种函数调用方式 (其他奇葩的不讨论) :

  1. func()
  2. func.call()func.apply()
  3. var binded = func.bind()

第一种很好理解,就是直接调用。

第二种很常用,经常用于调整函数的 this 指向的对象。如:

function say_hello () {
  return 'Hello, ' + this.name
}

say_hello.call({ name: 'John' }) // 'Hello, John'

第三种是 [EcmaScript 5] 新加入的 [Function.prototype.bind()]。它和 call() 类似,但它不是直接调用,而是返回一个新的函数:

function withdrawCash (amount) {
  if (this.bank.cash < amount) {
    throw new Error('You do not have enough money')
  }
  this.bank.cash = this.bank.cash - amount
  console.log('Cash ' + amount + ' out')
}

var John = withdrawCash.bind({
  bank: {
    cash: 1000
  }
})

var Marry = withdrawCash.bind({
  bank: {
    cash: 10000
  }
})

John(1000)        // 'Cash 1000 out'
Marry(10000)      // 'Cash 10000 out'
Marry(1)          // throw 'You do not have enough money'
withdrawCash(100) // throw 'TypeError: Cannot read property 'cash' of undefined'

从例子可以看到,JohnMarry 的数据是分开,相当于为他们单独定义了 withdrawCash()。如果用 call() 实现的话就是这样:

var John = {
  bank: {
    cash: 1000
  }
}

var Marry = {
  bank: {
    cash: 10000
  }
}

withdrawCash.call(John, 1000)
withdrawCash.call(Marry, 10000)
withdrawCash.call(Marry, 1)

虽然可以达到一样的效果,但是 withdrawCash.call() 明显不如 var func_name = withdrawCash.bind(BankInfo) 易读和易维护,而且后续调用时更简单,不需要再次指定 call() 的第一个参数,减少 typo 造成的隐患。

看到这里,有人会想:「既然 bind()call() 好,那以后就用它好了」。

虽然不少浏览器已经支持,也有 shim 可用,而且至少 Node 环境是可以无障碍地使用,但我还是要说,慎用 bind(),因为它有性能问题。

这是在 jspert.com 上的测试结果

function direct call vs call() vs bind()

这是在 Node 上的测试结果:

{ http_parser: '2.3',
  node: '0.11.14',
  v8: '3.26.33',
  uv: '1.0.0',
  zlib: '1.2.3',
  modules: '14',
  openssl: '1.0.1i' }
✓  direct call x 68,038,524 ops/sec ±0.88% (94 runs sampled)
✓  call() x 17,655,227 ops/sec ±0.87% (95 runs sampled)
✓  bind() x 4,607,937 ops/sec ±0.91% (93 runs sampled)
Fastest is direct call

虽然 bind() 的性能是超过了每秒百万次,但如果一处代码多次使用,性能就会急剧下降:

func.call(John)
func.call(Marry)

func.bind(John)();
func.bind(Marry)();
✓  call() x 9,328,537 ops/sec ±0.88% (93 runs sampled)
✓  bind() x 221,863 ops/sec ±1.92% (85 runs sampled)

因此建议:

[EcmaScript 5]: http://www.ecma-international.org/ecma-262/5.1
[Function.prototype.bind()]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
[Koa Middleware]: http://koajs.com/#application

 
8
Kudos
 
8
Kudos

Now read this

Control Flow in JavaScript

这是一篇我在飞飞商城针对 JavaScript Control Flow 的演讲稿文字版。 Control Flow 是什么? # Control Flow 就是控制流,简单地归纳,就是代码执行的顺序(和控制的手段)。 常见的控制流有: if-else switch-case for / while loop goto 问题儿:JavaScript # 由于 JavaScript 的运行环境,决定了她拥有异步执行的能力。 if (checkEmail() ===... Continue →