亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

細說JavaScript中的變量,作用域和垃圾回收

 更新時間:2022年11月11日 11:20:20   作者:hellocoder2029  
這篇文章主要和大家介紹一下JavaScript中的變量,作用域和垃圾回收的定義與使用,文中的示例代碼講解詳細,對我們學習JavaScript有一定的幫助,需要的可以參考一下

在 JavaScript 中,數(shù)據(jù)類型可分為基本類型和引用類型,

基本類型有六種:Null,Undefined,String,Boolean,Number,Symbol;

而引用類型就是傳說中的 Object 了。

其中基本類型是按值傳遞,而引用類型的值是按引用訪問的,所以在操作對象時,實際上是在操作對象的引用而不是實際的對象 ( ps:在為對象添加屬性時,操作的是實際的對象 )。

關(guān)于基本類型和引用類型的不同,大概有以下幾點:

1、引用類型是動態(tài)的屬性,而基本類型不是。

對于引用類型,我們可以為其添加、刪除屬性和方法,但不能給基本類型的值添加屬性:

// 基本類型
var name = 'Fly_001';
name.age = 22;
alert(name.age); // undefined;

// 引用類型
var person = new Object();
person.name = 'Fly_001';
alert(person.name); // 'Fly_001';

2、復(fù)制的方式不同。

如果從一個變量向另一個變量復(fù)制基本類型的值,會將值復(fù)制到為新變量分配的位置上:

var num1 = 5;
var num2 = num1;

當使用 num1 的值來初始化 num2 時,num2 中也保存了值5,但該值只是 num1 中 5 的一個副本,兩個變量不會互相影響。

當從一個變量向另一個變量復(fù)制引用類型的值時,傳遞的是一個指針,其指向存儲在堆中的一個對象,在復(fù)制結(jié)束后,兩個變量實際上將引用同一個對象,改變其中一個變量就會影響另一個變量:

var obj1 = new Object();
var obj2 = obj1;
obj1.name = 'Fly_001';
alert(obj2.name); // 'Fly_001';

3、傳遞參數(shù)的特點。

這是一個容易困惑的點 。

ECMAScript 中所有函數(shù)的參數(shù)都是按值傳遞的。也就是說,把函數(shù)外部的值復(fù)制給函數(shù)內(nèi)部的參數(shù),就和把值從一個變量復(fù)制到另一個變量一樣。基本類型值的傳遞如同基本類型變量的復(fù)制一樣,而引用類型的傳遞,則如同引用類型變量的復(fù)制一樣,這一點確實會引起很多小伙伴的爭議,歡迎討論~

  • 在向參數(shù)傳遞基本類型的值時,被傳遞的值會被復(fù)制給一個局部變量( 即 arguments 對象中的一個元素 )。
  • 在向參數(shù)傳遞引用類型的值時,會把這個值在內(nèi)存中的地址復(fù)制給一個局部變量,因此該局部變量的變化會反映到函數(shù)的外部:
function addTen(num) {
    num += 10;
    return num;
}
var count = 20;
var result = addTen(count);
alert(count); // 20,木有變化;
alert(result); // 30

function setNmae(obj) {
    obj.name = 'Fly_001';
}
var person = new Object();
setName(person);
alert(person.name); // 'Fly_001';

在上面代碼中我們創(chuàng)建了一個對象,并將其保存在了變量 person 中。然后,這個對象被傳遞到 setName () 函數(shù)中就被復(fù)制給了 obj,在這個函數(shù)內(nèi)部,obj 和 person 引用的是同一個對象。

很多小伙伴會認為該參數(shù)是按引用傳遞的,為了證明對象是按值傳遞的,再看下這個修改過的代碼:

function setName(obj) {
    obj.name = 'Fly_001';
    obj = new Object();
    obj.name = 'juejin';
}

var person  = new Object();
setName(person);
alert(person.name); // 'Fly_001';

如果 person 是按引用傳遞的,那么 person 就會自動被修改為指向其 name 屬性為 ‘juejin’ 的新對象。但接下來再訪問 person.name 時仍然顯示 ‘Fly_001’,這說明即使在函數(shù)內(nèi)部修改了參數(shù)的值,但原始的引用仍保持不變。( 實際上,當在函數(shù)內(nèi)部重寫 obj 時,這個變量引用的就是一個局部對象了,其將在函數(shù)執(zhí)行完畢后立即被銷毀。)

4、檢測類型的操作符不同。

檢測基本類型適宜用 typeof 操作符

