首发个人博客
JavaScript 中的this
,大家都用过。但是它到底指向哪里呢?今天在阅读 《你不知道的JavaScript (上卷)》再结合自己平时看的博客,对它又有了新的认识,在此来做个小结,再碰到this
,就再也不用担心不知道它指向哪里了。
与调用位置有关,而与定义位置无关
以下示例(在浏览器端运行)
function sayHi(){
var hi = 1;
console.log(this.hi);
}
var hi = 2;
var obj = {
hi : 3,
sayHi : sayHi
}
// output
sayHi(); // 2
obj.sayHi(); // 3
从上述代码的执行结果我们可以看出,直接调用 sayHi()
函数,它输出的 this.hi
不指向函数体内自己定义的 hi
变量,也就是说,this
的指向与词法作用域无关,这也是刚接触 JavaScript的同学常犯的一个错误(包括我自己),认为 this.xx
就指向函数体里面定义的变量 xx
。sayHi
函数的只定义了一次,但obj.sayHi()
与 sayHi()
的输出结果不一样,也恰恰证明了 函数体内this的指向与函数定义的位置无关,而与函数被调用的位置有关,至于为什么输出结果不一样,在下文会讲到。
this 的绑定规则
默认绑定
默认绑定比较常见,表现形式就是在全局作用域中独立调用,如上文中的 sayHi()
,这种直接调用方式函数体内的 this
就应用了默认绑定规则,默认绑定有以下两种情况。
- 在严格模式下(指在函数声明的过程中,处在严格模式,而不是函数调用处于严格模式),函数体内
this
绑定到undefined
- 在非严格模式下,函数体内
this
指向全局对象window
/***** 以下例子为处在严格模式下 *****/
function fn1(){
'use strict'
console.log(this);
}
fn1(); // undefined
/***** 以下例子处于非严格模式下被调用 *****/
function fn2(){
console.log(this);
}
fn2(); // window
function fn3(){
console.log(this);
}
/*
* 这也是在非严格模式下被调用
* 因为在函数定义时没有用严格模式
*/
'use strict'
fn3(); // window
隐式绑定
隐式绑定的常见形式为 obj.fn()
,若obj
对象中有 fn
这个方法(fn可以在别处定义,但必须被添加到obj中作为obj的一个属性方法),那么 fn
中的 this
就指向 obj
对象,如最开始代码中 obj.sayHi()
输出3,就是因为sayHi
中的this
隐式绑定到obj
对象。(个人觉得默认绑定中绑定到window对象时也可以归类为隐式绑定,因为在全局对象中,非严格模式且不考虑ES6的话,所有的全局变量都自动成为window的属性)。来看个具有迷惑性的例子(出自于原书)
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"
这是个具有迷惑性的例子,obj
对象有函数 foo
这个方法,后面又在全局作用域中,bar
引用了这个方法,最后再调用 bar
。我们只需要关注函数最后被调用的位置,它是在全局作用域中被单独调用的,所以还是为默认绑定,指向 window
显式绑定
显示绑定就比较简单了,用 call,apply,bind方法,都会绑定函数中的this到传入的参数对象中
new 绑定
所谓new绑定就是在用构造函数new一个对象的时候,其中的this指向生成的对象。这就完了嘛?还没有哦。
箭头函数,词法作用域中的this
简单的说,箭头函数中的this,会绑定到函数外(也就是上一层作用域中的this),函数外的this指向哪,箭头函数中的this就指向哪。(代码出自于原书)
function foo() {
// 返回一个箭头函数
return (a) => {
//this 继承自 foo()
console.log( this.a );
};
}
var obj1 = {
a:2
};
var obj2 = {
a:3
};
var bar = foo.call( obj1 );
/*
* foo先绑定this到obj1对象上,所以foo内的this指向obj1,
* 返回的箭头函数根据词法作用域规则,继承了外部foo的this,
* 所以箭头函数中的this指向obj1
*/
bar.call( obj2 ); // 2, 不是 3 !
/*
* 箭头函数中this一但绑定,不可更改
* 此时还是指向obj1
* 所以输出的是 obj1.a => 2
*/
绑定的优先级
new > 显示 > 隐式 > 默认
上述知识来自《你不知道的JavaScript(上卷)》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。