Javascript 變量、作用域和內存題目

基礎範例和援用範例的值

基礎範例值:簡樸的數據段 ,五種基礎範例(Number Boolean String Null Undefined)的值都是基礎範例值,基礎範例的值在內存中大小牢固,因此保存在棧內存中。
援用範例值:能夠由多個值組成的對象。不能操縱援用範例的內存空間。保存在堆內存中。

動態的屬性

援用範例值

我們可認為援用範例的值增加、修正、刪除屬性和要領,比方:

var cat = new Animal();
cat.name = "cat";
cat.speak = function()  {
  alert(this.name);
};
cat.speak(); // cat
基礎範例值

但是為基礎範例的值增加屬性和要領是無效的。

var name = "Sue";
name.age = 18;
alert(name.age); //undefined

複製變量值

基礎範例值
var num1 = 5;
var num2 = 5;

num1與num2的內存空間是完整自力的,對一方的轉變不會影響到另一方。

援用範例值
var obj1 = new Object();
var obj2 = obj1;
obj2.name = "Sue";
alert(obj1.name); // Sue

當我們將對象obj1複製給obj2時,只是建立了一個指針副本,這個指針副本與obj1指向同一個保存在堆內存中的對象。因此轉變一方,另一方也會發作響應的轉變。

通報參數

實介入形參
var num = 2
function add(num1, num2) {
  return num1 + num2;
}
add(1, num);

在上述代碼中,add(1, num)傳入的參數是實參,而arguments[]老是獵取由實參串起來的參數值,在函數體中的num1 num2是形參,相當於聲清楚明了兩個局部變量,指向arguments[0] arguments[1]

按值通報

ECMAScript 中一切函數的參數都是按值通報的,把函數外部的值複製給函數內部的參數,就和把值從一個變量複製到另一個變量一樣(無論是基礎範例照樣援用範例)。

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}
var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);   // 10
console.log(obj1.item);    // changed
console.log(obj2.item);    // unchanged

以上的例子是怎樣申明ECMAScript中函數的參數都是按值通報的呢?
起首基礎數據範例,全局變量num複製本身給參數num,兩者是完整自力的,修改不會相互影響。
關於對象,我們好像看到了函數內部對obj1對象屬性的修改迴響反應到了函數外部,而obj2從新賦值為另一個對象卻沒有迴響反應到外部,這是為何呢?書中詮釋得有點簡樸,筆者找了一下材料,本來傳入對象的時刻,實在傳入的是對象在內存中的地點,當在函數中轉變對象的屬性時,是在同一個地區舉行操縱,所以會在函數外反應出來,但是,假如對這個局部變量從新賦值,內存中的地點轉變,就不會對函數外的對象產生影響了,這類頭腦稱為 call by sharing。

 However, since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are 
mutable, within the function are visible to the caller, which may appear to differ from call by value semantics. Mutations of a mutable object within the function are visible to the caller because the object is not copied or cloned — it is shared.
Wikipedia

檢測範例

哪一種基礎數據範例

運用typeof能夠識別String Number Undefined Boolean Object另有函數。

typeof("name"); //string
typeof(18); //number
typeof(undefined); //undefined
typeof(null); //object
typeof(true); //boolean
typeof(new Array()); //object
typeof(Array); //function

正則表達式在某些瀏覽器中typeof返回效果為object,某些返回function

什麼範例的對象

instanceof能夠推斷是不是是給定範例的實例

var a = new Array;
a instanceof Array; //true

運用instanceof測試基礎數據範例時,用於返回false

實行環境和作用域

實行環境(execution context)

定義了變量或函數有權接見的其他數據。每一個實行環境都有一個與之關聯的變量對象(variable object),環境中定義的一切變量和函數都保存在這個對象中。

全局實行環境

全局實行環境是最外圍的一個實行環境。在Web 瀏覽器中,全局實行環境被認為是window 對象,因此一切全局變量和函數都是作為window 對象的屬性和要領建立的。某個實行環境中的一切代碼實行終了后,該環境被燒毀,保存在个中的一切變量和函數定義也隨之燒毀(全局實行環境直到運用程序退出,比方封閉網頁或瀏覽器時才會被燒毀)。

函數實行環境

當實行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數實行以後,棧將其環境彈出,將控制器返還給之前的實行環境。

作用域鏈(scope chain)

當代碼在一個環境中實行時,會建立變量對象的一個作用域鏈,以保證對實行環境有權接見的一切變量和函數的有序接見。作用域鏈的前端,一直都是當前實行的代碼地點環境的變量對象,關於全局實行環境,就是window對象,關於函數實行環境,就是該函數的運動對象。作用域鏈的後續,是該函數對象的[[scope]]屬性(全局實行環境沒有後續)。

函數對象

在一個函數被定義時,會建立這個函數對象的[[scope]]屬性,指向這個函數的外圍。

運動對象

在一個函數被調用時,會建立一個運動對象,起首將該函數的形參和實參(arguments)增加進該運動對象,然後增加函數體內聲明的變量和函數(提早聲明,在剛進入該函數實行環境時,值為undefined),這個運動對象將作為該函數實行環境作用域鏈的最前端。
關於JS的提早聲明機制,我們舉個例子證實一下:

function add (num1){
    console.log(num2);
    var num3 = 4;
    return num1 + num2;
}
add(1); //undefined 5

上述代碼中,我們在變量聲明前運用它,卻沒有跑出ReferenceError,申明函數實行時,一最先,num2就已聲清楚明了。

內部環境能夠經由歷程作用域鏈接見一切的外部環境,但外部環境不能接見內部環境中的任何變量和函數。這些環境之間的聯絡是線性、有序次的。每一個環境都能夠向上搜刮作用域鏈,以查詢變量和函數名;但任何環境都不能經由歷程向下搜刮作用域鏈而進入另一個實行環境。
我們舉一個例子,趁便明白一下前面的觀點:

