JavaScript OOP(二):this關鍵字以及call、apply、bind
JavaScript的this關鍵字非常靈活!
this 返回的總是對象;即返回屬性或方法“當前”所在的對象1 var o1={ 2 name:‘apple‘, 3 age:100, 4 msg:function(){ 5 return ‘顯示name和age信息:‘+‘name: ‘+this.name+‘, age: ‘+this.age; 6 } 7 }; 8 //針對msg中的this進行研究: 9 console.log(o1.msg());//this 指向當前對象o1 10 var o2={ 11 name:‘blue‘,12 age:1000 13 }; 14 o2.msg=o1.msg; 15 console.log(o2.msg());//this 指向當前對象o2
當o1.msg()時,this指向o1;而o2.msg()時,this指向o2。也就是this指向的是“當前”環境運行時所在的對象。
運行結果:
將函數提出來,更形象的表示:
1 console.log(‘---‘); 2 function f(){ 3 console.log(this.name1); 4 } 5 var o3={ 6 name1:‘alice‘, 7 info:f 8 };9 var o4={ 10 name1:‘boy‘, 11 info:f 12 }; 13 f();//undefined 14 o3.info();//alice 15 o4.info();//boy
f():this指向頂層對象window;o3.info():this指向的是o3;o4.info():this指向的是o4。即this總是指向“當前”運行時所在的對象。
運行結果:
如果我們在全局環境中,將對象中的方法賦值給變量。以本文最上面代碼o1.msg示例:
1 var name=‘abc‘; 2 var age=1; 3 var test1=o1.msg; 4 console.log(test1());//此時this指向頂層對象window
此時test1():this指向的是window
運行結果:
由上面的這些例子,我們可以“粗略”的認為:每個函數中都存在著this,它總是指向當前運行環境的對象。
全局環境下的this:指向頂層對象window
1 function test2(){ 2 if(this === window){ 3 console.log(‘此時this 指向頂層對象window‘); 4 } 5 } 6 test2();
運行結果:
構造函數中的this:指向實例化的對象1 function Test3(num){ 2 this.num=num; 3 } 4 var t3=new Test3(100); 5 console.log(t3.num);//this 指向t3 6 7 Test3.prototype.m=function (){//所有由Test3構造函數生成的實例化對象都共享m方法 8 return this.num; 9 }; 10 console.log(t3.m());//this 指向t3
運行結果:
註意下面這種情況:
1 var o5={ 2 name:‘application‘, 3 send:function(){ 4 console.log(this 5 ); 6 } 7 }; 8 o5.send();//o5 9 (o5.send=o5.send)();//window 10 /** 11 * 相當於 12 * (o5.send=function(){ 13 * console.log(this); 14 * }) 15 */ 16 (false||o5.send)();//window 17 /** 18 * 相當於 19 * (false || function(){ 20 * console.log(this); 21 * }) 22 */ 23 (1,o5.send)();//window 24 /** 25 * 相當於 26 * (1,function(){ 27 * console.log(this); 28 * }) 29 */
即:除非直接使用o5.send(),結果返回當前對象;否則均返回頂層對象window
運行結果:
如果方法位於多層對象的內部,那麽this指向當前對象層,不會繼承更上面的層:
1 var o6={ 2 name:‘cat‘, 3 f:{ 4 f1:function (){ 5 console.log(this.name); 6 console.log(this==o6.f);//其實this指向的是o6.f 7 } 8 } 9 }; 10 o6.f.f1();//undefined 11 //因為此時this指向的是f
上面代碼中o6對象中f屬性對應的值,是一個對象。該對象裏面又存在著一個函數,此時函數裏面的this指向o6.f,而不是o6
運行結果:
如果想達到預期的效果:
1 var o6={ 2 name:‘cat‘, 3 f:{ 4 f1:function(){ 5 console.log(this.name); 6 }, 7 name:‘cat‘ 8 } 9 } 10 o6.f.f1();//cat
繼續進行變通:
1 //將o6.f.f1賦值給變量 2 var v=o6.f.f1; 3 /** 4 * 相當於 5 * var v=function (){ 6 * console.log(this.name); 7 * } 8 */ 9 v();//this指向的對象又指向了頂層對象window 10 //將o6.f賦值給變量 11 var v1=o6.f; 12 v1.f1();//此時返回的結果為‘cat‘ 13 /** 14 * 相當於 15 * var v1={ 16 * f1:function(){ 17 * console.log(this.name);}, 18 * name:‘cat‘ 19 * }; 20 */
同時應盡量避免在函數中使用多層this:
1 //盡量避免在函數中使用多層this 2 var o7={ 3 name:‘apple‘, 4 f:function(){ 5 console.log(this);//this指向當前運行環境對象,即o7 6 var f1=function(){ 7 console.log(this);//this指向頂層對象,即window 8 }();//IIFE;這是立即調用的函數表達式 9 } 10 }; 11 o7.f();
運行結果:
為了讓f1中的this也指向該對象:添加一個臨時變量作為輔助:固定this。
1 var o8={ 2 name:‘apple‘, 3 f:function(){ 4 console.log(this);//this指向o8 5 var that=this;//使用變量固定this 6 var f1=function(){ 7 console.log(that);//此時that指向o8 8 }(); 9 } 10 }; 11 o8.f();
運行結果:
當然如果采用嚴格模式,那麽函數內部this不能指向頂層對象window!
由於this的靈活性,有時候難以把控。所以有三種綁定this的方法: call,apply,bind 三種方法中如果第一個參數為空、null\undefined,那麽默認指向全局對象window
call():調用函數,指定this指向的對象;第一個參數是this指向的對象,第二個、第三個等是函數調用的參數
1 var o9={ 2 name:‘orange‘ 3 }; 4 function test4(){ 5 console.log(this); 6 } 7 test4(); 8 test4.call(o9);//指定this的指向 9 //call方法中如果參數為空、null\undefined,那麽默認指向全局對象window 10 test4(null); 11 test4(undefined); 12 //call方法第一個參數是this指向的對象,後面的參數是函數調用時用到的參數
運行結果:
call的一個應用:調用對象原生方法,即使該方法被覆蓋
1 var o10={}; 2 console.log(o10.hasOwnProperty(‘toString‘)); 3 o10.hasOwnProperty=function(){ 4 return true; 5 }; 6 console.log(o10.hasOwnProperty(‘toString‘)); 7 //this指向o10,這樣方法被覆蓋,依然能夠調用對象的原生方法 8 console.log(Object.prototype.hasOwnProperty.call(o10,‘toString‘));
運行結果:
apply():作用與call()類似,第一個參數也是this指向的對象;不同的是函數調用的參數以數組形式傳入
1 //apply與call作用類似,只是apply傳入的參數是以數組形式傳入 2 //同理,第一個參數是this指向的對象,空或null或undefined,默認是全局對象window 3 function test6(a,b){ 4 console.log(a+b); 5 } 6 test6.call(null,1,10);//test6(1,10) 7 test6.apply(null,[10,100]);//test6(10,100) 8 var arr=[1,2,3,4,5]; 9 console.log(Math.max.apply(null,arr));//this指向window,arr調用Math.max方法 10 console.log(Array.prototype.slice.apply({0:1,1:100,length:2}));//類似數組的對象調用方法變為數組
運行結果:
bind():將this綁定到某個對象,返回一個新函數(相較於call,apply的函數立即執行)
1 //bind將this綁定到某個對象,並返回一個函數 2 var o10={ 3 fruit:‘apple‘, 4 f:function(){ 5 console.log(this.fruit); 6 } 7 }; 8 o10.f();//正常取值 9 console.log(‘---‘); 10 var a=o10; 11 a.f();//這樣也能正確取值 12 var b=o10.f; 13 b();//這樣取值就不行了;undefined 14 /** 15 * 此時this指向全局對象window,上面相當於 16 * var b=function (){ 17 * console.log(this.fruit);//this指向window 18 * } 19 */ 20 21 //綁定this指向o10 22 var c=o10.f.bind(o10); 23 c();//此時能夠正確取值
運行結果:
bind還能綁定函數的參數:
1 //bind還可以綁定函數的參數 2 var o10={ 3 name:‘apple‘, 4 age:100 5 }; 6 function test7(x,y){ 7 console.log(x,this.name,y,this.age) ; 8 } 9 var t5= test7.bind(o10,‘姓名信息: ‘);//綁定第一個參數,返回t5這個新函數 10 t5(‘ 年齡信息: ‘);
運行結果:
參考:阮一峰JavaScript標準參考教程
JavaScript OOP(二):this關鍵字以及call、apply、bind