前言
看博客文章和别人写的代码时,经常会看到函数调用call(),apply(),bind()方法,因为很少见到函数本身去调用一个方法,所以感到很奇怪,也造成了对这三个方法的理解上的障碍。退后一步,仔细梳理了一下思路,发现了我产生奇怪感觉的来源。那就是函数在我的知识体系里,是作为一个方法的,里面包含了对数据对象的操作。而call,apply,bind等于是方法调用方法,这就让我疑惑了。但是现在看来,这并没有奇怪的。因为主体部分,还是调用的方法。而call,apply和bind是用来完善调用方法的。既然有完善,那就肯定有缺漏的地方,因为需要使用call,apply,bind来弥补缺漏。这个缺漏之处就是函数的this
值,本质上来说是函数的执行上下文,call,apply和bind就是用来改变函数的执行上下文的。
函数也是对象
在JS中,函数其实是对象,可以看做是构造函数Function()的实例。那么作为对象,就必然具有方法,所以函数可以使用apply,call和bind方法。这一点告诉我们,这3个方法是由函数本身调用的。
上下文(context)
JS中函数是有上下文这个概念的,怎么理解呢?可以理解为函数所处的环境状态,这个环境状态提供了函数调用所需要的相关信息。比如,我们看以下的这段 JavaScript 代码:
var b = {}
let c = 1
this.a = 2;
要想正确执行它,我们需要知道以下信息:
- var 把变量b声明到哪里;
- b表示哪个变量;
- b的原型是哪个对象;
- let把c声明到哪里;
- this指向哪个对象;
这些信息就需要执行上下文来提供了,也就是执行环境。除了执行上下文,还有定义上下文,指的是函数定义时的环境。这两者都是函数的环境,不同的是所处的时刻不一样。
this值
this
是JS语言的一个关键字。它是函数运行时,在函数体内部自动生成的一个对象,只能在函数内部使用。
function test() {
this.x = 1;
}
上面代码中,函数test运行时,内部会自动有一个this
对象可以使用。那么this
的值是什么呢?不同的函数调用场合,this
的值也是不一样。总的来说,this
就是函数运行时所在的环境对象。
this的用法
- 纯粹函数的调用
这是函数最普通的调用,这里的this
代表的就是全局对象
var x = 1;
function test() {
console.log(this.x);
}
test(); // 1
- 作为对象方法的调用
当函数作为对象的方法调用时,this
代表的就是这个上级对象。
function test() {
console.log(this.x);
}
var obj = {};
obj.x = 1;
obj.m = test;
obj.m(); // 1
- 作为构造函数调用
构造函数是指可以通过构造函数本身生成一个新的对象的函数,它也是函数。我们一般使用new
关键字来调用构造函数。这里的this
指的就是这个新对象,或者叫做实例。
function Person() {
this.name = '龙傲天'
}
var person = new Person()
console.log(person.name) // 龙傲天
- 箭头函数
箭头函数是ES6引入的新语法,写起来简洁明了。而且重要的是箭头函数里的this
值比较特殊,不受调用时的环境影响而改变。它始终都是继承自函数定义时上级对象的this
值。
var o = {}
o.foo = function foo(){
console.log(this);
return () => {
console.log(this);
return () => console.log(this);
}
}
o.foo()()(); // o, o, o
这个例子中,我们定义了三层嵌套的函数,最外层为普通函数,两层都是箭头函数。这里调用三个函数,获得的 this 值是一致的,都是对象 o。
改变函数内部this
的方法
JS还提供了一些方法,在函数调用时改变函数内部的this
值。那就是Function.prototype.call()、Function.prototype.apply()和Function.prototype.bind()。这里要注意,这三个方法是不能改变箭头函数的this
值的,只能传入参数。
- call()和apply()
function foo(a, b, c){
console.log(this);
console.log(a, b, c);
}
foo.call({}, 1, 2, 3);
foo.apply({}, [1, 2, 3]);
如上面代码所示,函数foo的this
值被call和apply从全局对象改为了空对象{}。这两个方法的语法是一样的,只是传参方式有所区别。
- bind()
bind()方法生成了一个绑定了this
值的新函数。
function foo(a, b, c){
console.log(this);
console.log(a, b, c);
}
foo.bind({}, [1, 2, 3])();
参考文章
1. 深入浅出 妙用Javascript中apply、call、bind
2. [译] JavaScript 中至关重要的 Apply, Call 和 Bind
3. Javascript 的 this 用法——阮一峰
4. 《重学前端》18讲
网友评论