作用域和內存題目

基礎範例和援用範例的值

ECMAscript 變量能夠包括兩種差別的數據範例的值,
基礎範例值和援用範例值

基礎範例指的是簡樸的數據段,而援用範例指那些能夠有多個值組成的對象

  • 基礎數據範例 :undefinedNullBooleanNumberstring.這五種基礎數據範例是按值接見的,由於能夠操縱存在變量中的現實的值

動態屬性

  • 定義基礎範例的體式格局和援用範例的體式格局是相似的,建立一個變量併為該變量賦值。
  • 關於援用範例的值,能夠為其增加屬性和要領,也能夠轉變和刪除其屬性和要領
var person = new Object();
person.name = sunny;
console.log(person.name); // sunny

上面代碼建立了一個對象並將其保留了在變量person中,然後為該對象建立一個名為name的屬性。假如這個對象不被燒毀或許這個屬性不被刪除,則這個屬性將一向存在。

  • 然則,我們不能給基礎範例的值增加屬性
var name = "sunny";
name.age = 18;
consolem.log(name.age); // undefined

上面代碼為字符串name定義了一個名為age的屬性,併為該屬性賦值18,然則鄙人一行接見的這個屬性的時刻,發現該屬性不見了,這申明只能給援用範例值動態增加屬性

複製變量值

  • 假如從一個變量像另一個變量複製基礎範例的值,會在變量對象上建立一個新值,然後把該值複製到新變量分派的位置上
var num1 = 10;
var num2 = num1; 
num1 = 20;
connsole.log(num1,num2) // 20 10

num1中保留的值是10;當運用num1的值來初始化num2時,num2也保留了值10,但num2中的10與num1中的10是完整自力的,該值只是num1中10的一個副本,今後,這兩個變量能夠完整介入任何操縱而不會相互影響

  • 當從一個變量向另一個變量複製援用範例的值時,一樣也會將存儲在變量對象中的值複製一份放到為新變量分派的空間中。差別的是,這個值的副本現實上是一個指針,而這個指針指向存儲在堆中的一個對象,複製操縱終了以後,這兩個變量現實大將援用同一個對象,因而轉變个中一個變量,就會影響另一個變量
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "sunny";
console.log(obj2.name); // sunny;
obj2.name = "zhang";
console.log(obj1.name); // zhang;

變量obj1保留了一個對象新實例,這個值被複制到obj2中,所以obj1和obj2都指向同一個對象,當obj1或許obj2增加nane屬性的時刻,這個屬性都是存儲在同一個對象。都能夠經由歷程obj1和obj2去接見。

通報參數

函數中的參數都是按值通報的,基礎範例值的通報犹如基礎範例變量的複製一樣,而援用範例值的通報,則犹如援用範例變量的複製一樣。被通報的值會被複制到一個部分變量(即定名參數,或許用ECMAscript的觀點來講,就是
arguments對象中的一個元素)。

  • 在向參數通報援用範例的值時,會把這個值的內存中的地點複製給一個部分變量,因而這個部分變量的變化會反應在函數的外部。
function addTen(){
    num += 10;
    return num; 
}
var count = 20;
var result = addTen(count);
console.log(count); // 20  沒有變化
console.log(result); // 30 返回 num += 10的效果

上面變量count做為參數被通報給函數,這個變量的值是20;數值20被複制到參數num以便在addTen()函數中運用,在函數內部num+=10,但這一變化不會影響函數外部的count變量。參數nun和count互不熟悉,它們僅僅具有想同的值。

  • 運用對象通報參數。
function setName(obj){
    obj.name = "sunny";
}
var person = new Object();
setName(person);
console.log(person.name); // sunny

上面代碼中建立一個對象,並將其保在了變量person中,在這個函數內部,參數obj和變量person援用的都是同一個對象,當在函數內部為obj增加name屬性后,函數外部的person也會有所反應。由於person指向的對象在堆內存中只要一個,而且照樣全局對象。

  • 很多人毛病的以為 (在部分作用域中修正的對象會在全局作用域中反應出來,就申明參數是按援用通報的),為了證實對象是按值通報的,下面修正一下代碼
function setName(obj) {
    obj.name = "sunny";
    obj = new Object(); // 從新定義一個新對象
    obj.name = "zhang";
}
var person = new Object();
setName(person);
console.log(person.name); // sunny

