javascript進階系列

來源:http://www.blogjava.net/zkjbeyond,作者:zkjbeyond

一、javascript進階之變量篇


仔細的看了看<javascript權威指南>,算筆記吧
1、關於變量的聲名
大家都知道javascript是可以隱式聲名變量的。但要注意,隱式聲名變量總是被創建爲全局變量。看以下代碼,情願javascript語言強制聲明變量。建議大家一定要var聲明變量。
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
function test(){
    var a=222;
document.writeln(a);
}
test();
document.writeln(a);
</SCRIPT>



javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
function test(){
    a=222;
document.writeln(a);
}
test();
document.writeln(a);
</SCRIPT>

2、關於變量的作用域
猜猜以下代碼輸出什麼。
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
var x='000';
document.writeln(x);
a();
function a(){
    var x='aaa';
function b(){
     document.writeln(x);
        var x='bbb';
  document.writeln(x);
}
b();
    document.writeln(x);
}
</SCRIPT>


如果你的答案是 000 undefined bbb aaa。恭喜,ok.當代碼用到x變量時,先從函數塊(權威指南中用調用對象來解釋)中找,如果找不到,從上一級函數塊找,直到找到,如果知道頂層代碼 (指var x='000';的位置)還沒找到定義,代碼會報未定義錯誤。

改一下代碼,得到 000 undefined 111 111
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
var x='000';
document.writeln(x);
a();
function a(){
function b(){
     document.writeln(x);
  document.writeln(x);
}
document.writeln(x);
var x='111';
b();
}
</SCRIPT>


3、新的問題
變量個作用域清楚了,注意上面的代碼。爲什麼我的function a()定義以前就可以調用a函數了,而我的var x='111';前“不可以用”x啊???
讓我把我的理解一一道來
首先:以下代碼讓我相信javascript有個預編譯過程,不是完全按照順序解釋執行的。
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
a();
function a(){
    alert();
}
</SCRIPT>


個人理解這個預編譯過程不會象java/c#那樣把代碼編譯成虛擬機認識的語言,更不會象vb,vc那樣編譯成更底層的語言。猜想只是把這個函數 預裝載到這段函數執行的全局環境中,在這個執行環境中,該函數被標識定義過,可以直接使用了。(看到網上很多人寫的AOP的javascript實現,其 實這個預編譯過程纔是翻譯元數據最佳時候,可惜就javascript語言來講,是有些落伍了)

這個文章主要講變量的一些問題。變量說了,爲什麼函數可以,我變量就不可以呢。
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
document.writeln(a);
var a=0;
</SCRIPT>


爲什麼我要輸出undefined呢?爲什麼我a就不可以預編譯一把呢?
大家看看以下兩段代碼會輸出什麼呢啊???
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
document.writeln(a);
a=0;
</SCRIPT>



javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
document.writeln(a);
</SCRIPT>



可能你運行試了,可能你本來就知道,a未定義。哈哈哈,好了。
現在我確信var a=0;被javascript解釋器“預編譯過”,至少是記錄下來了。甚至把它的值設置爲 undefined。“undefined”這個詞名字取的很是讓人誤解,怎麼能叫未定義呢,分明是javascript中所有變量的初始化值。關於 null與undefined的比較我實在不願提了。
注意上面兩段代碼還反映一個現象。隱式聲明的變量是在解釋的時候才把自己定義爲全局變量的。

關於函數與變量javascript預編譯的不同處理,大家可以與java class的加載過程比較下。java也是對基本類型設出值,對象爲null的。(不往遠扯了)
4、區別未定義變量和未附值變量
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
var a;
document.writeln(a);
</SCRIPT>



javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
document.writeln(a);
</SCRIPT>


未定義變量和未附值變量 權威指南中文版 定義的。通過第三條分析,我覺得變量就應該以 定義和未定義變量區別。未附值變量和undefined有點衝突,javascript不是強類型語言,沒發附默認值,纔來了個undefined。
5、基本類型和引用類型
熟悉java的朋友可能這部分很清楚。沒啥
說頭。
6、javascript的垃圾回收
關於這部分內容一直沒見着個權威說法。在javascript權威指南中有兩小節提到這個問題。
對於字符串、對象、數據這些沒有固定大小,必須爲它們動態的分配內存,但什麼時候回收這些內存呢?javascript使用和java一樣的garbage collection的方法。
javascript代碼: 

var s="hello";
var u=s.toUpperCase();
s=u;


運行這段代碼後,"hello"沒有變量會再用到他,這是"hello"的存儲空間的被垃圾回收了。對於javascript的垃圾回收,你唯一要關心的是,它一定會進行,不要對內存擔心。
注意,javascript不提供任何的強制垃圾回收或釋放內存的運算附或語句。
javascript的delete運算附和C++中的不同。
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
var o=new Object();
o.name="zkj";
o.age=25;
o.bir=new Date();
for(var key in o){
document.writeln(key+':'+o[key]+'</br>');
}
document.writeln('delete o.bir</br>');
delete o.bir;
for(var key in o){
document.writeln(key+':'+o[key]+'</br>');
}
</SCRIPT>



