JavaScript高級程序設計(第4章 變量、作用域和內存問題)
ECMAScript變量可能包含兩種不同數據類型的值:基本類型值和引用類型值。
基本類型值指的是簡單的數據段:Undefined、Null、Boolean、Number和String。
引用類型值指可能由多個值構成的對象,是保存在內存中的對象。
不能給基本類型值添加屬性和方法,但是能爲引用類型值添加屬性和方法,也可以改變和刪除其屬性和方法。
實例:
var person = new Object();
person.name = "Nicholas";
alert(person.name); //"Nicholas"
複製基本類型值:兩個變量雖然值相同,但是是相互獨立的。
實例:
var num1 = 5;
var num2 = num1;
此例中,num1和num2都等於5,但是這兩個5是相互獨立的,不會相互影響,使用的不同的內存空間。
複製引用類型值:兩個變量實際上將引用一個對象(使用同一個指針指向內存堆中的一個對象),改變其中一個變量,就會影響另一個。
實例:
var obj1 = new object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert(obj2.name); //"Nicholas"
ECMAScript中所有函數的參數都是按值傳遞的,即把函數外部的值賦值給函數內部的參數。
這裏的按值傳遞的意思是:
如果向參數傳遞基本類型值,被傳遞的值就會被複制給一個局部變量(即命名參數,也就是arguments裏的一個元素),此時這個局部變量的變化不會反映在函數外部;
而傳遞引用類型時,實際是複製了引用類型值在內存中的地址給局部變量,此時這個局部變量的變化會反映在函數外部。
實例1:傳遞基本類型值
function addTen(num){
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
alert(count); //20
alert(result); //30
實例2:簡單傳遞引用類型值
function setName(obj){
obj.name = "Nicholas";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"
實例3:複雜傳遞引用類型值
function setName(obj){
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"
因爲setName函數內部重新給obj定義了一個新對象,這個變量引用的就是一個局部變量了,這個局部變量和全局的person對象是相互獨立的,因爲最後顯示的還是Nicholas。
同理:
function setName(obj){
obj = new Object();
obj.name = "Nicholas";
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Undefined"
1、typeof操作符
用來確定一個變量是字符串、數值、布爾值,還是undefined類型,但如果變量的值是一個對象或者null,則檢測後會返回object,當變量是函數時,會返回function。
語法:
typeof(變量);
實例:
alert(typeof("Nicholas")); //string
2、instanceof操作符
所以,一般使用instanceof操作符檢測對象的類型。
語法:
變量 instanceof 類型
實例:
alert(a instanceof Array); //檢測變量a是不是數組
執行環境(也成爲環境,execution context)定義了變量或函數有權訪問的其他數據,決定了他們各自的行爲。
每個執行環境都有一個與之關聯的變量對象(variable object),環境中定義的所有變量和函數都保存在這個對象中。
作用域的前端 ,始終都是當前執行環境的代碼所在環境的變量對象。
兩種執行環境類型:全局和局部(函數)
全局環境是最外圍的一個執行環境,在Web瀏覽器中,全局執行環境被認爲是windows對象。
每個函數都有自己的執行環境,即環境棧。
當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈(scope chain)。
作用域鏈的用途是保證對執行環境有權訪問的所有變量和函數的有序訪問。
內部環境可以通過作用域鏈訪問所有的外部環境,但外部環境不能訪問內部環境中的任何變量和函數。
每個環境都可以向上搜索作用域鏈,一查詢變量和函數名;但任何環境都不能通過向下搜索作用域鏈而進入另一個執行環境。
實例:
var a=1; //全局環境裏不能訪問函數A和B裏面的任何變量
function A(){
var b=a; //A 函數裏可以訪問全局變量a,但是不能訪問B函數裏的變量c和d
function B(){
var c=b;
var d=a; //B函數裏可以訪問A函數裏的變量b,也可以訪問全局變量a
}
}
通過在作用域鏈的前端臨時增加一個變量對象 (該變量對象會在代碼執行後被移除),可以延長作用域鏈。
具體來說,就是當執行流進入下列任何一個語句時,作用域鏈就會得到加長:
try-catch與的catch塊;
with語句。
由於JavaScript沒有塊級作用域,因而支持根據條件來定義變量。在條件語句中定義的變量,在循環結束後不會被銷燬,而會被定義到該條件所在的外部執行環境中。
實例:
for(var i = 0; i<10; i++){
doSomething(i);
}
alert(i); //10
1、申明變量
使用var申明的變量會被自動添加到最接近的環境中;
如果初始化變量時沒有使用var申明,該變量會被自動添加到全局環境。(嚴格模式下,這樣做會導致錯誤,不建議使用)
2、查詢標識符(可以忽略這個知識點,常識)
按照作用域鏈的順序查詢。
JavaScript具有自動垃圾收集機制,也就是說,執行環境會負責管理代碼執行過程中使用的內存。
JavaScript中最常用的垃圾收集方式是標記清除(mark-and-sweep):當變量進入環境時,就將這個變量標記爲“進入環境”,當變量離開環境時則將其標記爲“離開環境”。
另一種不常見的垃圾收集策略是引用計數(reference counting)。
優化內存佔用的最佳方法是,爲執行中的代碼只保存必要的數據。一旦數據不再有用,最好通過將其值設置爲null來釋放其引用,將其值脫離執行環境,以便垃圾收集器下次運行時將其回收,這個方法叫:解除引用(dereferencing)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.