1. 程式人生 > >JavaScript OOP(二):this關鍵字以及call、apply、bind

JavaScript OOP(二):this關鍵字以及call、apply、bind

col als == ole 構造 prototype logs rip .com

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