var num = 10;
function add (num1, num2) {
  function preAdd(num) {
    var pre = 1;
    return pre  + num;
  num1 = preAdd(num1);
  var result = num1 + num2 + num;
  return result;
}
add(10, 20);

《Javascript 變量、作用域和內存題目》

最先實行add()時,起首建立一個實行上下文,然後建立一個運動對象,將arguments num1 num2 pre preAdd() result保存在運動對象中,並將運動對象放在作用域鏈的前端,實行上下文獲得add保存的[[scope]],並將其放入作用域鏈的後端,然後實行到preAddpreAdd建立一個實行上下文,並壓入棧頂,建立一個運動對象,保存arguments num pre ,放在作用域鏈的前端,獲得preAdd[[scope]],放入作用域鏈的後端。當編譯器最先剖析pre時,起首從preAdd作用域鏈的前端最先找,找到了馬上住手。當編譯器最先剖析result = num1 + num2 + num,因為在add的作用域鏈前端(局部變量)中沒有該變量,因此繼承在作用域後端中尋覓,並終究在全局變量中找到了num

延伸作用域鏈

with

在塊作用域內,將指定變量放在作用域鏈的前端

try-catch

建立一個新的變量對象,个中包括的是被拋出的毛病對象的聲明,將這個對象放在作用域鏈的最前端,catch實行完畢后,作用域鏈恢復。

沒有塊級作用域

ECMAScript中沒有塊級作用域,因此塊的實行環境與其外部的實行環境雷同。

聲明變量

運用var 聲明的變量會自動被增加到最接近的環境中。假如初始化變量時沒有運用var 聲明,該變量會自動被增加到全局環境(嚴厲情勢下,如許寫會拋錯)。

查詢標識符

當對一個變量舉行讀取或修正操縱時,我們起首要搜刮到它,搜刮的遞次如圖:
標識符剖析是沿着作用域鏈一級一級地搜刮標識符的歷程。搜刮歷程一直從作用域鏈的前端最先,然後逐級地向後回溯,直至找到標識符為止(假如找不到標識符,通常會致使毛病發作)。

機能

渣滓網絡

Javascript具有自動渣滓網絡機制,周期性地接納那些不再運用的變量,並開釋其佔用的內存。

標記消滅(mark-and-sweep)

這是Javascript中最經常使用的渣滓網絡體式格局,當變量進入環境時,將其標記為“進入環境”,脫離環境時,標記為“脫離環境”。理論上,不能夠接納標記為“進入環境”的變量。
《Javascript 變量、作用域和內存題目》

能夠運用任何體式格局來標記變量。比方,能夠經由歷程翻轉某個特別的位來紀錄一個變量什麼時候進入環境,或許運用一個“進入環境的”變量列表及一個“脫離環境的”變量列表來跟蹤哪一個變量發作了變化。說到底,怎樣標記變量實在並不主要,關鍵在於採用什麼戰略。

援用計數(reference counting)

不太罕見,跟蹤紀錄每一個值被援用的次數。
當聲清楚明了一個變量並將一個援用範例值賦給該變量時,則這個值的援用次數就是1。假如同一個值又被賦給另一個變量,則該值的援用次數加1。相反,假如包括對這個值援用的變量又獲得了別的一個值或當它們的性命期完畢的時刻,要給它們所指向的對象的援用計數減1。當這個值的援用次數變成0 時,則申明沒有辦法再接見這個值了,因此就能夠將其佔用的內存空間接納返來。

var a = new Cat(); // 1
var b = a; // 2
var c = b; // 3
b = new Dog(); // 2
c = new Fox(); // 1
a = new Object(); // 0

《Javascript 變量、作用域和內存題目》

如許看起來,援用計數法好像沒什麼題目,但是,當碰到輪迴援用時,就跪了。。。

var a = new Object(); //a指向的Object的援用次數+1
var b = new Object(); //b指向的Object的援用次數+1
a.another = b; //b指向的Object的援用次數+1
b.another = a; //a指向的Object的援用次數+1

此時,兩個對象的援用次數都為2,用於都不會變成0,永久都不會被GC,糟蹋內存。
因為援用計數存在上述題目,因此早在Navigator 4.0就摒棄了這一戰略,但輪迴援用帶來的貧苦卻依舊存在。
IE 中有一部分對象並非原生JavaScript 對象。比方,BOM 和DOM 中的對象就是運用C++以COM(Component Object Model,組件對象模子)對象的情勢完成的,COM的渣滓接納戰略是援用計數法,因此只需涉及到COM對象,就會存在輪迴援用的題目,舉一個例子:

var element = document.getElementById("some_element");
var myObject = new Object();
myObject.element = element;
element.someObject = myObject;

IE9 把BOM 和DOM 對象都轉換成了真正的JavaScript 對象。如許,就避免了
兩種渣滓網絡算法並存致使的題目。

治理內存

因為體系分配給瀏覽器的內存比較小(比桌面運用小),而內存限定勢必會影響網頁機能,因此Javascript中,優化內存佔用是一個必要的題目,最好體式格局就是只保存必要的數據。局部變量會在脫離實行環境后自動消除援用,然後被GC,因此我們只需在不再須要某個全局變量時,將其設為null,來消除它對內存的援用(即消除援用dereferencing),適用於大多數全局變量和全局對象的屬性。
針對上一節的例子,我們能夠運用一樣的要領:

myObject.element = null;
element.someObject = null
    原文作者:Sue1024
    原文地址: https://segmentfault.com/a/1190000014756485
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