写给自己的 JavaScript 知识点拾遗 - 5

今天早读看文章的时候,看到这样一行代码:

1
var arg_fn = Array.prototype.slice.call(arguments)

这行代码其实就是调用数组的 slice 方法将本来为类数组的 arguments 转换为纯属组。然后回想起 call,又联想起 apply 和 bind 感觉有些些记不清了,就再巩固一下。

首先,明确知道的是:

  1. 函数有定义时上下文,运行时上下文,上下文是可以改变的这样的概念。
  2. call、apply 和 bind 都可以用来修改函数体内部的 this 指向。

call 和 apply

call 和 apply 的作用完全一样,唯一不同的是传入的参数不太一样。就想是下面这样:

1
2
3
func.call(this, arg1, arg2...)

func.apply(this, [arg1, arg2])

call 方法是将参数一个一个传进去,apply 方法是将参数放在数组里传进去。在 JavaScript 中,函数的参数数目不确定,所以要说适用的情况,也只能说当参数数目不固定时,最好适用 apply 方法,其他的时候,具体情况具体分析。

像下面这种:

1
2
3
let number = [1, 2, 3, 4, 5, 6]
let maxNumber = Math.max.apply(Math, number)
let mexNumber = Math.max.call(Math, ...number)

如果要细分 call 和 apply 的用法,可以分成下面几种:

  1. 改变 this 的指向:
1
2
3
4
5
6
7
8
9
let obj = {
name: 'shuai'
}

function log() {
console.log(this.name)
}

log.call(obj) //shuai
  1. 借用别的对象的方法,如上面的例子。
  2. 调用函数:
1
2
3
4
5
function fun() {
console.log('shuai')
}

fun.call()

apply 和 apply 的方法都会使函数立即执行,但是基本用不到这样的方法去调用函数。

bind

使用 bind 方法和 call、apply 一个很大的不同点是,他不会立即执行方法,而是返回一个改变了上下文 this 指向的函数。参数形式类似 call。可接受多个参数。

官方解释是:

bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

来个刚才的栗子🌰改写:

1
2
3
4
5
6
7
8
9
10
11
let obj = {
name: 'shuai'
}

function log() {
console.log(this.name)
}

let fun = log.bind(obj)

fun() //shuai

log 方法中的this指向并没有变。

还有一个很好的栗子🌰说明了参数的问题:

1
2
3
4
5
6
7
8
9
function log(a, b, c) {
console.log(a, b, c);
}
var func1 = func.bind(null,'shuai');

func('A', 'B', 'C'); // A B C
func1('A', 'B', 'C'); // shuai A B
func1('B', 'C'); // shuai B C
func.call(null, 'shuai'); // shuai undefined undefined

注意:

在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。