obj從新定義一個對象,並設置name屬性 值為 zhang,
假如person是按援用通報的話,那末person就會自動修正為指向其name屬性值為zhang 的新對象,然則接見person.name時,值仍然是sunny,這申明縱然在函數內部修正了參數的值,但原始的援用堅持穩定,
現實上,在函數內部重寫obj時,這個變量援用就是一個部分變量了。而這個部分變量在實行終了后馬上被燒毀

檢測範例

要檢測一個變量是否是基礎數據範例,
typeof操縱符是最好的東西。
typeof操縱符是肯定一個變臉是字符串、數值、布爾值、Undefined的最好東西,假如一個變量的值是null,則
typeof操縱符會返回object

var name = "sunny"; // string範例
var age = 18;  // Number範例
var bool = true;  // boolean
var unde;   // Undefined
var n = null;  // null
var person = new Object();  // 對象實例

console.log(typeof(name));  // string
console.log(typeof(age));  // number
console.log(typeof(bool));  // boolean
console.log(typeof(unde));  // undefined
console.log(typeof(n)); // object
console.log(typeof(person)); // object
  • 雖然在檢測基礎範例的時刻,typeof操縱符是異常得力的助手,然則在檢測援用範例的時刻,這個操縱符的作用不大。想曉得是什麼範例的對象,運用instanceof操縱符

假如變量是給定援用範例(依據它的原型鏈來辨認)的實例,那末
instanceof就會返回 true ;

var name = "sunny";
var my_array = new Array();
var my_obj = new Object();

console.log(my_array instanceof Array); // true
console.log(my_obj instanceof Object); // true
console.log(name instanceof Object); // false
console.log(my_array instanceof Object); // true
console.log(my_obj instanceof Array); // false

一切援用範例的值都是Object的實例,在檢測一個援用範例和Object組織函數時,
instanceof操縱符一直會返回 true。假如運用
instanceof操縱符檢測基礎範例的值一直會返回false,由於基礎範例不是對象

實行環境及作用域

實行環境定義了變量或函數有權接見的其他數據。決議它們各自的行動,每一個實行環境都有一個與之關聯的變量對象,環境中定義的一切變量和函數都保留在這個對象中。

  • 全局實行環境是最外圍的一個實行環境,在web瀏覽器中,全局實行環境被以為是window對象。因而一切全局變量和函數都是作為window對象的屬性和要領建立的。某個實行環境中的一切代碼實行終了后,該環境被燒毀,保留在个中的一切變量和函定義也隨之燒毀(全局實行環境直到應用程序退出后–比方封閉頁面或退出瀏覽器時才會被燒毀)。
  • 當代碼在一個黃靜中實行時,會建立變量對象的一個作用域鏈,作用域鏈的用處是保證對實行環境有權接見的一切變量和函數的有序接見。假如這個環境是函數,則將其運動對象作為變量對象。運動對象在最最先時只包括一個變量,即arguments對象(這個對象在全局環境中是不存在的)。
  • 全局實行環境的變量對象一直都是作用域鏈中的末了一個對象。標識符剖析是沿着作用域鏈一級一級地搜刮標識符的歷程,搜刮歷程一直從作用域鏈的前端最先,然後逐級地向後回溯,曉得找到標識符為止(假如找不到標識符,通常會致使毛病)。
var color = "blue";
function changecolor(){
    if(color === "blue"){
        color = "red";
    }
    else {
        color = "blue";
    }
}
changecolor();
console.log("color is now" + color); // red

在簡樸的例子中,函數changecolor()的作用域鏈包括着兩個對象,它子級的變量對象(个中定義這
arguments)和全局環境變量對象。能夠在函數內部接見變量color,就是由於能夠在這個作用域鏈中找到它

var color  = "blue";
function changecolor(){
    var anothercolor = "red";
    function swapcolor(){
        var tempcolor = anothercolor;
        anothercolor = color;
        color = tempcolor
        //  這裡能夠接見到color 、anothercolor、 tempcolor
    }
    // 這裡能夠接見到//  這裡能夠接見到color 、anothercolor,然則不能接見 tempcolor
}
// 這裏只能接見color;
changecolor();

