亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

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

JohnLui / 1500人閱讀

摘要:保存在堆內存中。因此改變一方,另一方也會發(fā)生相應的改變。作用域鏈當代碼在一個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈,以保證對執(zhí)行環(huán)境有權訪問的所有變量和函數(shù)的有序訪問。

基本類型和引用類型的值

基本類型值:簡單的數(shù)據(jù)段 ,五種基本類型(Number Boolean String Null Undefined)的值都是基本類型值,基本類型的值在內存中大小固定,因此保存在棧內存中。
引用類型值:可能由多個值構成的對象。不能操作引用類型的內存空間。保存在堆內存中。

動態(tài)的屬性
引用類型值

我們可以為引用類型的值添加、修改、刪除屬性和方法,比如:

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時,只是創(chuàng)建了一個指針副本,這個指針副本與obj1指向同一個保存在堆內存中的對象。因此改變一方,另一方也會發(fā)生相應的改變。

傳遞參數(shù)
實參與形參
var num = 2
function add(num1, num2) {
  return num1 + num2;
}
add(1, num);

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

按值傳遞

ECMAScript 中所有函數(shù)的參數(shù)都是按值傳遞的,把函數(shù)外部的值復制給函數(shù)內部的參數(shù),就和把值從一個變量復制到另一個變量一樣(無論是基本類型還是引用類型)。

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中函數(shù)的參數(shù)都是按值傳遞的呢?
首先基本數(shù)據(jù)類型,全局變量num復制自身給參數(shù)num,二者是完全獨立的,改動不會相互影響。
關于對象,我們似乎看到了函數(shù)內部對obj1對象屬性的改動反應到了函數(shù)外部,而obj2重新賦值為另一個對象卻沒有反應到外部,這是為什么呢?書中解釋得有點簡單,筆者找了一下資料,原來傳入對象的時候,其實傳入的是對象在內存中的地址,當在函數(shù)中改變對象的屬性時,是在同一個區(qū)域進行操作,所以會在函數(shù)外反映出來,然而,如果對這個局部變量重新賦值,內存中的地址改變,就不會對函數(shù)外的對象產生影響了,這種思想稱為 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
檢測類型
哪種基本數(shù)據(jù)類型

使用typeof可以辨認String Number Undefined Boolean Object還有函數(shù)。

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測試基本數(shù)據(jù)類型時,用于返回false。

執(zhí)行環(huán)境和作用域 執(zhí)行環(huán)境(execution context)

定義了變量或函數(shù)有權訪問的其他數(shù)據(jù)。每個執(zhí)行環(huán)境都有一個與之關聯(lián)的變量對象(variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。

全局執(zhí)行環(huán)境

全局執(zhí)行環(huán)境是最外圍的一個執(zhí)行環(huán)境。在Web 瀏覽器中,全局執(zhí)行環(huán)境被認為是window 對象,因此所有全局變量和函數(shù)都是作為window 對象的屬性和方法創(chuàng)建的。某個執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后,該環(huán)境被銷毀,保存在其中的所有變量和函數(shù)定義也隨之銷毀(全局執(zhí)行環(huán)境直到應用程序退出,例如關閉網頁或瀏覽器時才會被銷毀)。

函數(shù)執(zhí)行環(huán)境

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

作用域鏈(scope chain)

當代碼在一個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈,以保證對執(zhí)行環(huán)境有權訪問的所有變量和函數(shù)的有序訪問。作用域鏈的前端,始終都是當前執(zhí)行的代碼所在環(huán)境的變量對象,對于全局執(zhí)行環(huán)境,就是window對象,對于函數(shù)執(zhí)行環(huán)境,就是該函數(shù)的活動對象。作用域鏈的后續(xù),是該函數(shù)對象的[[scope]]屬性(全局執(zhí)行環(huán)境沒有后續(xù))。

函數(shù)對象

在一個函數(shù)被定義時,會創(chuàng)建這個函數(shù)對象的[[scope]]屬性,指向這個函數(shù)的外圍。

活動對象

在一個函數(shù)被調用時,會創(chuàng)建一個活動對象,首先將該函數(shù)的形參和實參(arguments)添加進該活動對象,然后添加函數(shù)體內聲明的變量和函數(shù)(提前聲明,在剛進入該函數(shù)執(zhí)行環(huán)境時,值為undefined),這個活動對象將作為該函數(shù)執(zhí)行環(huán)境作用域鏈的最前端。
關于JS的提前聲明機制,我們舉個例子證明一下:

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

上述代碼中,我們在變量聲明前使用它,卻沒有跑出ReferenceError,說明函數(shù)執(zhí)行時,一開始,num2就已經聲明了。

內部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境,但外部環(huán)境不能訪問內部環(huán)境中的任何變量和函數(shù)。這些環(huán)境之間的聯(lián)系是線性、有次序的。每個環(huán)境都可以向上搜索作用域鏈,以查詢變量和函數(shù)名;但任何環(huán)境都不能通過向下搜索作用域鏈而進入另一個執(zhí)行環(huán)境。
我們舉一個例子,順便理解一下前面的概念:

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);

