写在前面上一篇博客我们知道词法作用域是由变量书写的位置决定的,那this又是在哪里确定的呢?如何能够精准的判断this的指向?这篇博客会逐条阐述书中有这样几句话:this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this 就是记录的其中一个属性,会在函数执行的过程中用到。关于执行上下文,可以参考《javascript高级程序设计》笔记:内存与执行环境一、 绑定规则1.1 默认绑定最常用的函数调用类型——独立函数调用,使用的即为默认绑定规则,在非strict mode下,this指向全局对象function foo1() { console.log(this.a);}var a = 10;foo1(); // 10// 即使函数嵌套比较深function foo2() { foo1();}function foo3() { foo2();}foo3();当然,我们实际使用中,难以判别的并不是直接型的默认绑定模式,而是隐式绑定丢失型的默认绑定(下面会着重说明)1.2 隐式绑定【重点】调用的位置是否有上下文对象,或者说被某个对象拥有或包含// 基本形式function foo() { console.log(this.a);}var obj = { a: 10, foo };obj.foo(); // 10隐式绑定中的几个雷区:1. 多个对象嵌套引用时,只有最后一层在调用位置中起作用function foo() { console.log(a);}var obj2 = { a: 42, foo };var obj1 = { a: 10, obj2 };obj1.obj2.foo(); // 422.【隐式丢失】当调用函数被重新赋值为新变量,调用新变量时this指向会有不同// 共用部分function foo(){ console.log(this.a);}var obj = { a: 10, foo };var a = ‘opps, global’;// 直接赋值var bar = obj.foo;bar(); // ‘oops, global’// 回调间接赋值1function doFoo(fn) { fn();}doFoo(obj.foo); // ‘oops, global’ 相当于间接赋值// 回调间接赋值2setTimeout(obj.foo, 100); // ‘oops, global’ 内置的setTimeout也相当于间接赋值经典综合案例:var length = 10;function fn(){ console.log(this.length);}var obj = { length: 5, method: function (fn) { fn(); arguments0; }};obj.method(fn, 123);分析:fn()为函数fn的引用,默认绑定,指向全局;arguments0;相当于下面的引用,数据隐式绑定,绑定对象为arguments,其属性length值为参数数量2arguments: { ‘0’: function fn(){ console.log(this.length); }}答案:10 21.3 显式绑定call()/apply()/bind()能够显式修改this指向通过上述方法调用的方式为显示绑定,它们第一个参数是一个对象,在调用函数时,绑定在this中。关于三者的基本用法和说明在之前博客《javascript高级程序设计》函数调用模式 & this深度理解中已作说明,在此不做唠述两点注意:1. 通过显式绑定的不能再修改它的this指向function foo() { console.log(this.a);}var obj = { a: 2 };var bar = function() { foo.call(obj);}bar(); // 2setTimeout(bar, 200); // 2bar.call(window); // 22. 将null/undefined作为第一个参数时,调用会忽略这些值,采用默认绑定规则function foo() { console.log(this.a);}var a = 2;foo.call(null); // 21.4 new绑定使用关键字new执行函数,当函数无返回值或返回值非对象时,this指向为实例对象new关键字执行函数流程:创建一个全新的对象这个新对象会被执行[[prototype]]连接这个新对象会绑定到函数调用的this上如果函数没有返回其他对象,所执行函数会自动返回这个新的对象须知:构造函数与普通函数无异,作为区分,我们一般讲通过new调用的函数称为构造函数,并大写第一个单词。所有函数均可由关键字new调用function foo(a) { this.a = a;}var bar = new foo(2);console.log(bar.a); // 2二、优先级&判断规则2.1 优先级new绑定 –> 显式绑定 –> 隐式绑定 –> 默认绑定2.2. 判断规则【重点】【new绑定】函数是否在new中调用?如果是,this绑定的是新创建的对象【显式绑定】函数是否在call/aplly/bind中调用?如果是,this绑定的是指定对象【隐式绑定】函数是否在某个上下文中调用?如果是,this绑定到那个上下文对象【默认绑定】如果都不是,this绑定严格模式下为undefined,非严格模式下为全局对象三、箭头函数中的thisES6中箭头函数不使用上面this的四种标准规格,而是根据外层(函数或者全局)作用域来决定this指向下面是一个普通函数和箭头函数的对比:function foo1() { setTimeout(() => { console.log(this.a) }, 100)}function foo2() { setTimeout(function() { console.log(this.a) }, 100)}var a = 10;var obj = { a: 2 };foo1.call(obj); // 2 箭头函数this指向外层(obj)foo2.call(obj); // 10 隐式丢失,默认绑定【利用闭包】理解箭头函数中的this:// 上例中的箭头函数相当于function foo1() { var self = this; setTimeout(function() { console.log(self.a) }, 100)}上一篇:《你不知道的javascript》笔记_作用域与闭包