JS---變量、作用域和內存問題


由於Javascript是松散型的,所以其變量只是在特定時間用於保存特定值的一個名字而已,並不存在某個變量必須保存某種類型的值的規則,變量的值以及其數據類型都可以在腳本的聲明周期內改變

一、基本類型與引用類型

  1.1 基本類型:保存在棧內存中的簡單數據段,值保存在內存中的一個位置

  1.2 引用類型:保存在堆內存中的對象,變量保存的僅僅是一個指針,這個指針指向內存中的另一個位置,該位置保存對象

    1.2.1 5種基本數據類型:Undefined、Null、Boolean、Number、String在內存中分別占有固定大小的空間,保存在棧內存中

    1.2.2 按值訪問:對於保存基本數據類型的變量,我們操作的是它們實際保存的值

    1.2.3 引用類型的值由於大小不固定,因此保存在堆內存中,但存放引用類型值的堆內存的內存地址大小是固定的,因此我們將這個地址保存在棧內存中,當操作引用類型的值時,先到棧內存        讀取該引用類型值的堆內存地址,然后在找到保存在堆內存中的引用類型的值

    1.2.4 按引用訪問:因為我們操作的不是實際的值,而是被這個值所引用的對象

    1.2.5 保存在棧內存中的每個值分別占據固定大小的空間,因此可以按照順序來訪問

      

  1.3 動態屬性:只能給引用類型值動態的添加屬性

  1.4 復制變量值:針對基本數據類型值的復制,會在棧中創建一個新值,然后把該值復制到為新變量分配的位置上

    針對引用類型值時,復制的只是一個指針,復制操作結束后,兩個變量實際上將引用同一個對象

var num1=5;
var num2=num1;
num1=6;
alert(num2);//5
/**/
var obj1=new Object();
var obj2=obj1;
obj1.name="Jim";
alert(obj2.name);//"Jim"

 

 1.5 傳遞參數:所有的參數傳遞全部是按值傳遞,雖然訪問變量的時候有按值和按引用兩種方式,但是參數傳遞只能按值傳遞。在向參數傳遞引用類型的值時,會把這個值在內存中的地址復制給一個局部變量,這個變量的變化不會反映在函數外面

function setName(obj){
    obj.name="Jim";
    obj=new Object();
    obj.name="Greg";
}
var p=new Object();
setName(p);
alert(p.name);//"Jim"
//如果是按引用傳遞的話,那么p就會被自動修改為指向其name屬性為”Greg“的新對象,但是由下圖分析可知,參數傳遞是按值傳遞

  1.6 檢測類型:typeof操作符是確定一個變量是字符串、數值、布爾、還是undefined,以及object的最佳工具,但是對於一個對象或者null,卻只能返回"object",因此在檢測引用類型值的時候,引入了instanceof操作符

function Name(){
}
var p=new Name();
alert(p instanceof Name);//true

二、執行環境和作用域

  2.1 執行環境定義了變量和函數有權訪問的其他數據,每個執行環境都有一個變量對象,環境中的所有變量和函數都保存在這個對象中,我們編寫的代碼無法訪問這個對象

  2.2 全局執行環境:最外圍的一個執行環境,在web瀏覽器中,全局執行環境是window對象,因此全局變量和函數都作為window對象的屬性和方法創建的

  2.3 當某個執行環境的所有代碼執行完畢后,該環境會被銷毀,保存在其中的變量和函數也會被銷毀

  2.4 全局執行環境的銷毀是在關閉網頁或者瀏覽器時

  2.5 每個函數都有自己的執行環境,當執行到某個函數時,會把控制權交給該函數,直到該函數執行完畢后,再把控制權交給之前的環境

  2.6 當代碼在一個環境(例如一個函數)中執行時,會創建變量對象的作用域鏈

  2.7 作用域鏈的作用是保證對執行環境中的變量和函數的有序訪問

  2.8 作用域鏈的前端始終是當前代碼的執行環境的變量對象

   2.9 如果執行環境是函數,那么變量對象是活動對象,初始只包含一個變量arguments對象,外層是包含該環境的環境,外層的外層是包含該環境的環境的環境,一直延伸到全局執行環境

  2.10 全局執行環境始終是作用域鏈的最后一個對象

  2.11 在搜索某個標識符(如變量名,函數名等)時,會沿着作用域鏈一級一級的搜索,直到找到,搜索停止,這也就達到了2.7中提到的作用域鏈的作用(保證對執行環境中的變量和函數的有序訪問)