開始執(zhí)行add()時,首先創(chuàng)建一個執(zhí)行上下文,然后創(chuàng)建一個活動對象,將arguments num1 num2 pre preAdd() result保存在活動對象中,并將活動對象放在作用域鏈的前端,執(zhí)行上下文取得add保存的[[scope]],并將其放入作用域鏈的后端,然后執(zhí)行到preAdd,preAdd創(chuàng)建一個執(zhí)行上下文,并壓入棧頂,創(chuàng)建一個活動對象,保存arguments num pre ,放在作用域鏈的前端,取得preAdd[[scope]],放入作用域鏈的后端。當編譯器開始解析pre時,首先從preAdd作用域鏈的前端開始找,找到了立刻停止。當編譯器開始解析result = num1 + num2 + num,由于在add的作用域鏈前端(局部變量)中沒有該變量,因此繼續(xù)在作用域后端中尋找,并最終在全局變量中找到了num

延長作用域鏈
with

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

try-catch

創(chuàng)建一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明,將這個對象放在作用域鏈的最前端,catch執(zhí)行結束后,作用域鏈恢復。

沒有塊級作用域

ECMAScript中沒有塊級作用域,因此塊的執(zhí)行環(huán)境與其外部的執(zhí)行環(huán)境相同。

聲明變量

使用var 聲明的變量會自動被添加到最接近的環(huán)境中。如果初始化變量時沒有使用var 聲明,該變量會自動被添加到全局環(huán)境(嚴格模式下,這樣寫會拋錯)。

查詢標識符

當對一個變量進行讀取或修改操作時,我們首先要搜索到它,搜索的順序如圖:
標識符解析是沿著作用域鏈一級一級地搜索標識符的過程。搜索過程始終從作用域鏈的前端開始,然后逐級地向后回溯,直至找到標識符為止(如果找不到標識符,通常會導致錯誤發(fā)生)。

性能 垃圾收集

Javascript具有自動垃圾收集機制,周期性地回收那些不再使用的變量,并釋放其占用的內存。

標記清除(mark-and-sweep)

這是Javascript中最常用的垃圾收集方式,當變量進入環(huán)境時,將其標記為“進入環(huán)境”,離開環(huán)境時,標記為“離開環(huán)境”。理論上,不可以回收標記為“進入環(huán)境”的變量。

可以使用任何方式來標記變量。比如,可以通過翻轉某個特殊的位來記錄一個變量何時進入環(huán)境,或者使用一個“進入環(huán)境的”變量列表及一個“離開環(huán)境的”變量列表來跟蹤哪個變量發(fā)生了變化。說到底,如何標記變量其實并不重要,關鍵在于采取什么策略。
引用計數(shù)(reference counting)

不太常見,跟蹤記錄每個值被引用的次數(shù)。
當聲明了一個變量并將一個引用類型值賦給該變量時,則這個值的引用次數(shù)就是1。如果同一個值又被賦給另一個變量,則該值的引用次數(shù)加1。相反,如果包含對這個值引用的變量又取得了另外一個值或當它們的生命期結束的時候,要給它們所指向的對象的引用計數(shù)減1。當這個值的引用次數(shù)變成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

這樣看起來,引用計數(shù)法似乎沒什么問題,然而,當遇到循環(huán)引用時,就跪了。。。

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

此時,兩個對象的引用次數(shù)都為2,用于都不會變?yōu)?,永遠都不會被GC,浪費內存。
由于引用計數(shù)存在上述問題,因此早在Navigator 4.0就放棄了這一策略,但循環(huán)引用帶來的麻煩卻依然存在。
IE 中有一部分對象并不是原生JavaScript 對象。例如,BOM 和DOM 中的對象就是使用C++以COM(Component Object Model,組件對象模型)對象的形式實現(xiàn)的,COM的垃圾回收策略是引用計數(shù)法,因此只要涉及到COM對象,就會存在循環(huán)引用的問題,舉一個例子:

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

IE9 把BOM 和DOM 對象都轉換成了真正的JavaScript 對象。這樣,就避免了
兩種垃圾收集算法并存導致的問題。

管理內存

