JavaScript的self和this使用小結

一、self
  這個非常簡單。我們知道,打開任何一個網頁,瀏覽器會首先創建一個窗口,這個窗口就是一個window對象,也是js運行所依附的全局環境對象和全局作用域對象。self 指窗口本身,它返回的對象跟window對象是一模一樣的。也正因爲如此,window對象的常用方法和函數都可以用self代替window。舉個例子,常見的寫法如“self.close();”,把它放在<a>標記中:“<a href="javascript:self.close();">關閉窗口</a>”,單擊“關閉窗口”鏈接,當前頁面關閉。
二、this關鍵字
  在講this之前,看下面的一段代碼:
<body>
<script type="text/javascript">
function thisTest()
{
this.textValue = 'this的dom測試';
this.element= document.createElement('span');
this.element.innerHTML = this.textValue;
this.element.style.color = "blue";
this.element.style.cursor = "pointer";
this.element.attachEvent('onclick', this.ToString);
}

thisTest.prototype.RenderDom = function()
{
document.body.appendChild(this.element);
}

thisTest.prototype.ToString = function()
{
alert("單擊我:"+this.textValue);
};
var test= new thisTest();
test.RenderDom();
//test.ToString();
</script>
</body>
本來的目的是想在body中添加一個span元素,對於這個span元素,制定了它的字體顏色,懸浮在它上面的鼠標樣式和單擊觸發事件。問題就出現在它的單擊事件上(彈出"單擊我:undefined")。也許有人會說你丫傻呀,寫這麼多sb代碼還不就是爲了實現下面這個東東嗎?
<span style='cursor:pointer;color:blue;' onclick="alert(this.innerHTML)">this的dom測試</span>
1、this到底指什麼?
我們熟悉的c#有this關鍵字,它的主要作用就是指代當前對象實例(參數傳遞和索引器都要用到this)。在javascript中,this通常指向的是我們正在執行的函數本身,或者是指向該函數所屬的對象(運行時)。
2、常見使用方式
(1)、直接在dom元素中使用
<script type="text/javascript">
function thisTest(){
alert(this.value); // 彈出undefined, this在這裏指向??
}
</script>
<input id="btnTest" type="button" value="提交" onclick="thisTest()" />
分析:onclick事件直接調用thisTest函數,程序就會彈出undefined。因爲thisTest函數是在window對象中定義的,
所以thisTest的擁有者(作用域)是window,thisTest的this也是window。而window是沒有value屬性的,所以就報錯了。
b、正確的方式
<input id="btnTest" type="button" value="提交" />
<script type="text/javascript">
function thisTest(){
alert(this.value);
}
document.getElementById("btnTest").onclick=thisTest; //給button的onclick事件註冊一個函數
</script>
分析:在前面的示例中,thisTest函數定義在全局作用域(這裏就是window對象),所以this指代的是當前的window對象。而通過document.getElementById("btnTest").onclick=thisTest;這樣的形式,其實是將btnTest的onclick屬性設置爲thisTest函數的一個副本,在btnTest的onclick屬性的函數作用域內,this歸btnTest所有,this也就指向了btnTest。其實如果有多個dom元素要註冊該事件,我們可以利用不同的dom元素id,用下面的方式實現:
document.getElementById("domID").onclick=thisTest; //給button的onclick事件註冊一個函數。
因爲多個不同的HTML元素雖然創建了不同的函數副本,但每個副本的擁有者都是相對應的HTML元素,各自的this也都指向它們的擁有者,不會造成混亂。
爲了驗證上述說法,我們改進一下代碼,讓button直接彈出它們對應的觸發函數:
<input id="btnTest1" type="button" value="提交1" onclick="thisTest()" />
<input id="btnTest2" type="button" value="提交2" />

