先提早说明一个观点,实际上,this既不指向函数自身,也不止像函数的词法做用域 ,此外还有一个重点:this跟函数在哪里定义无关,决定this的是函数在哪里调用,它是一种在函数调用时发生的绑定。javascript
this的四种绑定规则分别为:默认绑定、隐式绑定、显式绑定、new绑定。java
他们中的优先级为 new绑定 > 显式绑定 > 阴式绑定 > 默认绑定app
即当没有任何其余绑定规则时默认会生效的规则。最常使用的是当函数被单独定义以及调用时,它默认绑定全局变量函数
function fun() {
console.log( this.x );
}
var x = '我是一个全局变量';
fun(); //‘我是一个全局变量’ this此时指向window
复制代码
但咱们须要注意,当咱们在严格模式下时,全局对象是没法用默认绑定的,会抛出错误post
function fun() {
"use strict";
console.log( this.x );
}
var x = '我是严格模式下的全局变量';
fun(); //Uncaught TypeError:Cannot read property 'x' of underfined
复制代码
若是函数的调用是在某个对象上触发,那么这个调用位置存在一个上下文对象来绑定this。ui
function fun() {
console.log( this.a );
}
var a = '我是一个全局变量';
var obj = {
a: '我是obj对象的a属性',
fun: fun
};
obj.fun(); //'我是obj对象的a属性'
复制代码
这里obj对象内的fun函数被当作引用属性,当调用它时实际上的流程为:this
经过obj对象会去到fun 属性 --> 根据引用关系找到 fun 函数 --> 调用fun函数spa
因此在此时调用fun函数是,this 被隐式绑定到了obj上下文上,此时的 this.a 也就被解析为了 obj.a。code
有时咱们调用一个函数时会使用多层调用的状况,此时咱们经过分析一个实际的例子来演示对象
function fun() {
console.log( this.a );
}
var a = '我是全局变量!';
var obj1 = {
a: '我是obj1的属性a',
fun: fun
};
var obj2 = {
a: '我是obj2的属性a',
obj1:obj1
}
obj2.obj1.fun(); // '我是obj1的属性a'
复制代码
此时这个函数的调用过程为:
访问obj2.obj1 --> 经过引用获取 obj1对象 --> 访问obj1.fun --> 引用获取fun函数 --> 调用fun函数
此时隐式绑定实际上只是获取最后一层调用的上下文对象,把this绑定上去。
咱们可能会在平常开发中有这样的场景
function fun() {
console.log( this.a );
}
var a = '我是全局变量';
var obj = {
a: '我是obj对象的a属性',
fun: fun
};
//此时再赋予这个函数一个别名
var bar = obj.fun;
bar(); //'我是全局变量'
复制代码
结果并非'我是obj对象的a属性',这是由于obj.fun 本质上是引用属性,因此下面两行本质上是没有区别的
var bar = obj.fun;
var bar = fun
因此调用时本质上是 bar 找到了 fun函数自己,进行调用时是在全局环境下调用的,因此不会输出obj内定义的属性,因此此时就是默认绑定
当咱们使用回调函数时也会存在隐式绑定丢失的状况
function fun() {
console.log( this.a );
}
var a = '我是全局变量';
var obj = {
a: '我是obj的属性a',
fun: fun
};
setTimeout( obj.fun, 1000); //我是全局变量
复制代码
发证这个结果的缘由也是一样的道理,传入 setTimeout的obj.fun 是引用属性, 因此调用顺序为:
setTimeout --> 获取obj.fun属性 --> 经过引用属性的到fun函数 --> 调用执行fun函数。
因此在调用执行的时候并无在obj的上下文中,因此仍为默认绑定。
相较于隐式绑定而言,咱们有时须要手动来为函数调用指明函数执行的上下文,此时据须要使用显式绑定
显式绑定主要是使用三个方法 call,apply以及bind方法来实现,这部份内容我在另外一篇文章中有详细解析
当咱们使用new 来修饰并调用函数时,他会先查看这个函数是否有其余的返回对象,若是没有就自动返回本身做为一个新对象。
function fun(a) {
this.a = a;
console.log( this.a );
}
var a = '我是全局变量';
var fun1 = new fun('我是fun1的参数');
var fun2 = new fun('我是fun2的参数');
console.log(fun1.a,fun2.a); // '我是fun1的参数','我是fun2的参数'
复制代码
由于当咱们使用new时他会生成一个全新的对象来绑定this。
箭头函数做为ES6提出的新概念,它是一种对以前this复杂操做的一种极好的解决方案,他的this绑定取决于外层(函数或者全局)做用域。
注意,是只取决于外层做用域,也就是咱们对箭头函数使用显示绑定来强制修改上下文绑定也是无效的。
《你不知道的JavaScript(上卷)》——Kyle Simpson 著
《ES6标准入门(第三版)》 —— 阮一峰 著