標籤:js中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]”總結的很有代表性,這裡就不再贅述了。
JavaScript的self和this使用小結