前言
近期在翻《你不知道的Javacript》,读来大有裨益,记忆短促,只有一边记录,一边体会,才能融会贯通。同时,也向大家分享这些你知道或不知道的知识点,希望“你不知道的Javascript”,会成为我们都知道的Javascript。
简介
本片内容节选和总结自书籍第二部分,this和对象原型一章。
默认绑定(this指向window)
function foo() {
console.log(this.a);
}
var a = 2;
foo(); // 2
复制代码
分析:先根据词法静态分析,foo所在作用域是全局作用域,声明foo函数会向window对象添加一个foo的属性。调用foo方法相当于调用window.foo方法,所以此时foo中的this指向window对象,所以this.a的值即为外部变量a的值。
function foo() {
'use strict';
console.log(this.a);
}
var a = 2;
foo(); // TypeError
复制代码
But,如果在严格模式下运行代码,this并不会默认绑定到全局对象上。
隐式绑定(绑定至调用对象)
funtion foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); //2
复制代码
分析:对象obj的foo属性引用了foo函数,并且调用foo函数,是通过obj.foo来访问的,因此foo中的this指向了调用它的对象obj。可以想象一下,在另一门xxxSript语言中,它所有的语法和语义和Javascript完全一致,唯独它的全局对象名称叫“obj”。那么你应该发现了,隐式绑定其实和上面的默认绑定是一样的,只不过默认绑定有了window的默认语义。
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
复制代码
由码可见,在通过一系列对象引用调用函数时,函数中this仅仅绑定到它的直接调用对象上,简单的讲,谁牵着foo的小手,谁就是foo的小主子~。
隐式丢失(多级引用的迷惑)
function foo() {
console.log( this.a )
};
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo;
var a = "oops, global";
bar(); // "oops, global"
复制代码
不看答案,你有没有觉得这块代码会输出2?先来看bar是如何被调用的。首先obj对象的foo属性引用了foo函数,然后又将这个引用赋予了bar变量,最终我们调用了bar方法。我们知道js中对象和函数的赋值都是以引用的方式进行的,那么这里无论是obj.foo还是bar,都仅仅只是对foo函数的一个引用,在内存中的模样,仅仅只是记录了foo函数的内存地址。foo函数定义在全局环境中,显而易见,此处调用bar方法,也就是调用了window.foo函数。切记,不要被函数引用迷惑了双眼~
显式绑定
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2
复制代码
Javascript中提供了两个方法可以进行上下文(this)的显式绑定,call和apply这两个大名鼎鼎的方法想必大家都使用过或有所耳闻。在上述代码中,通过call方法调用了foo函数,并传入了obj对象作为foo函数的上下文,那么foo函数中的this也就指向了这个上下文环境的宿主,即obj对象。否则这里就要输出undefined了~
- 硬绑定
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // // 硬绑定的 bar 不可能再修改
bar.call( window ); //
复制代码
通过call或apply方法强制绑定上下文,并通过函数二次调用,便可以避免this的默认绑定和绑定丢失。foo的this指针被绑定到obj对象中,并通过匿名函数来调用,这样无论如何都不会丢失obj的上下文环境了。
new绑定
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
复制代码
通过构造函数创建的对象,构造函数的this会自动指向这个生成对象。
优先级
以上的几种this绑定规则的优先级,可以通过这些方法来判断:
- 函数是否通过 new 调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。 var bar = new foo()
- 函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是 指定的对象。 var bar = foo.call(obj2)
- 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上 下文对象。 var bar = obj1.foo()
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定 到全局对象。 var bar = foo()
总结
this实际是函数执行时,上下文环境的指针变量。通常情况下,找到函数的调用对象,再分析这个对象的上下文环境,便可以解决与this相关的问题。