由于系統(tǒng)分配給瀏覽器的內存比較?。ū茸烂鎽眯。?,而內存限制勢必會影響網頁性能,因此Javascript中,優(yōu)化內存占用是一個必要的問題,最佳方式就是只保留必要的數(shù)據(jù)。局部變量會在離開執(zhí)行環(huán)境后自動解除引用,而后被GC,因此我們只需在不再需要某個全局變量時,將其設為null,來解除它對內存的引用(即解除引用dereferencing),適用于大多數(shù)全局變量和全局對象的屬性。
針對上一節(jié)的例子,我們可以使用同樣的方法:

myObject.element = null;
element.someObject = null

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://www.ezyhdfw.cn/yun/94769.html

相關文章

  • JavaScript高級程序設計(第3版)》——變量、作用域和內存問題(四)

    摘要:執(zhí)行環(huán)境的類型有兩種全局全局執(zhí)行環(huán)境局部函數(shù)執(zhí)行環(huán)境每個環(huán)境都可以向上搜索作用域鏈,以查詢變量和函數(shù)名但任何環(huán)境都不能通過向下搜索作用域鏈而進入另一個執(zhí)行環(huán)境。內部可通過作用域鏈訪問外部,外部不能訪問內部。 變量、作用域和內存問題 ECMAScript 數(shù)據(jù)類型 基本類型(5種): Undefined,Null,Boolean,Number,String typeof() 檢測...

    YacaToy 評論0 收藏0
  • JavaScript紅寶書筆記(四)---變量作用域和內存問題

    摘要:在操作對象時,實際上是在操作對象的引用而不是實際的對象。為此,引用類型的值是按引用訪問的。標記清除是目前主流的垃圾收集算法,這種算法的思想是給當前不使用的值加上標記,然后再回收其內存 1.在操作對象時,實際上是在操作對象的引用而不是實際的對象。為此,引用類型的值是按引用訪問的。 2.當從一個變量向另一個變量復制引用類型的值時,兩個變量實際上將引用同一個對象,因此,改變其中一個變量,就會...

    imtianx 評論0 收藏0
  • JavaScript變量、作用域和內存問題

    摘要:全局變量是最外圍的一個執(zhí)行環(huán)境,代碼在環(huán)境中執(zhí)行,會創(chuàng)建一個作用域鏈,用途是保證對執(zhí)行環(huán)境有權訪問所有變量和函數(shù)的有序訪問。作用域鏈中最后一個對象始終是全局執(zhí)行環(huán)境。內部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境,外部則不能訪問內部。 1、基本類型和引用類型的值 * 基本類型 : 指的是簡單的數(shù)據(jù)段,五種基本類型是按值訪問的,可以直接操作保存在變量中實際的值。 * 引用類型 : 指那些可能...

    Dr_Noooo 評論0 收藏0
  • JS學習筆記(第4章)(變量作用域和內存問題

    摘要:具體來說就是當執(zhí)行流進入下列任何一個語句時,作用域鏈就會得到加長語句的塊和語句。這兩個語句都會在作用域鏈的前端添加一個變量對象。對來說,會將指定的對象添加到作用域鏈中。 1. 基本類型和引用類型的值 JavaScript變量可以用來保存兩種類型的值:基本類性值和引用類性值?;绢愋椭翟醋砸韵?種基本數(shù)據(jù)類型:Undefined、Null、Boolean、Number和String?;?..

    linkin 評論0 收藏0
  • 深入javascript——作用域和閉包

    摘要:注意由于閉包會額外的附帶函數(shù)的作用域內部匿名函數(shù)攜帶外部函數(shù)的作用域,因此,閉包會比其它函數(shù)多占用些內存空間,過度的使用可能會導致內存占用的增加。 作用域和作用域鏈是javascript中非常重要的特性,對于他們的理解直接關系到對于整個javascript體系的理解,而閉包又是對作用域的延伸,也是在實際開發(fā)中經常使用的一個特性,實際上,不僅僅是javascript,在很多語言中都...

    oogh 評論0 收藏0
  • 變量作用域和內存問題

    摘要:變量作用域和內存問題基本類型和引用類型的值基本類型就是簡單的數(shù)據(jù)段種值類型,而引用類型就是對象操控對象的引用。但是不但能訪問自己的變量,也能訪問和全局作用域下的變量。延長作用域鏈相當于創(chuàng)造了一個新的變量對象在當前作用域的上方。 變量作用域和內存問題 1.基本類型和引用類型的值 基本類型就是簡單的數(shù)據(jù)段(5種值類型),而引用類型就是對象(操控對象的引用)。 1.1復制變量值 引用類型實際...

    wuyangchun 評論0 收藏0

發(fā)表評論

0條評論

JohnLui

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<