7、作爲屬性的變量
猜猜以下代碼會輸出什麼。
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
var x=100;
document.writeln(x);
add(x);
document.writeln('</br>------------------------</br>');
var x=200;
document.writeln(x);
add(x);
function add(x){
    document.writeln(x);
    var x=300;
    document.writeln(x);
    var x=400;
    document.writeln(x);
}
</SCRIPT>



估計很多人能得出正確答案
100 100 300 400
------------------------
200 200 300 400
但這裏我想引入 全局對象和調用對象的 概念(javascript權威指南是這麼翻譯滴)
javascript代碼: 

<SCRIPT LANGUAGE="JavaScript">
var x=100;   //我們在全局對象中加了個屬性x. 對比
//var o=new Object();o.x=100;
document.writeln(this.x);//用this訪問全局對象
add(this.x);//把全局對象的屬性值傳遞對函數中
document.writeln('</br>------------------------</br>');
this.x=200;//把全局變量中的x屬性修改掉
document.writeln(window.x);
add(window.x);
function add(x){
    //假設有個局部對象,調用對象,函數調用過程中的對象
    //  temp   temp.x=${傳入的值}
    document.writeln(x); //這打印的可是參數中的值,也就是temp.x=this.x
//的值,
    var x=300;//把調用對象變量的簽名給覆蓋了.
    document.writeln(x); //打印修改過的值。 temp.x
var x=400;//temp.x=400
    document.writeln(x);
}
</SCRIPT>



在函數的調用過程中,假設有個調用對象存在,把函數的參數,和函數內的臨時變量當成這個調用對象的屬性。當然這個調用對象的生命週期很短。
注意,當我們訪問全局變量的屬性入x的時候,不必要用this.x 或window.x訪問,當在有<frame><iframe>的頁面時會出現混淆。
關於函數的詳細討論我後續會詳細討論。

二、
javascript進階之函數篇

在javascript中,function作爲一個關鍵字有兩方面的應用。
a、可以定義函數。
b、可以定義一個數據類型。這裏只介紹函數。關於function定義類的功能在對象中介紹。
1、函數的概念
javascript中提供了許多預定義函數,我們可以直接使用,比如Math.sin();eval()等。
我們也可以定義自己的函數。有以下四種方法:

javascript代碼: 

a、function add(x,y){return x+y;}
b、var add = function(x,y){return x+y;}
c、var add = new Function("x","y","return x+y;");
d、var add =function a(x,y){return x+y;}



在這,如果用add(2,3);調用,返回的結果都是5。但它們使用有很大的區別,後面在詳細解釋。

2、函數的參數
回想上一篇提到函數的調用對象。雖然我們不能訪問它,看不見,但引入這個概念確實可以方便我們解釋很多東西。
在一個函數體內,標識符arguments有特殊含義。它是調用對象的一個特殊屬性。



javascript代碼: 

<SCRIPT LANGUAGE = "JavaScript">
  function  add(x,y)  {
document.writeln('x = ' + x);
document.writeln('y = ' + y);
document.writeln('arguments[ 0 ] = ' + arguments[ 0 ]);
document.writeln('arguments[ 1 ] = ' + arguments[ 1 ]);
arguments[ 0 ] = 11 ;
document.writeln('arguments[ 0 ] = ' + arguments[ 0 ]);
return  x + y;
}
document.writeln(add( 2 , 3 ));
</SCRIPT>


看例子,基本你可以把arguments當成個數組使用,但並不是真正的數組。
雖然arguments.length and arguments[index]可以在函數內改變,但建議你不要在函數內改變它們。關於arguments的具體用法可以看prototype.js.
arguments還有個屬性 callee.引用當前正在執行的函數。



javascript代碼: 

<SCRIPT LANGUAGE = "JavaScript">
  function  add(x,y)  {
    document.writeln(arguments.callee);
    document.writeln( typeof (arguments.callee));
     return  x + y;
}
document.writeln(add);
document.writeln( typeof (add));
document.writeln(' </ br >-----------------------</ br > ');
add();
</SCRIPT>



3、函數是一種數據類型
在javascript中,函數不但能象java一樣當成語言的語法特性,還可以象字符串、數字、日期一樣當成一種數據。
var add=function(x,y){return x+y;};其實我們函數的定義都是把函數附值給變量。函數不但可以附值給變量,還可以當成參數傳遞。



javascript代碼: 

<SCRIPT LANGUAGE = "JavaScript">
  var  add = function (x,y)  { return  x + y;} ;
var  fun = add;
function  opetate(fun,x,y)  {
     return  fun(x,y);
}
alert(opetate(fun, 2 , 3 ));
</SCRIPT>




其實javascript的熟手不會在全局變量裏(直接在js文件中)定義函數,都會定義在全局變量的屬性字段中。如


javascript代碼: 


