5分钟理解bind、call和apply

2021-01-13
80
189

在JavaScript语言中,bind、call、apply都是用来改变函数的this对象的指向的。就是说都可以用来代替另一个对象调用一个方法,将一个函数的对象的执行期上下文从初始的上下文改变为由当前对象的上下文。其中bind较为特殊,因为它并不会立即执行,而是会返回一个新的函数。

先通过一段代码看看普通函数以及函数作为对象属性的情况下this指向是怎样的

function test() {
    console.log(this)
}
// this 默认指向 window,严格模式下指向 'undefined'
test() // window

let obj = {
    foo: test
}

// obj.foo 引用了 test 函数
obj.foo() // 打印 obj 自身

Function.prototype.call/apply

使用 Function.prototype.call()Function.prototype.apply() 可以轻松实现上面一样的效果,这次给obj对象增加一个属性方便辨识

let obj = { name: 'Pork', foo: test }

function test() {
    return this.name
}

test.call(obj) === obj.foo() // true
test.apply(obj) === obj.foo() // true

如果你对作用域中 变量/函数提升的概念不是很理解的话,你可能会很疑惑我上面的代码: test函数的定义明明比在obj对象的下面,而obj中的foo属性引用了test函数居然没有报错,这其实是由变量提升概念所带来的“益处”。

所有使用 var 声明的变量和定义的 function 都会默认被提升到该作用域顶部

Function.prototype.apply的使用跟Function.prototype.call几乎无异,区别在于传参列表不同:

  • call(obj, arg1, arg2, arg3, ...)
  • apply(obj, ...argArray)
function test(a, b, c) {
    this.a = a
    this.b = b
    this.c = c
}

let obj = {}
test.call(obj, 1, 2, 3)
console.log(obj) // {a: 1, b: 2, c: 3}

let anotherObj = {}
test.apply(anotherObj, ['Hello', 'World', '...'])
console.log(anotherObj) // {a: "Hello", b: "World", c: "..."}

Function.prototype.bind

Function.prototype.bind 跟前面二者不同的地方在于该操作会返回一个新函数,而不会立即执行

let obj = { name: 'Pork', foo: test }

function test() {
    return this.name
}

test.bind(obj)() === 'Pork' // true

// 通常更推荐这样写
const newFn = test.bind(obj)
newFn() === 'Pork' // true

Function.prototype.bind 的传参列表跟 apply 一样:

  • bind(thisArg, ...argArray)
80

文章版权所有:PORK's BLOG,采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。

欢迎分享,转载务必保留出处及原文链接