最近看到很多面试题中都提到了apply(),call()bind(),遂搞清楚了记录下。

1、基本作用:

改变对象的执行上下文。

什么是执行上下文

我们在写一个方法的时候,总是会用到一个关键字this,而this的指向就是我们这里所说的执行上下文(执行环境)

为什么需要改变执行上下文?

小明有一个炒菜的铲子,小明的室友小刚今天突然想自己做菜吃,但是小刚没有铲子。小刚又不想为了做个菜单独买把铲子,于是就借用了小明的铲子,这样既达到了目的,又节省了开支,一举两得

改变执行上下文也是一样,A对象有一个方法,而B对象因为某种不可言说的情况也需要用到一样的方法,那么这时候我们是单独为B扩展个方法呢,还是借用一下A的方法呢?当然是借用A的啦,既完成了需求,又减少了内存的占用.

2、call()和apply()

每个函数都有两个非继承的方法call()apply()call()apply()都属于Function.prototype的一个方法,他是JavaScript
引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例,都有call(),apply()属性。其实他们的作用是一样的,
只是传递的参数不一样而已。

  • call():接收两个参数,第一个参数指定了函数体内this对象的指向,后面的参数是函数调用时的参数按顺序传递。
  • apply():接收两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为数组或者一个类数组。apply传入
    的是一个参数数组,也就是将多个参数组合成为一个数组传入。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
let obj1 = {
name: "cherryl",
getName: function(){
return this.name;
}
}
let obj2 = {
name: 'liuqian'
}
console.log(obj1.getName()); // "cherryl"
console.log(obj1.getName.call(obj2)); // "liuqian"
1
2
3
4
5
function showArgs(a, b, c){
console.log(a,b,c);
}
showArgs.call(this, 3,4,5); // "3 4 5"
showArgs.apply(this, [5,6,7]); // "5 ,6,7"

面试题:定义一个 log 方法,让它可以代理 console.log 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function log(msg){
console.log(msg);
}
log(12); //12
log(1,2); //1
//上面能够基本解决打印输入问题,但是下面多参数的时候就gg了。换个更好的方式吧。
//去面试的时候,面试官问我怎么使用传进去的参数,我说直接使用,他说不对,是通过一个arguments数组
function log(){
console.log.apply(console,arguments);
}
log(12); //12
log(1,2) //1 2
////接下来是要在每一条打印信息前面都要加上一个特定字符串'Error'又怎么说呢?
function log(){
let args = Array.prototype.slice.call(arguments);//将类数组转化为数组
args.unshift("Error")
console.log(console,args);
}
log(12)
log(1,2)

3、bind()

bind方法与apply和call很相似,也是可以改变函数体内this的指向。

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

举个例子:

1
2
3
4
5
6
7
8
9
10
var func = function(){
console.log(this.x);
console.log(arguments);
}
func()
var obj = {
x:2
}
var bar = func.bind(obj,1)
bar() //2 , {'0':1}

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

4、这三个方法的异同

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
x: 81,
};

var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81
console.log(foo.getX.bind(obj)()); //81

看到bind后面对了一对括号。区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。

总结:

apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind是返回对应函数,便于稍后调用;apply、call则是立即调用

参考:
《call,apply,bind》-Husbin