<SCRIPT LANGUAGE = "JavaScript">  
    var  MyLib =  {} ;
  MyLib.fun1 = function ()  {} ;
  MyLib.fun2 = function ()  {} ;
  alert(MyLib);
  alert(MyLib.fun1);
</SCRIPT>



如果這種寫法,我們就基本不會和別人寫的程序發生命名上的衝突了。

4、函數的生命週期
在變量裏我們知道函數有個預編譯過程,這個過程我們也看到不到,另外函數定義不同寫法情況也不同。所以,感覺函數的執行過程很是複雜。



javascript代碼: 

<SCRIPT LANGUAGE = "JavaScript">  
  fun1();
  alert(fun1);
// ///////////把以下代碼放到定義最後面可以執行。
   fun2();
  alert(fun2)
  fun3();
  alert(fun3)
   // /////////////////////////
     function  fun1()  {document.writeln('fun1 run ! ')}
    var  fun2  = function ()  {document.writeln('fun2 run ! ')} ;
   var  fun3  = new  Function( " document.writeln('fun3 run!') " );
</SCRIPT>



a、javascript預編譯過程(在html中, </head>前後,<body>內外有特殊情況,這指純.js文件中)
javascript的預編譯我認爲是個很簡單的過程,對於解釋執行語言,肯定不會編譯成什麼中間語言。(我認爲)過程如下:
首先,爲執行環境(一個html也,框架環境下有幾個執行環境)建立建立一個全局對象,一般客戶端腳本爲 window或global對象。(在這我覺得global好理解,因爲瀏覽器會放入window總其他的屬性)
然後,檢查某環境中根代碼塊中(非函數或{}中)var關鍵字,把這些變量設置成global對象的屬性,並附初值undefined.
如果過程中遇到直接定義的函數(fun1的定義),那麼把fun1設置成global對象的屬性,並附初值函數的定義(你可以把函數當成undefined,true,1,'1'等數據類型)。
b、解釋執行過程
代碼按照順序執行,
一般我們把全局變量的俯值放到代碼最前面,如var s="hello"; 那麼把s的值用 'hello'替換預編譯過程的undefined。
如果遇到變量的應用,如代碼 document.writeln(a),如果我們在global找到屬性a,看a是否在前面代碼俯值過,如果沒有,a的值爲預編譯的undefined。如果是隱式變量聲明,在這把它添加到global的屬性中去。
如果遇到類似var fun=function(){}; 把函數當成數據賦值給變量fun.
c、函數的調用過程
你運行一下以下代碼,爲什麼結果是 test undefined local


javascript代碼: 

<SCRIPT LANGUAGE = "JavaScript">  
var  s = " golbe " ;
function  fun(s)  {
    document.writeln(s);
    document.writeln(a);
     var  s = " local " ;
     var  a = "" ;
     return  s;
}
document.writeln(fun( " test " ));
</SCRIPT>




首先:預編譯代碼,把s、fun設置爲golbe的屬性,並賦值s=undefined,fun=函數定義(可以直接調用)。

然後:按照順序解釋執行代碼。賦值s="golbe"; 函數已經可以直接調用了。
執行函數:當運行到fun("test")時,開始函數調用。
(1)先爲本次調用建立一個調用對象(fun-global)。
(2)預編譯函數體內代碼,把參數設置爲fun-global的屬性,值爲爲傳 值"test",查找var,把a設置成fun-global的屬性,值爲undefined.(如果var s與參數名稱相同,則不重複設置例如函數體內var s="local"Wink
(3)按照順序執行函數內代碼,當代碼中遇到變量,先從調用對象屬性中查找,如果找到直接使用。遇到var s="local";時,代碼會覆蓋調用對象中屬性值(和參數名稱相同)

調用對象是個暫時對象,生命週期很短,只有在調用過程中存在,遞歸可能會建立多個獨立的調用對象。
javascript的函數應該和java中一樣是線程安全的吧。


5、函數的屬性和方法
a、length

看了以下代碼一目瞭然,函數的length代表函數實際需要的參數個數。


javascript代碼: 

<SCRIPT LANGUAGE = "JavaScript">  
  function  add(x,y)  {
     var  actual = arguments.length;
     var  expected = arguments.callee.length;
     if (actual != expected)  {
         throw   new  Error('函數需要' + expected + '個參數,你輸入了' + actual + '個');
    }
     return  x + y;
}
alert(add( 3 , 2 ));
alert(add( 3 , 2 , 3 ));
</SCRIPT>



b、prototype
這個屬性引用的是預定義的原型對象,只有在new 函數名 纔有效。講解對象時介紹。
c、定義自己的函數屬性


javascript代碼: 

<SCRIPT LANGUAGE = "JavaScript">  
add.count = 0 ;
function  add(x,y)  {
    add.count ++ ;
     return  x + y;
}
add( 3 , 2 );
add( 3 , 2 );
add( 3 , 2 );
add( 3 , 2 );
document.writeln('共調用函數幾次:' + add.count);
</SCRIPT>



d、apply() call()方法
去讀讀prototype.js吧。

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