讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議90:預防this誤用的策略 >

建議90:預防this誤用的策略

this雖然用法靈活,但容易出錯。好的使用習慣可以很好地預防this誤用。事實上,this的複雜性在很大程度上取決於用戶的使用方式。由於this指代靈活,如果把它放在複雜的應用環境中,它也會變得很不確定,因此必須牢記:確保在同一作用域中操作this,避免把包含有this的全局函數動態用於局部作用域中,同時應避免在不同作用域的對象之間相互引用包含this的方法。

如果把this作為參數值來調用函數,那麼可以避免了this多變的問題,因為this始終與當前對像保持一致。例如,下面的做法是錯誤的,因為this在這裡始終指向Window對象,而不是所期望的當前按鈕對像:


<input type="button"onclick="f"/>

<input type="button"onclick="f"/>

<input type="button"onclick="f"/>

<script language="javascript"type="text/javascript">

function f{

alert(this.value);

}

</script>


但是,如果把this作為參數值進行傳遞,那麼它就會代表當前對像:


<input type="button"onclick="f(this)"/>

<input type="button"onclick="f(this)"/>

<input type="button"onclick="f(this)"/>

<script language="javascript"type="text/javascript">

function f(o){

alert(o.value);

}

</script>


如果要確保構造函數的方法在初始化之後其中所包含的this指針不再發生變化,一個很簡單的方法就是:在構造函數中把this指針存儲在私有變量中,然後在方法中使用私有變量來引用this指針,這樣所引用的對象始終都是初始化的實例對象,而不會在類型繼承中發生變化。例如:


function Base{//基類

var_this=this;//存儲初始化時對象的引用指針

this.m=function{

return_this;//初始化時對象的引用指針

};

this.name="Base";

}

function F{//子類

this.name="F";

}

F.prototype=new Base;//繼承基類

var f=new F;

var n=f.m;

alert(n.name);//this始終指向原型對象,而不再是子類的實例對像


對於對像直接量來說,如果希望使用this代表當前對像直接量,則可以直接調用對像直接量的名稱,而不用this關鍵字。

當然,作為一個動態指針,this也是可以被轉換為靜態指針的,實現的方法是主要利用Function對象的call或apply方法。這兩個方法都可以強制指定this的指代對象。例如,為Function對像擴展一個原型方法pointTo,具體代碼如下:


//把this轉換為靜態指針,參數o表示預設置this所指代的對象,返回一個閉包涵數

Function.prototype.pointTo=function(o){

var_this=this;//存儲當前函數對像

return function{//一個閉包涵數

return_this.apply(o,arguments);/*執行當前函數並把當前函數的作用域強制設置為指定對像*/

}

}


這個方法將調用當前函數,並在由參數指定的對象上執行,從而把this綁定到該對像上。

然後應用這個函數擴展方法,以實現強制指定對像o的方法b中的this始終指向定義對像o。


var o={

name:"this=o"

}

o.b=(function{

return this;

}).pointTo(o);//把this綁定到對像o身上

var o1={

name:"this=o1",

b:o.b

}

var a=o1.b;

alert(a.name);//字符串"this=o",說明this的值沒有發生變化


還可以擴展new運算符的替代方法,從而間接使用自定義函數實例化類。


//把構造函數轉換為實例對象,參數f表示構造函數,返回構造函數f的實例對像

function instanceFrom(f){

var a=.slice.call(arguments,1);//獲取構造函數的參數

f.prototype.constructor=f;//手工設置構造函數的原型構造器

f.apply(f.prototype,a);//在原型對像上強制執行構造函數

return f.prototype;//返回原型對像

}


例如,下面的示例演示了如何使用這個自定義的實例化類方法把一個簡單的構造函數轉換為具體的實例對象。


function F{

this.name="F";

}

var f=instanceFrom(F);

alert(f.name);


通過這個示例也進一步說明,call和apply方法具有強大的功能,它不僅能夠執行普通函數,也能夠實例化構造函數,具備new運算符的運算功能。