js高級程序設計-讀書記錄 ch4

第四章 變量、作用域和內存問題

4.1 基本類型和引用類型的值

這是ECMA包含的兩種不同數據類型。基本類型是保存在棧內存中的簡單數據段,即這種值完全保存在內存中的一個位置;

引用類型則是指那些保存在堆內存中的對象,意思是變量中保存的實際上是一個指針,這個指針指向內存中的另一個保存對象的位置。

在將一個值賦給變量時,必須確定這個值是基本類型值還是引用類型值。ch3提到的五種基本數據類型:Undefined,Null,Boolean,Number,String.這五種基本數據類型的值在內存中分別佔有固定大小的空間,所以可以把它們的值保存在棧內存中,對於保存基本類型的值,我們說它們是按值訪問的。

如果賦給變量的是一個引用類型的值,則必須在堆內存中爲這個值分配空間,由於這種值的大小不固定,因此不能把它們保存在棧內存中。但內存地址的大小是固定的,因此可以把內存地址保存在棧內存中。先從棧中讀取內存地址,再找到保存在堆內存中的值。保存在堆內存中的數據不是按照順序訪問的,因爲每個對象所需要的空間並不相等。

(棧內存、堆內存、內存空間?)

似乎就是說棧內存中要麼保存着基本數據類型的值要麼保存的是引用類型在堆內存中的地址。

4.1.1 動態屬性大笑

定義基本類型值和引用類型值的方式是類似的:創建一個變量併爲該變量賦值。但是對不同類型值可以執行的操作大相徑庭。對於引用類型的值,我們可以爲其添加屬性和方法,也可以改變和刪除其屬性和方法。

var person=new Object();//引用類型就是對象嗎?
person.name="Nicolas";
alert(person.name);

不能給基本類型的值添加屬性,儘管不會報錯,但如若輸出,是“Undefined”

4.1.2 複製變量值大笑

基本類型:從一個變量向另一個變量複製基本類型的值,會在棧中創建一個新值。

引用類型:同樣也會將存儲在棧中的值(也只是地址相當於指針了)複製一份放到爲新變量分配的空間中。指向同一個對象。

4.1.3 傳遞參數大笑

ECMA中所有函數的參數都是按值傳遞

基本類型的值傳遞給參數,其在函數內的操作不會影響它本身的值。而引用類型就會了。說起來略顯抽象,其實也就是C那一套。媽的反正就是都傳遞內存中存的東西唄,一個是值而另一個是指針。這書講這兒講得一點不清楚,沒基礎的肯定都暈了。

看下面一個例子

function setName(obj){
    obj.name="Nicolas";
    obj=new Object();
    obj.name="Greg";
}
var person=new Object();
setName(person);//剛剛寫成person.setName() 注意一下
alert(person.name);//Nicolas 因爲是按值傳遞

4.1.4 檢測類型大笑

typeOf操作符是確定一個變量是字符串、數值、布爾值還是Undefined的最佳工具。如果變量的值是一個對象或null,則typeOf會返回“object”。使用typeOf檢測函數時,會返回function。

根據規定,所有引用類型的值都是Object的實例。因此,在檢測一個引用類型值和Object構造函數時,instanceof操作符始終返回true。

4.2 執行環境和作用域

執行環境是js中最重要的一個概念。執行環境定義了變量或函數有權訪問的其他數據,決定了它們各自的行爲。每個執行環境都有一個與之關聯的變量對象,環境中定義的所有變量和函數都保存在這個對象中。雖然我們編寫的代碼無法訪問這個對象,但是解析器在處理數據時會在後臺使用它。

每個函數在被調用時都會創建自己的調用環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行之後,棧將其環境彈出,把控制權返回給之前的執行環境。

當代碼在一個環境中執行時,會創建由變量對象構成的一個作用域鏈。作用域鏈的用途,是保證對執行環境有權訪問的所有變量和函數的有序訪問。作用域鏈的前端始終是當前執行的代碼所在環境的變量對象。(真抽象,不知道還要看多少概念)

標識符解析是沿着作用域鏈一級一級地搜索標識符的過程。看下面這個例子

var color="blue";
function changeColor()
{
    if(color=="blue"){
        color="red";
    }
    else{
        color="yellow";
    }
}

changeColor();
//changeColor("red")或是changeColor(color1)都沒有用
alert("color is now "+color);//red
//changeColor的作用域包含兩個對象:他自己的變量對象(其中定義着arguments對象)和全局環境的變量對象)

此外,在局部作用域中定義的變量可以在局部環境中與全局變量互換使用,如下面例子

var color="blue";
function changeColor()
{
    var anotherColor="red";
    function swapColors(){
        var tempColor=anotherColor;
        anotherColor=color;//blue
        color=tempColor;//red
        //這裏可以訪問color, anotherColor和tempColor
    }
    //這裏可以訪問color和anotherColor,但不能訪問tempColor
    swapColors();
}
changeColor();
//這裏不能訪問anotherColor和tempColor,但可以訪問color
alert("color is now "+color);//red

內部環境可以通過作用域鏈訪問所有的外部環境,但外部環境不能訪問內部環境中的任何變量和函數。

注:函數參數也被當做變量來對待,因此其訪問規則與執行環境中的其它變量相同。

4.2.1 延長作用域鏈

延長辦法:當執行流進入下列任何一個語句時,作用域鏈就會得到加長。

......try-catch的catch塊

......with語句

這兩個語句都會在鏈的前端添加一個變量對象,對with語句來說,其變量對象中包含着爲指定對象的所有屬性和方法所做的變量聲明;對catch語句來說,其變量對象中包含的是被拋出的錯誤對象的聲明。這些變量對象都是隻讀的,因此在with和catch語句中聲明的變量都會被添加到所在執行環境的變量對象中。下面看一個例子

function buildUrl(){
    var qs="?debug=true";
    
    with(location){
        var url=href+qs;//實際上是location.href
    }//出了with框仍然有效
    return url;
}

var result=buildUrl();
alert(result);

4.2.2 沒有塊級作用域

if(true){
    var color="blue";
}
alert(color);//blue

在C/C++/JAVA中,color會在if語句執行完畢後被銷燬。但在JavaScript中,if語句中的變量聲明會將變量添加到當前的執行環境;由for語句創建的變量i即使在for循環結束後,也依舊會存在於循環外部的執行環境中。

js沒有塊級作用域,函數沒有重載。

1.聲明變量

在使用var聲明變量時,這個變量將被自動添加到距離最近的可用環境中。對於函數而言,這個最近的環境指函數的局部環境;對於前面例子的with語句而言,這個最近的環境也是函數的環境。如果變量在未經聲明的情況下被初始化,那麼變量會被自動添加到全局環境。但絕對不推薦這麼寫。

2.查詢標識符

當在某個環境中爲了讀取或寫入而引用一個標識符時,必須通過搜索作用域鏈來查詢與給定名字匹配的標識符。將一直追溯到全局環境的變量對象。

變量查詢也是有代價的。

4.3 垃圾收集

具有自動垃圾收集機制。

4.3.1 標記清除

4.3.2 引用計數

4.3.3 性能問題

4.3.4 管理內存



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