以上代碼共觸及3個實行環境:全局環境,changecolor()的部分變量環境和swapcolor()的部分變量環境,內部環境能夠經由歷程作用域鏈接見一切的外部環境,但外部壞境不能接見內部環境中的任何變量和函數。每一個環境都能夠向上搜刮作用域鏈,以查詢變量和函數名,但任何環境都不能經由歷程向下搜刮作用域鏈而進入另一個實行環境。

延伸作用域鏈

有些語句能夠在作用域鏈的前端暫時增加一個變量對象,該變量對象會在代碼實行后被移除。具體來講就是當實行流進入以下任何一個語句時,作用域鏈就獲得加長。

  1. try-catch語句中的catch

catch語句會建立一個新的變量對象。个中包括的是拋出的毛病對象的聲明。

try{
    adddlert("Welcome guest!");
    }
catch(err){
    //  建立一個err對象
    console.log(err.message); 
}
  1. with語句

將指定的對象增加到作用域鏈中。

var s = "?debug=true";
with(location){
    var url = href + s ; // 現實援用的是location.href;
}
console.log(url); // 輸出 location對象下的href屬性的值。
// 等同於  console.log(location.href);

沒有塊級作用域

  • javascript中,if語句中的變量聲明會增加到當前的實行環境中。
if(true){
    var color = "blue";
}
console.log(color); // blue
  • javascript中,for語句建立的變量縱然在變量縱然在輪迴實行終了后,也依舊會存在與輪迴環境外部的實行環境中
for (var i = 0; i < 10; i++){    
}
console.log(i);  // 10

1.變量聲明

  • 運用var聲明的變量會自動被增加到最接近的環境當中,在函數內部,最接近的環境就是函數的部分環境,在with語句中,最接近的環境是函數環境。假如初始化變量沒有運用var聲明,該變量會自動被增加到全局環境中
function add(num1,num2){
    var sum = num1 + num2;
    return sum;
}
var result = add(20,30);
console.log(result); // 50
console.log(sum); // sum is not defined
  • 運用var定義的sum變量,只能在函數add內接見到,假如疏忽var關鍵字定義sum,則能夠在函數外部接見獲得
function add(num1,num2){
    sum = num1 + num2;
    return sum;
}
var result = add(20,30);
console.log(result); // 50
console.log(sum); // 50

變量被初始化賦值的時刻沒有運用var關鍵字,則該變量會增加到全局環境中。

渣滓網絡

  • javascript 中最長用的渣滓網絡體式格局是標記消滅(mark-and-sweep)。當變量進入環境(比方,在函數中聲明一個變量)時,就將這個變量標記為”進入環境”。從邏輯上講,永久不能開釋進入環境的變量所佔用的內存,當變量離開環境時,則將其標記為”離開環境”

援用計數

  • 經由歷程跟蹤每一個值被援用的次數來消滅所佔用的內存,當聲明一個變量並將一個援用範例賦值給該變量的時刻,則這個值的援用次數就是1,假犹如一個值又被賦值給另一個變量,則該值的援用次數加1,相反,假如包括這個值援用的變量又取得了另一個值,則援用次數減1,當援用次數為0時,就將其佔用的內存空間接納返來。

機能題目

  • 渣滓網絡器是周期性運轉的,而且假如為變量分派的內存數目很可觀,那末接納工作量也是想當大的

有是瀏覽器中能夠直接觸發渣滓網絡歷程,(但我們不發起這麼做),在IE中,挪用window.CollectGarbage()要領會馬上實行渣滓網絡,在opera7及更高版本中,挪用window.opera.Collect()也會啟動渣滓網絡案例

治理內存

一旦數據不再有效,就將其值設置為null來開釋其援用—這個做法平常叫“消除援用”,這個做法適用於大多數全局變量和全局對象的屬性。部分變量會在它們離開實行環境時自動被消除援用。
不過,消除一個值的援用並不意味着自動接納該值所佔用的內存,而是讓值離開實行環境,以更渣滓網絡器下次運轉時將其接納

function crean(name){
    var localperson = new Object();
    localperson.name = name;
    return localperson;
}
var globalperson = crean("sunny");
console.log(globalperson); // sunny
globalperson = nill;  // 手動消除 globalperson 的援用
    原文作者:sunny
    原文地址: https://segmentfault.com/a/1190000014967236
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