浏览器全局环境下this指向window对象javascript
console.log(this); //Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
nodejs环境下this指向global对象html
非严格模式this指向window,严格模式除箭头函数外指向undefinejava
//a.js 非严格模式 function fun1(){console.log(this)} fun1();//window (function(){console.log(this)})() //window var fun2 = ()=>{console.log(this)} fun2();//window //b.js 严格模式 'use strict'; function fun1(){console.log(this)} fun1();//undefined (function(){console.log(this)})() //undefined var fun2 = ()=>{console.log(this)} fun2();//window
function做为对象属性时,this指针指向对象的实例node
var obj1 = { name: 'a', func1:function(){console.log(this.name);} } obj1.func1();//a //等同于 function func2(){console.log(this.name);} var obj2 = { name: 'b', funRef:func2 } obj2.funRef();
构造函数调用时,this指针指向函数的实例(function也是对象的一种)浏览器
function func3(c){ this.c = c; console.log(this.c);//c console.log(this);//func3 {c: "c"} } var finst3 = new func3('c');
同步回调函数中的this指针服从上面1-3点的规则闭包
function callback() { console.log(this.d);//undefined console.log(this);//window } function func4(cb){ cb.d = 'd'; cb(); } func4(callback);
关于JavaScript是单线程执行就很少提了,这里详细分析一下对于异步操做的回调函数中this的指向问题。
提到异步操做就不得不提Event Loop,其详细介绍能够参见阮一峰大神的这篇。app
简单来讲就是:异步
注意:这里就有问题了,当主线程调用异步操做的回调函数时,是以什么环境和做用域来执行的呢?async
答案是: 以全局环境和执行回调函数时的做用域链来执行回调函数。
函数
参考如下代码:
setTimeout(function(){ console.log(this);//1秒后输出window }, 1000);
//再看 var asyncFun1 = { propA:"a", synFun:function() { console.log(this.propA); console.log(this); }, asynFun: function(){setTimeout(this.synFun, 1000)} } asyncFun1.synFun();//输出依次是 //a //{propA: "a", synFun: ƒ, asynFun: ƒ} asyncFun1.asynFun(); //undefined //window
//再再看 var asyncFun2={ propB:"b", asynFun:function(){ var c = 'string c'; setTimeout(function(){ console.log(c); },1000); } } asyncFun2.asynFun();//输出'string c'
注意为何会输出'string c': 虽然回调函数运行是在全局环境(this指向window),可是在其做用域链中是可以访问到变量c的。
常见的异步操做包括:
关于如何在异步操做的回调函数中使用正确的this指针,往下看。
经过上面的分析咱们知道在异步回调函数中this会指向window,那么咱们须要怎么作才能让this指向咱们指定的对象呢?
var fun1 = (x)=>{return x + 1;} var fun2 = x=>{return x+1;} var fun3 = x=>x+1; //返回值都是2 fun1(1);//2 fun2(1);//2 fun3(1);//2
那么箭头函数体内的this指针将符合做用域链原则,指向做用域链上最近的this
function fun4() { setTimeout(() => { console.log(this.id); }, 1000); } var id = 1; fun4.call({ id: 2 });//输出为2 fun4();//输出为1
分析以下:
接下来看看闭包中的this指针问题
//a.js function debounce(fn, delay) { var timer = null; return function() { var context = this; var args = arguments; //定义context和args,经过做用域链达到保存this指针和arguments的做用 clearTimeout(timer); timer = setTimeout(function() { //用apply保证回调函数的this指针不会被异步函数重置为window fn.apply(context, args); }, delay); } } //等同于 function debounce(fn, delay) { var timer = null; return function() { clearTimeout(timer); timer = setTimeout(()=> {fn.apply(this, arguments) }, delay); } } document.a1 = "2"; var obj1 = { a1:"1", fun1:function(){ console.log(this); console.log(this.a1); } } document.addEventListener('scroll', debounce(obj1.fun1, 2000)); //2秒后输出 //document //2
//b.js function debounce(fn, delay) { var timer = null; return function() { clearTimeout(timer); timer = setTimeout(()=> fn(), delay); } } window.a1 = "3"; document.a1 = "2"; var obj1 = { a1:"1", fun1:function(){ console.log(this); console.log(this.a1); } } document.addEventListener('scroll', debounce(obj1.fun1, 2000)); //2秒后输出 //window //3
关于这三个函数的对比和做用网上介绍不少,其主要做用为:
使用that替换函数内部this的指针指向为that。
function.call(that, arg1, arg2); function.apply(that, [arg1, arg2]); function.bind(that); //以下 var obj1 = { a1:"1", fun1:function(){console.log(this.a1)} } var obj2 = { a1:"2" } obj1.fun1();//1 obj1.fun1.call(obj2);//2 var funBind = obj1.fun1.bind(obj2); funBind();//2