alert(typeof 'Fly_001'); // 'string';
alert(typeof []); // 'object';

因為 typeof 操作符的返回值為 'undefined','string','boolean','number','symbol','object','function' 其中之一。

它可以很友好地指出某一具體基本類型,而對于引用類型則籠統(tǒng)地返回 'object'( typeof 對 數(shù)組、正則、null 都會返回 'object' )。

在檢測引用類型時更適合用 instanceof 操作符:

result = varible instanceof constructor;

如果變量是給定引用類型的實例( 根據(jù)它的原型鏈來識別 ),那 instanceof 操作符將會返回 true。

執(zhí)行環(huán)境及作用域

下面聊下 JavaScript 中很重要的一個概念 —— 執(zhí)行環(huán)境。

JS 中每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象,在 Web 瀏覽器中,全局執(zhí)行環(huán)境是 window 對象,因此所有全局變量和函數(shù)都是作為 window 對象的屬性和方法創(chuàng)建的。

某個執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后,該環(huán)境將會被銷毀,保存在其中的所有變量和函數(shù)定義也隨之銷毀,全局執(zhí)行環(huán)境直至網(wǎng)頁或瀏覽器關(guān)閉時才被銷毀( 如果存在閉包,情況又有所不同,會在后面幾篇提到 ,多謝 吳hr 指正)。

每個函數(shù)都有自己的執(zhí)行環(huán)境。當執(zhí)行流進入一個函數(shù)時,函數(shù)的環(huán)境就會被推入一個環(huán)境棧中。而在函數(shù)執(zhí)行之后,棧會將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境。

var color = 'blue';

function changeColor() {
    var anotherColor = 'red';

    function swapColors() {
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;

        // 這里可以訪問 color、anotherColor 和 tempColor;
    }

    swapColors();
    // 這里可以訪問 color 和 anotherColor,但不能訪問 tempColor;
}

changeColor();
// 這里只能訪問 color;

以上代碼共涉及 3 個執(zhí)行環(huán)境:全局環(huán)境、changeColor() 的局部環(huán)境和 swapColor() 局部環(huán)境。其中,內(nèi)部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境,但外部環(huán)境不能訪問內(nèi)部環(huán)境中的任何變量和函數(shù)。 這些環(huán)境之間的聯(lián)系是線性的、有次序的。每個環(huán)境可以向上搜索作用域鏈 ,以查詢變量和函數(shù)名;但任何環(huán)境都不能通過向下搜索作用域鏈而進入另一個執(zhí)行環(huán)境。

延長作用域鏈

雖然執(zhí)行環(huán)境的類型總共只有兩種 —— 全局和局部 (函數(shù)),但還是兩種辦法來延長作用域鏈~ 就是通過 try-catch 語句的 catch 塊和 with 語句。

這兩個語句都會在作用域鏈的前端添加一個變量對象。對 with 語句來說,會將指定的對象添加到作用域鏈中;對于 catch 語句來說,會創(chuàng)建一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明。

沒有塊級作用域

JavaScript 沒有塊級作用域經(jīng)常會導(dǎo)致理解上的困惑 。在其它類 C 的語言中,由花括號封閉的代碼塊都有自己的作用域,即執(zhí)行環(huán)境,但在 JavaScript 中卻不是這樣:

if (true) {
    var color = 'blue';
}

alert(color); // 'blue';

for (var i = 0; i < 10; i ++) {
    // dosomething
}

alert(i); // 10;

使用 var 聲明的變量會自動被添加到最接近的環(huán)境中。在函數(shù)內(nèi)部,最接近的環(huán)境就是函數(shù)的局部環(huán)境,若初始化變量時沒有使用 var 聲明,該變量會自動被添加到全局環(huán)境。( 創(chuàng)建塊范圍局部變量使用 let 關(guān)鍵字更方便 ):

function add(num1, num2) {
    var sum = num1 + num2;
    return sum;
}

var result = add(10, 20); // 30;
alert(sum); // 'sum is not defined';

在上面代碼中,雖然 sum 從函數(shù)中返回了,但在函數(shù)外部是訪問不到的。如果省略 var 關(guān)鍵字,這時 sum 是可以訪問到的( 不過在嚴格模式下,初始化未聲明的變量會報 'xxx is not defined' 錯 )。

模仿塊級作用域

雖然 js 沒有塊級作用域,但我們可以用匿名函數(shù)來模仿塊級作用域~,語法格式如下:

