2.this的綁定規則 1.預設綁定 在代碼中,foo()函數不帶任何修飾的引用進行調用的,那麼只能使用預設綁定。 2.隱式綁定 調用位置使用obj上下文來引用函數foo2,故可以說函數被調用時obj對象“擁有”或“包含”該函數foo2()。那麼foo2函數被調用時,確實加上了對obj的引用。當函數 ...
2.this的綁定規則
1.預設綁定
1 function foo( )
2 {
3 console.log(this.a);
4 }
5 var a=1;
6 foo(); //1
在代碼中,foo()函數不帶任何修飾的引用進行調用的,那麼只能使用預設綁定。
2.隱式綁定
1 function foo1()
2 {
3 console.log( this.a );
4 }
5 var obj = {
6 a: 1,
7 foo: foo1
8 };
9 obj.foo(); //1
調用位置使用obj上下文來引用函數foo2,故可以說函數被調用時obj對象“擁有”或“包含”該函數foo2()。
那麼foo2函數被調用時,確實加上了對obj的引用。當函數引用有上下文對象時,
隱式綁定規則會把函數調用中的this綁定到這個上下文對象。
故上文中的this.a等同於obj.a。
PS1:對象屬性引用鏈中只有上一層或者最後一層在調用位置中起作用。
1 function foo2()
2 {
3 console.log( this.a );
4 }
5 var obj2 = {
6 a: 2,
7 foo: foo2
8 };
9 var obj1 = {
10 a: 3,
11 obj2: obj2
12 };
13 obj1.obj2.foo(); //2
距離this最近的對象上下文時obj2,故this.a等同與obj2.a,同時等同於obj1.obj2.a。
PS2:隱式丟失
1 function foo3()
2 {
3 console.log( this.a );
4 }
5 var obj3 = {
6 a: 33,
7 foo: foo3
8 };
9 var bar=obj3.foo;
10 var a=3;
11 bar(); //3
這裡的要點就是關註var bar=obj3.foo;
雖然bar只是obj3.foo的一個引用,但bar實際引用的時foo3()函數本身。
因此此時的bar()其實是一個不帶任何修飾的函數調用,所以應用預設綁定。
PS2:隱式丟失(發生在函數調用時)。
1 function foo4( )
2 {
3 console.log(this.b);
4 }
5 function doFOO( fn )
6 {
7 var b=44;
8 fn();
9 }
10 var obj4={
11 b:10,
12 foo:foo4
13 };
14 var b=4;
15 doFOO(obj4.foo); //4
參數傳遞其實就是一個隱式賦值,故傳入函數也會被隱式賦值。
所以結果與上面例子一致。
同理,這裡傳入的是自定義函數。即使傳入的是內置函數,結果也是一樣的。
PS3:如上所見,回調函數丟失this綁定是很常見的。
與此同時,另一種丟失情況更加出人意料:調用回調函數可能會修改this。
在一些流行的js庫中,事件處理器經常會把回調函數的this綁定到DOM元素上。
3.顯式綁定
顯式綁定,即是通過call,apply等方法來強制綁定。
call與apply的第一個參數都是thisObj,表示this的指向。第二個參數,call是輸入單個參數,apply是輸入參數數組。多個參數時,後者性能更優。
1 function foo()
2 {
3 console.log( this.a );
4 }
5 var obj = {
6 a: 3
7 };
8 foo.call( obj ); //2 函數沒有參數輸入時,call只需要一個參數thisObj輸入。
通過foo.call(),可以在調用foo時強制把它的this綁定到obj上。
PS1:“裝箱”
如果thisObj參數傳入的是一個原始值(簡單數據類型),這個原始值會被轉換成它的對象形式(如new String(..),new Boolean(..),或者new Number(..))。這通常稱為“裝箱”。
PS2:解決之前提出的丟失綁定問題。
1.硬綁定(顯式綁定的一個變種)
1 function foo2()
2 {
3 console.log( this.a );
4 }
5 var obj2 = {
6 a: 3
7 };
8 var bar = function()
9 {
10 foo2.call( obj );
11 };
12 bar(); //3
13 setTimeout( bar, 100 ); //3
14
15 //切記,硬綁定的bar不可能在修改它的this。
16 bar.call( window ); //3
硬綁定的典型應用場景就是創建一個包裹函數,負責接收參數並返回值;
另一個使用方法就是創建一個可以重覆應用的輔助函數:
1 function foo3( something )
2 {
3 console.log( this.d, something );
4 return this.d + something;
5 }
6 function bind( fn, obj )
7 {
8 return function()
9 {
10 return fn.apply( obj, arguments );
11 };
12 }
13 var obj3 = {
14 d: 2
15 };
16 var bar3 = bind( foo3, obj3 );
17 var e = bar3( 3 ); //2 3
18 console.log( e ); //5
由於硬綁定是一個非常常用的模式,故ES5提供了一個內置方法bind,和上述用法類似。
1 function foo4( something )
2 {
3 console.log( this.a4, something );
4 return this.a4 + something;
5 }
6 var obj4 = {
7 a4: 2
8 };
9 var bar4 = foo4.bind( obj4 );
10 var b4 = bar4( 5 ); //2 5
11 console.log( b4 ); //7
bind(..)會返回一個硬編碼的新函數(切記,新函數),這會將指定的參數設置為this的上下文並調用原始函數。
2.API調用的“上下文”
第三方庫的許多函數,以及JS語言和宿主環境(如瀏覽器環境)中許多內置函數,都提供了一個可選參數,通常稱為“上下文(context)。
其作用與bind(..)類似,確保回調函數使用指定的this。
1 function foo5( el )
2 {
3 console.log( el, this.id );
4 }
5 var obj5 = {
6 id: "awesome"
7 };
8
9 //調用foo5(..)函數是將this綁定到obj。
10 [ "1", 2, 3 ].forEach( foo5, obj5 ); //console.log輸出數字和字元串時,數字在前,字元串會有雙引號;反之則沒有。
11 // 1 awesome; 2 awesome; 3 awesome
4.new綁定
在此先糾正js中new與其他語言中new的區別。
在其它面向類語言中,”構造函數“是類中的一些特殊方法,使用new 初始化類時調用類中的構造函數。
在JS中,構造函數只是一些使用new操作符時被調用的函數。它們不屬於某個類,也不會實例化一個類。
JS中使用new來調用函數,或者說發生構造函數調用時,會自動執行以下操作。
1.創建(或者說構造)一個全新的對象。
2.這個新對象會被執行[[Prototype]]鏈接。
3.這個新對象會綁定到函數調用的this。
4.如果函數沒有返回其他對象,那麼new表達式中的函數調用會自動返回這個新對象。
1 function foo( a )
2 {
3 this.a = a;
4 }
5 var bar = new foo( 4 );
6 console.log( bar.a );
7
8 //使用new來調用foo(..)時,會構建一個新對象並將它綁定到foo(..)調用的this上,並將該對象返回給bar。