一、說明
Javascript是動態語言,由瀏覽器執行,是解析性語言。
二、介紹 (大部分從JavaScript高級程序設計(第2版)摘錄)
五種基本數據類型
基本類型值指的是那些保存在棧內存中的簡單數據段,即這種值完全保存在內存中的一個位置。基本類型有五種,Undefined、Null、Boolean、Number和String。還有一種復雜數據類型——Object(本質上是由一組無序的名值對組成)
可以使用使用typeof操作符來獲得數據類型:
l "undefined"——如果這個值未定義
l "boolean"——如果這個值是布爾值
l "string"——如果這個值是字符串
l "number"——如果這個值是數值
l "object"——如果這個值是對象或null
l "function"——如果這個值是函數
代碼:

1 var a; 2 var b = 1; 3 var c = 'c'; 4 var d = {}; 5 var e = null; 6 var f = true; 7 var g=function(){ 8 9 }; 10 function show() { 11 var aType = 'a:' + typeof a; 12 var bType = 'b:' + typeof b; 13 var cType = 'c:' + typeof c; 14 var dType = 'd:' + typeof d; 15 var eType = 'e:' + typeof e; 16 var fType = 'f:' + typeof f; 17 var gType = 'g:' + typeof g; 18 19 alert(aType + "\n" + bType + "\n" + cType + "\n" + dType + "\n" + eType + "\n" + fType+"\n"+gType); 20 }
函數
函數使用function關鍵字來聲明,基本語法:
function functionName(arg0,arg1){
statements
}
注意下面幾點:
l 如果遇到return ; 函數會停止執行並返回undefined值
l 所有參數傳遞都是值,不可能通過引用傳遞參數
l 參數在內部是用一個數組來表示的。函數接受到的始終都是這個數組,而不關心數組中包含哪些參數(如果有參數的話)。函數體內可以通過arguments對象來訪問這個參數。arguments對象只是與數組類似(並不是Array實例)。
l 沒有傳遞值的命名參數將自動被賦予undefined值
l 如果定義了兩個名字相同的函數,則后面的會覆蓋前面的。
l 除了什么時候可以通過變量訪問函數這一點區別外,函數聲明與函數表達式的語法其實是等價的。
l 要訪問函數的指針而不執行函數的話,必須去掉函數名后面的那對圓括號。
l 函數有兩個特殊的對象:arguments和this。arguments還有一個名叫callee的屬性(指針),指向擁有這個arguments對象的函數。this引用的是函數據以執行操作的對象。
l 每個函數都包含兩個屬性:length和prototype。length表示函數希望接受的命名參數的個數。
l 每個函數都包含兩個非繼承而來的方法:apply()和call()。這兩個方法的用途都是在特定的作用域中調用函數,實際上等於設置函數體內this對象的值。
l 每個函數都有一個非標准的caller屬性,該屬性指向調用當前函數的函數。(只建議將該屬性用於調試目的)。
l 函數實際上是對象,每個函數都是Function類型的實例,而且都與其他引用類型一樣具有屬性和方法。由於函數是對象,因此函數名實際上也是一個指向函數對象的指針,不會與某個函數綁定。
l arguments.callee是一個指向正在執行的函數的指針
l JavaScript中沒有私有成員的概念,所有對象屬性都是公有的。不過,在任何函數中定義的變量,都可以認為是私有變量,因此不能在函數的外部訪問這些變量。但有兩種方式創建特權方法
l 函數f1內部定義了另外一個函數f2,當f1執行完畢后,當f2仍存在,則其活動對象也不會被銷毀,因為f2的作用域仍然在引用這個活動對象,f1的執行環境的作用域鏈會被銷毀,但它的活動對象仍然會留在內存中,直到f2被銷毀后,f1的活動對象才會被銷毀。
構造函數
注意下面幾點:
l 構造函數在不返回值的情況下,默認會返回新對象實例。
l 寄生構造函數模式:返回的對象與構造函數或者與構造函數的原型屬性之前沒有關系:也就是說,構造函數返回的對象與在構造函數外部創建的對象沒有什么不同。
l 組合繼承=原型鏈+構造函數(會用到call或apply),是最常用的繼承模式
l 寄生組合式繼承是引用類型最理想的繼承范式,YUI的YAHOO.lang.extend()方法采用了寄生組合繼承,從而讓這種模式首次出現在了一個應用非常廣泛的javascript庫中
引用類型
引用類型值是指那些保存在堆內存中的對象,意思是變量中保存的實際上只是一個指針,這個指針指向內存中的另一個位置,該位置保存對象。
基本類型和引用類型在內存中保存情況如下圖:
注意下面幾點:
l 訪問對象屬性時一般都使用點表示法,不過也可以使用方括號表示法來訪問對象屬性,但屬性必須以字符串的形式放在方括號中。
l Array數組,每一項可以保存任何類型的數據,它的大小是可以動態調整的。
l 使用索引設置數組的值時,如果超過了數組現有項數,數組會自動增加到該索引值加1的長度
l 數組的length屬性不是只讀的。
l 數組有棧方法和隊列方法(IE對JavaScript的實現中存在一個偏差,其unshift方法總是返回undefined而不是數組的新長度)
l 引用類型與基本包裝類型的主要區別就是對象的生存期。使用new操作符創建的引用類型的實例,在執行流離開當前作用域之前都一直保存在內存中。而自動創建的基本包裝類型的對象,則只存在與一行代碼的執行瞬間,然后立即被銷毀。
l 對基本包裝類型的實例調用typeof會返回"object",而所有基本包裝類型的對象都會被轉換為布爾值true。
執行環境及作用域
執行環境定義了變量或函數有權訪問的其他數據,決定了它們各自的行為。每個執行環境都有一個與之關聯的變量對象,環境中定義的所有變量和函數都保存在這個對象中。雖然我們編寫的代碼無法訪問這個對象,但解析器在處理數據時會在后台使用它。
某個執行環境中的所有代碼執行完畢后,該環境被銷毀,保存在其中的所有變量和函數定義也隨之銷毀(如果還能在其他環境中被訪問到,則不銷毀)。
環境的機制
存在環境棧,可把環境推入或彈出。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行之后,棧將其環境彈出,把控制權返回給之前的執行環境。
作用域鏈
當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈。它的用途,是保證對執行環境有權訪問的所有變量和函數的有序訪問。
作用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象。如果這個環境是函數,則將其活動對象作為變量對象。全局環境的變量對象始終都是作用域鏈中的最后一個對象。
標識符解析是沿着作用域鏈一級一級地搜索標識符的過程。
延長作用域鏈
try-catch語句的catch塊和with語句可以延長作用域鏈,即當執行流進入其中一個語句時,在作用域鏈的前端臨時增加一個變量對象,作用域鏈得到加長,該變量對象會在代碼執行后被已移除。(作用域增加的臨時變量對象都只是只讀的,所以在它們中定義的變量,成了函數執行環境的一部分)
注意下面幾點:
l JavaScript中沒有塊級作用域
l 變量在未經聲明的情況下被初始化,那么該變量會被自動添加到全局環境中。
l 局部變量會在它們離開執行環境時自動被解除引用(解除的真正作用是讓值脫離執行環境,以便垃圾收集器下次運行時將其回收)
l 垃圾收集策略:標記清除、引用計數(不常使用,除了IE,如果有循環引用會出現問題,需要手動斷開,即賦值為null,不然無法垃圾收集),不建議手動執行垃圾收集
頁面注意下面幾點:
l 在使用<script>嵌入JavaScript代碼時,不能在代碼的任何地方出現“</script>”字符串,會產生錯誤。因為按照解析嵌入式代碼的規則,當瀏覽器遇到字符串“</script>"時,就會認為那是結束的標簽。而通過把這個字符串分隔為兩部分可以解決這個問題。例如alert("<scr"+"ipt"/>);
l 解析器對<script>元素內部的所有代碼求值完畢以前,頁面中的 其余內容都不會被瀏覽器加載或顯示。
l 瀏覽器會按照<script>元素在頁面最后能夠出現的先后順序對它們依次進行解析。
l javascript代碼放在<head>和<body>的區別
l 變量定義沒有使用var,會看作全局變量
l 大量使用with語句會導致性能下降,同時也會給調試代碼造成困難,因此在開發大型應用程序時,不建議使用with語句。
l switch可以使用任何數據類型,每個case的值不一定是常量,可以是變量,甚至是表達式。switch語句在比較值時使用的是全等操作符,因此不會發生類型轉換(例如,字符串“10”不等於數值10)
三、理解
關於引用類型
在java和C++中都采用了引用這個概念,但是二者的行為並不一樣。java的引用其實更象c++中的指針,而非c++中的引用
引用其實是特殊的"指針",它只是沒有指針運算(a++,a- -這些),這樣會更安全。所以聲明定義一個引用時,它都會在棧中建立一個空間。
var a={};
var b=a;
a和b都代表同一個對象,而不是b擁有a的一個副本。
a存放了一個對象的內存地址,然后把地址賦值給了b,所以他們都指向同一個對象。注意的是,a和b都擁有自己的棧空間。
關於string特殊的基本類型(希望有人可解答我疑問)
Javascript中的基本類型都存放在棧中,並且所占空間大小固定,可是string大小並不固定,它為什么也是基本類型?。下面是我對此的理解:
l 字符串值存放在棧中:
上面所說空間固定,指的是當已經存放了值后,值的大小已經確定了,無法改變。而並不是指string基本類型在存放值之前空間大小已經固定,所以可能出現有a,b兩個string類型的值,它們在棧中的空間大小卻不一樣
賦值操作時:
var a="a";
var b=a;
此時棧中有a和b兩塊空間,存放的值都是"a",注意它們在不同的棧空間中。
l 字符串值存放在堆中:
上面所說空間固定,是指基本類型在存放值之前已經確定空間大小了。則string類型必定是一個引用,它存放指向堆空間的一個地址。可為什么不把它歸於基本類型呢?理由如下:
由於效率的原因,我們希望JS只復制對字符串的引用,而不是字符串的內容。但是另一方面,字符串在許多方面都和基本類型的表現相似,而字符串是不可變的這一事實(即沒法改變一個字符串值的內容),因此可以將字符串看成行為與基本類型相似的不可變引用類型。
關於關鍵字this
在 Java 等面向對象的語言中,this 關鍵字的含義是明確且具體的,即指代當前對象。一般在編譯期確定下來,或稱為編譯期綁定。而在 JavaScript 中,this 是動態綁定,或稱為運行期綁定的。
我的理解是——this表示的是當前環境的變量對象。
匿名函數是全局性的理由是,它不存在除全局環境之外的任何環境中,也就是說沒有引用可以訪問得到它。這時的this是全局環境的window對象。
是否為匿名函數,主要看是否有引用指向它。
代碼:

/* * 是否為匿名函數,主要看是否有引用指向它 */ var ob={}; ob.a=function () { (function() {//匿名函數 alert("a this:" + this); })(); } ob.b=function(){ alert("b this:" + this); } ob.c=function(){ return function(name){ alert(name+" this:" + this); }; } ob.d=ob.c();//ob.c()返回一個匿名函數,但是ob.d指向了它,則它不再是匿名函數,this執行ob function show() { ob.a(); ob.b(); ob.c()('c');//ob.c()得到一個匿名函數,可是卻沒有指針指向它,所以執行后this是window ob.d('d'); }
參考:JavaScript高級程序設計(第2版)