var color="blue";
function change(){
    var another="red";
    function swap(){
        var temp=another;
        another=color;
        color=temp;
    }//這里swap函數只是定義,並沒有執行
    swap();//這里才是swap函數的執行
}//這里change函數只是定義,並沒有執行
change();//這里才是change函數的執行

 

  2.12 內部環境可以通過作用域鏈一級一級訪問都所有外部的變量以及函數,包括最外層的全局執行環境,但是外部環境卻不能訪問內部環境的變量和函數,作用域鏈只能從下向上搜索,不能從下向上搜索

  2.13 例如swap函數可以訪問全局變量color,但是全局變量window卻不能訪問到another或者temp

  2.14 延長作用域鏈的方式:第一try-catch語句中的catch塊,第二with語句,這兩個語句都會在作用域鏈的前端添加一個變量對象,對於with語句來說,其變量對象中包含着為指定對象的所有屬性和方法所作的變量聲明。對於catch語句來時,其變量對象中包含的是被拋出的錯誤對象的聲明,這些變量對象是只讀的

  2.15 瀏覽器的兼容性問題1注意IE與標准在try-catch語句延長作用域鏈的不同之處:即使在catch塊的外部也可以訪問到錯誤對象

  2.16 js沒有塊級作用域的概念,所謂塊級作用域就是由花括號封閉的代碼塊都有自己的作用域,但是js中使用var聲明的變量,會被自動添加到距離最近的可用的執行環境中,未使用var聲明被初始化的變量,會被自動添加到全局環境,其作用范圍跟花括號沒有關系

  2.17 查詢標識符時也會根據作用域鏈,從下向上一級一級進行搜索,搜索到后,就停止,因此,如果局部環境存在同名的標識符,就不會使用父環境中的標識符

三、垃圾收集機制

  3.1 js具有自動垃圾收集機制,垃圾收集器會按照固定的時間間隔周期性地執行這一操作,兩種方式:標記清除和引用計數

  3.2 標記清除:js中最常用的垃圾收集方式,當執行流進入到相應的環境(即當執行流調用某個函數)時,該函數中聲明了某個變量,那么這個變量就被標記為“進入環境”,從邏輯上講,永遠不能釋放進入環境的變量的內存,當變量離開環境時,則被標記為“離開環境”。可利用翻轉某個特殊的位來記錄

  3.3 引用計數:跟蹤記錄每個值被引用的次數,將一個引用類型的值賦值給一個變量,那么這個引用類型的值的引用次數就加1,相反,如果這個變量被賦值了其他值,這個引用類型的值的引用次數就減1,當引用次數為0時,就說明沒有辦法再訪問這個引用類型的值了,那么她所占的內存空間會被垃圾回收器給回收

  3.4 循環引用:如果對象A中包含一個指向對象B的指針,而對象B中也包含一個指向對象A的指針,就形成了循環引用,它們的引用次數永遠不會為0,為了避免循環引用問題,最好是在不使用它們的時候,手工斷開它們之間的連接

  3.5 內存限制問題:js中分配給web瀏覽器的可用內存數量通常比分配給桌面應用程序的少,原因是為了防止運行js的網頁耗盡全部內存而導致系統崩潰

  3.6 解除引用:由於內存的限制,所以我們要優化內存占用,最佳策略就是執行代碼只保存必要的數據,一旦數據不再用,最好通過將其值設置為null來釋放對其的引用,這種方法稱為解除引用

  3.7 解除引用的適用范圍:解除引用適用於大多數全局變量和全局對象的屬性,局部變量會在它們離開執行環境時自動被解除引用

  3.8 對解除引用需要聲明的一點:解除引用並不代表收回該值所占的內存,解除引用的真正作用是讓值脫離執行環境,以便垃圾收集器下次運行時將其收回


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM