在《javascript 之執行環境-08》文中說到,當JavaScript代碼執行一段可執行代碼時,會建立對應的執行上下文(execution context)。對於每一個執行上下文,都有三個重要屬性:javascript
JavaScript
中的this
跟其餘語言有些不同,好比Java .net
語言中的this
是在代碼的執行階段是不可變的,而JavaScript的this
是在調用階段進行綁定。也由於這一性質給了this
很大的靈活性,即當函數在不一樣的調用方式下均可能會致使this的值不一樣;java
this 對象是在運行時基於函數的執行環境綁定的,跟函數的調用位置有關而不是聲明的位置;能夠理解爲this是在函數調用階段綁定,也就是執行上下文建立的階段進行賦值,保存在變量對象中;緩存
1 function createPerson(){ 2 return new person(); 3 } 4 function person() { 5 this.name = "Joel"; 6 this.say=function(){ 7 console.log(p) 8 console.log('hello' + this.name) 9 } 10 } 11 var p = new person(); 12 p.say(); 13 console.log(p.name);//Joel 14 console.log('開始') 15 var t=createPerson(); 16 t.say();
無論是直接new 仍是經過function createPerson 函數返回的對象,this 都是指向了新建立出來的對象;閉包
1 //call() apply() bind() 顯示綁定 2 window.color = "red"; 3 var Obj = { color: "blue" }; 4 function sayColor() { 5 console.log(this.color); 6 } 7 sayColor();// red this window 8 sayColor.call(this);//red 9 sayColor.call(window);//red 10 sayColor.call(Obj);// blue 把this指針改成對象Obj 11 sayColor.apply(Obj);//blue 把this指針改成對象Obj 12 sayColor.bind(Obj)();//blue 把this指針改成對象Obj
若是你把null/undefined做爲this的綁定對象傳入call/apply/bind,這些值在調用時會被忽略,實際用的是默認的綁定規則;app
1 function foo() { 2 console.log(this.a) 3 } 4 var a=2; 5 foo.call(null);//2 6 foo.call(undefined);//2
以對象的方法形式調用,this指向當前這個對象函數
1 function foo(){ 2 console.log(this)//{a: 2, name: "Joel", foo: ƒ} 3 console.log(this.a)//2 4 } 5 var obj={ 6 a:2, 7 name:'Joel', 8 foo:foo 9 }; 10 obj.foo();
這時this指向當前obj對象,可是若是換種寫法會形成this 丟失問題。oop
1 function foo(){ 2 console.log(this)//{a: 2, name: "Joel", foo: ƒ} 3 console.log(this.a)//2 4 } 5 var obj={ 6 a:2, 7 name:'Joel', 8 foo:foo 9 }; 10 obj.foo(); 11 //this 丟失的問題 12 var t= obj.foo; 13 t(); //window undefined
變量t此時保存的是函數的引用跟obj已經沒有關係,因此此時this指向window。this
1 function foo(){ 2 console.log(this)//window 3 console.log(this.a)//Joel 4 } 5 var a='Joel'; 6 foo(); 7 8 //嚴格模式 9 function fo(){ 10 'use strict' //嚴格模式 11 console.log(this)//undefined 12 console.log(this.b)//報錯 Cannot read property 'b' of undefined 13 } 14 var b='Joel'; 15 fo();
以上是基本的this綁定規則,其中new、顯示綁定很容易判斷,其中比較容易錯的是容易把默認綁定誤認爲是隱士綁定 如匿名函數、閉包、函數當作參數等;spa
獨立調用:this 指向window.net
1 var name='Joel',age=12; 2 function say(){ 3 function say1(){ 4 console.log(this.name);//window 5 function say2(){ 6 name+='-l' 7 console.log(this.name);//window 8 } 9 say2() 10 } 11 say1(); 12 } 13 say(); 14 15 //匿名函數 16 (function(){ 17 console.log(this.name) 18 })()
function 當作參數傳遞其實跟獨立調用同樣的原理
1 function foo() { 2 console.log(this)//window 3 console.log(this.a)//oops global 4 } 5 function doFoo(fn) { 6 console.log(this);//window 7 fn();//相似與 foo() 8 } 9 var obj = { 10 a: 2, 11 foo: foo 12 } 13 var a = 'oops global'; 14 doFoo(obj.foo);
同理setTimeout 也是同樣
1 var obj = { 2 a: 2, 3 foo: function() { 4 console.log(this); 5 }, 6 foo2: function() { 7 console.log(this); //this 指向 obj 8 setTimeout(this.foo, 1000); // this 指向 window 9 } 10 } 11 var a = 'oops global'; 12 obj.foo2();
閉包中的this
1 var name = "Joel"; 2 var obj = { 3 name: "My object", 4 getName: function() { 5 // var that = this; // 將getNameFunc()的this保存在that變量中 6 return function() { 7 return this.name; 8 }; 9 } 10 } 11 console.log(obj.getName()()); // "Joel"
這裏的雖然是對象的方法形式調用obj.getName(),在getName中的this是指向obj,可是返回的匿名函數中的this 爲何是window呢?
把最後的一句拆成兩個步驟執行:
1 var t=obj.getName(); 2 t();
是否是有點像在獨立調用呢?若是須要訪問obj中的name,只須要把this對象緩存起來,在匿名函數中訪問便可,把var that=this;去掉註釋便可;
1 var length = 5; 2 var obj = { 3 foo: function (fn) { 4 console.log(this.length); // this => obj 5 fn(); // this => window 6 arguments[0](); // this => arguments 7 var bar = arguments[0]; 8 bar(); // this => window 9 }, 10 length: 10 11 } 12 var fn = function () { 13 console.log(this.length); 14 } 15 obj.foo(fn); 16 //10, 5, 1, 5