Function.prototype.call.bind(Array.prototype.push)

1,969 阅读2分钟

1.前言

最近在看es6-shim这个库,看到前几十行,就发现了如标题一样的语法。刚看到时,内心os:这特娘的是个啥?一脸蒙蔽。为啥call后面能直接调用bind呢。这种语法又是为了解决什么呢?......

2.这种语法的使用方式及原因

使用方式如下。原因方面,个人认为有两处。

  • 可以用来处理原生api不能处理的数据。
  • 使代码更简洁。

(1)以数组的push方法为例:

const push = Function.prototype.call.bind(Array.prototype.push)
function fn () {
    const args = arguments
    // 这里不能使用原生的push语法,如args.push('i am pushed'),因为args是一个伪数组。
    push(args, 'i am pushed')
    console.log(args)
}
fn()

(2)在检测数据类型时,有一个方法是使用Object.prototype.toString.call

// 1.原始使用
const objStr = Object.prototype.toString.call({})
console.log(objStr)
// [object Object]

const arrStr = Object.prototype.toString.call([])
console.log(arrStr)
// [object Array]

可以看到,原始使用方法过于冗余繁杂,想在es6-shim中使用根本是不合适的。

// 2.简洁语法
const toString = Function.prototype.call.bind(Object.prototype.toString)
toString({})
toString([])
...

3.原理

分析原理的话,我们把这句语法Function.prototype.call.bind(Array.prototype.push)拆分开来,一点点说。

3-1.Function.prototype.call.bind

乍一看刚开始的语法,不能理解call后面直接加了bind。但其实call本身就是一个函数。既然是函数那么就自然能够调用bind。调用之后,会把call中的this指向bind绑定的值。即:

const obj = {}
// bind会返回一个新函数,这个函数与call函数使用方式相同,需要知道bind的源码。
const fn = Function.prototype.call.bind(obj)
fn(this, args)
// 其实Function.prototype.call.bind(obj)就相当于obj.call
// 因为bind绑定了this值,而this值始终指向函数的调用者。所以.bind(obj) === obj.
3-2.Function.prototype.call.bind(Array.prototype.push)

根据3-1可知,Function.prototype.call.bind(Array.prototype.push)就相当于Array.prototype.push.call。 所以:

const arr = []
const push = Function.prototype.call.bind(Array.prototype.push)
push(arr, 'hello')

Array.prototype.push.call(arr, 'world')
console.log(arr) // ['hello', 'world']

4.结语

最近项目提测,有了点时间钻研问题。call,apply以及bind的使用方法倒是烂熟于心,但标题的这句语法刚看到时真的如同当头一棒。理解这个最主要的一点是bind绑定的值决定了call函数的调用者。是晚上做梦的时候突然想通的。😂