瀏覽器全局環境下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