(function() {
    // 這里是塊級作用域;
}) ();

將函數(shù)聲明包含在一對圓括號里,表示它實際上是一個函數(shù)表達式,而緊隨其后的圓括號會立即調(diào)用這個函數(shù)。實際上就相當于:

var someFunction() {
    // 這里是塊級作用域;
};
someFunction();

同時因為 JavaScript 將 function 關(guān)鍵字當作一個函數(shù)聲明的開始,后面不能直接跟圓括號,而函數(shù)表達式后面可以跟圓括號,所以將函數(shù)聲明加上圓括號轉(zhuǎn)換成函數(shù)表達式。

無論在什么地方,只要臨時需要一些變量,就可以使用私有作用域:

function outputNumbers(count) {
    (function () {
        for (var i = 0; i < count; i ++) {
            alert(i);
        }
    }) ();

    alert(i); // 會導(dǎo)致錯誤,讀取不到 i;
}

因為在匿名函數(shù)中定義的任何變量,都會在執(zhí)行結(jié)束時立即銷毀,所以變量 i 只能在循環(huán)中使用。

查詢標識符

當在某個環(huán)境中為了讀取或?qū)懭攵靡粋€變量或函數(shù)名 ( 標識符 ),必須通過搜索來確定該它實際代表什么。

搜索過程從作用域的前端開始,向上逐級查找,如果存在一個局部的變量的定義,則停止搜索,即同名局部變量將覆蓋同名全局變量:

var color = 'blue';

function getColor() {
    var color = 'red'; // 局部變量;
    return color;
}

alert(getColor()); // 'red';
alert(window.color); // 'blue';

垃圾收集

JavaScript 具有自動垃圾收集機制,所以開發(fā)人員不必擔心內(nèi)存使用問題,是不是很開森 ,但最好還是了解下 。

首先我們來分析函數(shù)中局部變量的正常生命周期:局部變量只在函數(shù)執(zhí)行的過程中存在,函數(shù)執(zhí)行結(jié)束后就會釋放掉它們的內(nèi)存以供將來使用。所以 垃圾收集器必須跟蹤哪些變量有用、哪些變量沒用,具體到瀏覽器的實現(xiàn)有兩個策略:標記清除和引用計數(shù)

標記清除

此乃 JavaScript 中最常用的垃圾收集機制。

垃圾收集器在運行的時候會把存儲在內(nèi)存中的所有變量都加上標記,然后去掉環(huán)境中的變量及被環(huán)境中的變量引用的變量的標記,

在此之后還有標記的變量將被視為準備刪除的變量,因為環(huán)境中的變量已經(jīng)無法訪問到這些變量了。最后垃圾收集器完成內(nèi)存清除工作,銷毀那些帶標記的值并回收它們所占用的內(nèi)存空間。

引用計數(shù)

另一種出鏡率不高的垃圾收集策略是引用計數(shù)。

它主要跟蹤記錄每個值被引用的次數(shù),當某個值的引用次數(shù)為 0 時,則說明沒有辦法再訪問這個值了,因此就可以將其占用的內(nèi)存空間回收。

但引用計數(shù)會存在一個循環(huán)引用的問題:

function problem() {
    var objA = new Object();
    var objB = new Object();

    objA.someOtherObject = objB;
    objB.anotherObject = objA;
}

也就是說,在函數(shù)執(zhí)行完之后,objA 和 objB 還將繼續(xù)存在,因此它們的引用次數(shù)永遠不會是 0,假如這個函數(shù)被重復(fù)多次調(diào)用,就會導(dǎo)致大量內(nèi)存得不到回收 。

為了避免這樣的循環(huán)引用問題,最好在不使用它們的時候手動斷開連接:

objA.someOtherObject = null;
objB.anotherObject = null;

當垃圾收集器下次運行時,就會刪除這些值并回收它們所占用的內(nèi)存。

Tips:一旦數(shù)據(jù)不再有用,最好將其設(shè)為 null。

( 此條適合全局變量和全局對象的屬性,因為局部變量會在它們離開執(zhí)行環(huán)境時自動被解除引用 )。

ok,JavaScript 基礎(chǔ)的變量、作用域和垃圾回收咱就先講到這,下一篇會聊聊 JavaScript 面向?qū)ο蟮某绦蛟O(shè)計和函數(shù)表達式。

以上就是細說JavaScript中的變量,作用域和垃圾回收的詳細內(nèi)容,更多關(guān)于JavaScript變量 作用域 垃圾回收的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論