javascript變量、做用域和內存問題

基本類型和引用類型的值前端

ECMAScript變量可能包含兩種不一樣數據類型的值:
瀏覽器

1.基本類型值:指簡單的數據段。(5種基本數據類型:Undefined、Null、Boolean、Number和String,這5種基本數據類型是按值訪問的,能夠直接操做保存在變量中的實際的值)函數

2.引用類型值:指那些可能由多個值構成的對象。(引用類型的值是保存在內存中的對象。與其餘語言不一樣,JavaScript不容許直接訪問內存中的位置,不能直接操做對象的內存空間)spa

動態屬性指針

定義基本類型值和引用類型值的方式都是建立一個變量併爲該變量賦值。code

對於引用變量的值,能夠添加其屬性和方法,也能夠改變和刪除其屬性和方法:對象

var person = new Object(); // 建立一個對象並保存在變量person中
person.name = "benjamin"; // 給person對象添加一個name的屬性,並將字符串值"benjamin"賦值給這個屬性
console.log(person.name); // 經過點運算符訪問這個屬性,結果輸出"benjamin"

若是對象不被銷燬或這個屬性不被刪除,這個屬性將會一直存在。blog

而對於基本類型的值,則不能添加屬性,雖然不會致使任何錯誤:ip

var person = "susan"; // 建立一個變量person賦值基本類型字符串值"susan"
person.age = 27; // 僞裝能加屬性age並賦值數值類型27
console.log(person.age); // 結果輸出undefined(屬性丟失,添加失敗,但不會報錯)

以上說明了只能給引用類型值動態添加、改變和刪除屬性(person.age = undefined)。內存

複製變量值

1.基本類型值的複製:

從一個變量向另外一個變量複製基本類型的值,會在變量對象上建立一個新值,而後把該值複製到爲新變量分配的位置上。

var num1 = 666; // num1中保存了666
var num2 = num1; // 用num1的值來初始化num2,num2中也保存了值666
console.log(num2); // 結果輸出666

這裏num2中的666和num1中的666是徹底獨立的,對這兩個變量進行任何操做都不會相互影響。

2.引用類型值的複製:

從一個變量向另外一個變量複製引用類型的值時,一樣也會將存儲在變量對象中的值複製一份放到爲新變量分配的空間中。不一樣的是,這個值的副本其實是一個指針(原來的變量存儲的值也是指針),而這個指針指向存儲在堆中的一個對象。複製操做結束後,兩個變量實際上將引用同一個對象。所以若是改變其中一個變量,就會影響另一個變量。

var obj1 = new Object(); var obj2 = obj1; obj1.name = "benjamin"; console.log(obj2.name); // 結果輸出"benjamin"

傳遞參數

ECMAScript中全部函數的參數都是按值傳遞的,也就是說,把函數外部的值複製給函數內部的參數,就和把值從一個變量複製到另外一個變量同樣。基本類型值的傳遞如同基本類型變量的複製同樣,引用類型值的傳遞如同引用類型變量的複製同樣。

基本類型值的傳遞:

function addOne(num) { // 參數num其實是函數的局部變量
    num += 1; return num; } var count = 1; var result = addOne(count); // count的值(數值1)被複制給參數num,變量count和參數num互相不認識,僅僅具備相同的值

console.log(count); // 1,沒有變化(函數內部參數num變化不影響外部count變量),說明是傳參num是按值傳遞,不是按引用傳遞
console.log(result); // 2

引用類型值的傳遞:

function setName(obj) {
    obj.name = "kevin"; // obj和person引用的是同一個對象,對obj的任何操做都直接影響person
 obj = new Object(); // 改變了obj的值(指針),接下來的任何操做都和person變量指向的對象無關
    obj.name = "mark"; // 不影響原來的person
} var person = new Object(); // 變量person中存儲的值其實是指針,該指針指向一個對象
setName(person); // 在函數內部,obj和person引用的是同一個對象(按值傳遞,傳遞的是指針)
 console.log(person.name); // 結果輸出"kevin"

若是person是按引用傳遞的,那麼在執行obj = new Object()的時候,person指向的name屬性值爲"kevin"的對象就會被覆蓋爲新的、name屬性值爲"mark"的對象(事實上沒有)。事實上,當在函數內部重寫obj的時候,這個局部變量引用的就是一個局部對象了,這個局部對象會在函數執行完畢以後當即被銷燬。

檢測類型

1.typeof操做符

主要用於檢測基本數據類型,肯定一個變量是否字符串、數值、布爾值、仍是undefined。若是變量的值是一個對象或null,則返回"object"。

var s = "james"; var b = true; var i = 25; var u; var n = null; var o = new Object(); console.log(typeof s); // "string"
console.log(typeof i); // "number"
console.log(typeof b); // "boolean"
console.log(typeof u); // "undefined"
console.log(typeof n); // "object"
console.log(typeof o); // "object"

typeof操做符檢測基本數據類型頗有用,可是對於檢測引用類型的值用處不大,由於一般咱們須要知道的是某個值是什麼類型的對象,而不是隻知道它是對象。

2.instanceof操做符

用於檢測引用數據類型。若是變量給定引用類型的實例(根據原型鏈識別),那麼instanceof操做符就會返回true,不然返回false。

var people = new Array(); var person = "ben"; console.log(people instanceof Object); // true
console.log(people instanceof Array); // true
console.log(people instanceof RegExp); // false 
console.log(person instanceof Object); // false

ECMAScript規定全部引用類型的值都是Object的實例。使用instanceof操做符檢測基本類型的值,該操做符始終會返回false,由於基本類型不是對象。  

執行環境及做用域(難)

幾個概念:

1.執行環境(execution context)

執行環境定義了變量或函數有權訪問的其餘數據,決定了它們各自的行爲。

全局執行環境是最外圍的一個執行環境。在Web瀏覽器中,全局執行環境能夠認爲是window對象,所以全部全局變量和函數都是做爲window對象的屬性和方法建立的。

每一個函數都有本身的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行以後,棧會將其環境彈出,把控制權返回給以前的執行環境。ECMAScript程序中的執行流正是由這個方便的機制控制着。

當某個執行環境中的全部代碼執行完畢後,該環境被銷燬,保存在其中的全部變量和函數也隨之銷燬(全局執行環境直到應用程序退出,例如關閉網頁或瀏覽器的時候纔會被銷燬)。

2.變量對象(variable object)

每一個執行環境都有一個與之關聯的變量對象,環境中定義的全部變量和函數都保存在這個對象中。雖然咱們在編寫代碼沒法訪問這個對象,但解析器在處理數據時會在後臺使用它。

3.做用域鏈(scope chain)

當代碼在一個環境中執行時,會建立變量對象的一個做用域鏈。做用域鏈的用途是保證對執行環境有權訪問的全部變量和函數的有序訪問。

做用域鏈的前端始終是當前執行的代碼所在環境的變量對象。

若是這個環境是函數,則將其活動對象做爲變量對象。活動對象在最開始時只包含一個變量,即arguments對象。做用域鏈的下一個變量對象來自包含環境,而下一個變量對象則來自下一個包含環境,這樣一直延續到全局執行環境,全局執行環境的變量始終都是做用域鏈中的最後一個對象。

4.活動對象(activation object)

當函數被調用時,一個特殊的對象,即活動對象將會被建立,這個對象中包含形參和arguments對象。活動對象以後會做爲函數上下文的變量對象來使用。

這樣理解:活動對象除了變量和函數聲明以外,它還存儲了形參和arguments對象。

相關文章
相關標籤/搜索