<script type="text/javascript">
function thisTest(){
this.value="提交中";
}
var btn=document.getElementById("btnTest1");
alert(btn.onclick); //第一個按鈕函數
var btnOther=document.getElementById("btnTest2");
btnOther.onclick=thisTest;
alert(btnOther.onclick); //第二個按鈕函數
</script>
其彈出的結果是:
//第一個按鈕
function onclick(){
thisTest()
}
//第二個按鈕
function thisTest(){
this.value="提交中";
}
從上面的結果你一定理解的更透徹了。
By the way,每新建一個函數的副本,程序就會爲這個函數副本分配一定的內存。而實際應用中,大多數函數並不一定會被調用,於是這部分內存就被白白浪費了。所以我們通常都這麼寫:
<input id="btnTest1" type="button" value="提交1" onclick="thisTest(this)" />
<input id="btnTest2" type="button" value="提交2" onclick="thisTest(this)" />
<input id="btnTest3" type="button" value="提交3" onclick="thisTest(this)" />
<input id="btnTest4" type="button" value="提交4" onclick="thisTest(this)" />
<script type="text/javascript">
function thisTest(obj){
alert(obj.value);
}
</script>
這是因爲我們使用了函數引用的方式,程序就只會給函數的本體分配內存,而引用只分配指針。這樣寫一個函數,調用的地方給它分配一個(指針)引用,這樣效率就高很多。當然,如果你覺得這樣註冊事件不能兼容多種瀏覽器,可以寫下面的註冊事件的通用腳本:
//js事件 添加 EventUtil.addEvent(dom元素,事件名稱,事件觸發的函數名) 移除EventUtil.removeEvent(dom元素,事件名稱,事件觸發的函數名)
var EventUtil = new eventManager();
//js事件通用管理器 dom元素 添加或者移除事件
function eventManager() {
//添加事件
//oDomElement:dom元素,如按鈕,文本,document等; ** oEventType:事件名稱(如:click,如果是ie瀏覽器,自動將click轉換爲onclick);** oFunc:事件觸發的函數名
this.addEvent = function(oDomElement, oEventType, oFunc) {
//ie
if (oDomElement.attachEvent) {
oDomElement.attachEvent("on" + oEventType, oFunc);
}
//ff,opera,safari等
else if (oDomElement.addEventListener) {
oDomElement.addEventListener(oEventType, oFunc, false);
}
//其他
else {
oDomElement["on" + oEventType] = oFunc;
}
}
this.removeEvent = function(oDomElement, oEventType, oFunc) {
//ie
if (oDomElement.detachEvent) {
oDomElement.detachEvent("on" + oEventType, oFunc);
}
//ff,opera,safari等
else if (oDomElement.removeEventListener) {
oDomElement.removeEventListener(oEventType, oFunc, false);
}
//其他
else {
oDomElement["on" + oEventType] = null;
}
}
}
正像註釋寫的那樣,要註冊dom元素事件,用EventUtil.addEvent(dom元素,事件名稱,事件觸發的函數名)即可, 移除時可以這樣寫:EventUtil.removeEvent(dom元素,事件名稱,事件觸發的函數名)。這是題外話,不說了。
(3)、類定義中使用this關鍵字
這個其實再常見不過,看示例:
function thisTest()
{
var tmpName = 'jeff wong';
this.userName= 'jeff wong';
}
var test= new thisTest();
alert(test.userName==test.tmpName);//false
alert(test.userName); //jeff wong
alert(test.tmpName); //undefined
分析一下結果,其實這裏的this和c#裏的是類似的。
(4)、爲腳本對象添加原形方法
理解這裏的前提是你必須瞭解js裏的原型概念(說道這裏,kao,我還真的需要面壁一下):js中對象的prototype屬性,是用來返回對象類型原型的引用的。所有js內部對象都有隻讀的prototype屬性,可以向其原型中動態添加功能(屬性和方法),
但該對象不能被賦予不同的原型。但是對於用戶定義的對象可以被賦給新的原型。看個簡單的示例:
//js的內部對象String,向其原型中動態添加功能(屬性和方法)
//去掉字符串兩端的空白字符
String.prototype.Trim = function() {
return this.replace(/(^\s+)|(\s+$)/g, "");
}
function thisTest()
{
var tmpName = 'jeff wong';
this.userName= ' jeff wong ';
}
//給用戶定義的對象添加原型方法
thisTest.prototype.ToString = function()
{
alert(this.userName); //jeff wong(有空格)
alert(this.userName.Trim()); //jeff wong (無空格)
//alert(tmpName); //腳本錯誤,tmpName未定義
}
var test= new thisTest();
test.ToString(); //調用原型的ToString()
function myTest(){
this.userName= ' test ';
}
var test1=new myTest();
//test1.ToString(); //這裏暫時不支持調用ToString()方法
//用戶定義的對象被賦給新的原型
myTest.prototype = new thisTest();
test1.ToString(); //調用原型的ToString()
測試結果顯示,這裏的this指代的是被添加原形(方法或屬性)的類的實例,和(3)中的定義基本相似。
(5)、在函數的內部函數中使用this關鍵字
這個你要是理解作用域和閉包,問題就迎刃而解。看最典型的示例:
function thisTest()
{
this.userName= 'outer userName';
function innerThisTest(){
var userName="inner userName";
alert(userName); //inner userName
alert(this.userName); //outer userName
}
return innerThisTest;
}
thisTest()();
分析:thisTest()調用內部的innerThisTest函數,形成一個閉包。innerThisTest執行時,第一次彈出innerUserName,是因爲innerThisTest函數作用域內有一個變量叫userName,所以直接彈出當前作用域下變量的指定值;第二次彈出outer userName是因爲innerThisTest作用域內沒有userName屬性(示例中的this.userName),所以它向上一級作用域中找userName屬性,這次在thisTest中找到(示例中的this.userName= 'outer userName';),所以彈出對應值。
(6)通過Function的call和apply函數指定特定的this
這個指定來指定去,this就有可能造成“你中有我,我中有你”的局面,不想把自己弄暈了的話,瞭解一下就可以了。改變this指定對象對於代碼維護也是一件很不好的事情。貼出舊文中的示例代碼結束吧:
function myFuncOne() {
this.p = "myFuncOne-";
this.A = function(arg) {
alert(this.p + arg);
}
}
function myFuncTwo() {
this.p = "myFuncTwo-";
this.B = function(arg) {
alert(this.p + arg);
}
}
function test() {
var obj1 = new myFuncOne();
var obj2 = new myFuncTwo();
obj1.A("testA"); //顯示myFuncOne-testA
obj2.B("testB"); //顯示myFuncTwo-testB
obj1.A.apply(obj2, ["testA"]); //顯示myFuncTwo-testA,其中[ testA”]是僅有一個元素的數組
obj2.B.apply(obj1, ["testB"]); //顯示myFuncOne-testB,其中[ testB”]是僅有一個元素的數組
obj1.A.call(obj2, "testA"); //顯示myFuncTwo-testA
obj2.B.call(obj1, "testB"); //顯示myFuncOne-testB
}
總結:到這裏,對於開篇中的span彈出undefined的問題你是不是已經豁然開朗?如果你還在懵懂中,給個可有可無的提示:當前的這個span元素有沒有textValue屬性啊!?
三、void
1、定義
javascript中void是一個操作符,該操作符指定要計算一個表達式但是不返回值。
2、語法
void 操作符用法格式如下:
(1). javascript:void (expression)
(2). javascript:void expression
注意:expression是一個要計算的js標準的表達式。表達式外側的圓括號是可選的,但是寫上去你可以一眼就知道括弧內的是一個表達式(這和typeof後面的表達式語法是一樣的)。
3、實例代碼
function voidTest() {
void (alert("it is a void test")); //執行函數
var oTestNum = 1;
void (oTestNum++); //整數自加
alert(oTestNum);
oTestNum = 1;
void (oTestNum += " void test"); //整數加字符串
alert(oTestNum);
}
voidTest();
4、在a元素下使用void(0)
(1)適用情況
在網頁中,我們經常看到html裏的a標籤不需要它導航到某一個頁面時,href屬性設置的寫法:
<a href="#">link1</a>
<a href="javascript:void(0);">link2</a>
注意:第一種“#”的寫法(其實#可以是多個,通常都是1個),當a元素所在的鏈接在瀏覽器一屏以下時,會導致頁面回滾到頂部;所以當我們需要a標籤不導航到其他頁面,不需要網頁位置的回滾,都會採取void(0)那種寫法。
(2)ie6下void(0)造成的詭異問題
這個問題網上有很多討論,個人認爲“落葉滿長沙[void]”總結的很有代表性,這裏就不再贅述了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章