国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript學習筆記 - 變量、作用域與內存問題

lavnFan / 3063人閱讀

摘要:語句中的塊語句對來說,將會指定對象添加到作用域鏈中。在嚴格模式下,初始化未經聲明的變量會導致錯誤。查詢標識符搜索過程從作用域鏈的前端開始,向上逐級查詢與給定名字匹配的標識符。

本文記錄了我在學習前端上的筆記,方便以后的復習和鞏固。

4.1基本類型和引用類型的值

ECMAScript變量可能包含兩種不同數據類型的值:基本類型值和引用類型值。基本類型指的是簡單的數據段,而引用類型值指那些可能由多個值構成的對象。

數據類型:

基本類型值:Undefined、Null、Boolean、Number、String;

引用類型值,也就是對象類型:Object、Array、Function、Date等;

聲明變量時不同的內存分配

基本類型值:存儲在棧(stack)中的簡單數據段,它們的值直接存儲在變量訪問的位置。這是因為這些基本類型占據的空間是固定的,所以可以將它們存儲在較小的內存區域 - 棧中。這樣存儲更便于迅速查尋變量的值。

引用值:存儲在堆(heap)中的對象,也就是說,存儲在變量處的值是一個指針(point),指向存儲對象的內存地址。這是因為:引用值的大小會改變,所以不能把它放在中,否則會降低變量查尋的速度。相反,放在變量的??臻g中的值是該對象存儲在中的地址。地址的大小是固定的,所以把它存儲在中對變量性能無任何負面影響。

不同的內存分配機制也帶來了不同的訪問機制

javascript中是不允許直接訪問保存在堆內存中的對象的,也就是說不能直接操作對象的內存空間。所以在訪問一個對象時,首先得到的是這個對象在堆內存中的地址,然后再按照這個地址去獲得這個對象中的值,這就是傳說中的按引用訪問。而原始類型的值則是可以直接訪問到的。

注意:當復制保存著對象的某個變量時,操作的事對象的引用。但在為對象添加屬性時,操作的是實際的對象

復制變量的不同

基礎類型值:在將一個保存著基礎類型值的變量復制給另一個變量時,會將原始值的副本賦值給新變量,此后這兩個變量是完全獨立的,他們只是擁有相同的value而已。

function addTen(num) {
    num += 10;
    return num;
}
var count = 20;
var result = addTen(count);
console.log(count); //20 沒有變化
console.log(result); //30

引用值:在將一個保存著對象內存地址的變量復制給另一個變量時,會把這個內存地址賦值給新變量,也就是說這兩個變量都指向了堆內存中的同一個對象,他們中任何一個作出的改變都會反映在另一個身上。(這里要理解的一點就是,復制對象時并不會在堆內存中新生成一個一模一樣的對象,只是多了一個保存指向這個對象指針的變量罷了)

function setName(obj) {
    obj.name = "Nicholas";
}
var person = new Object();
setName(person);
console.log(person.name); //"Nicholas"
參數傳遞的不同

首先我們應該明確一點:ECMAScript中所有函數的參數都是按值來傳遞的。但是為什么涉及到基礎類型與引用類型的值時仍然有區別呢,還不就是因為內存分配時的差別。

基礎類型值:只是把變量里的值傳遞給參數,之后參數和這個變量互不影響。

引用類型值:對象變量它里面的值是這個對象在堆內存中的內存地址,這一點你要時刻銘記在心!因此它傳遞的值也就是這個內存地址,這也就是為什么函數內部對這個參數的修改會體現在外部的原因了,因為它們都指向同一個對象呀。或許我這么說了以后你對書上的例子還是有點不太理解,那么請看圖吧:

所以,如果是按引用傳遞的話,是把第二格中的內容(也就是變量本身)整個傳遞進去(就不會有第四格的存在了)。但事實是變量把它里面的值傳遞(復制)給了參數,讓這個參數也指向原對象。因此如果在函數內部給這個參數賦值另一個對象時,這個參數就會更改它的值為新對象的內存地址指向新的對象,但此時原來的變量仍然指向原來的對象,這時候他們是相互獨立的;但如果這個參數是改變對象內部的屬性的話,這個改變會體現在外部,因為他們共同指向的這個對象被修改了呀!來看下面這個例子吧:(傳說中的call by sharing)

var obj1 = {
  value:"111"
};
 
var obj2 = {
  value:"222"
};
 
function changeStuff(obj){
  obj.value = "333";
  obj = obj2;       
  return obj.value;
}
 
 
var foo = changeStuff(obj1);
 
console.log(foo);// "222" 參數obj指向了新的對象obj2
console.log(obj1.value);//"333"

obj1仍然指向原來的對象,之所以value改變了,
是因為changeStuff里的第一條語句,這個時候obj是指向obj1的 .
再啰嗦一句,如果是按引用傳遞的話,這個時候obj1.value應該是等于"222"

可以把ECMAScript函數的參數想象成局部變量

4.1.4 檢測類型

如果變量的值是一個對象null,則typeof操作符會返回"object".

通常我們并不是想知道某個值是對象,而是想知道它是什么類型的對象。為此,ECMAScript提供了instanceof操作符;

如果對象是給定引用類型的實例,那么instanceof操作符就會返回true

console.log(person instanceof Object);  //變量person是Object嗎?
console.log(colors instanceof Array);   //變量colors是Array嗎?
console.log(pattern instanceof RegExp); //變量pattern是RegExp嗎?

根據規定,所有引用類型的值都是Object的實例。在檢查一個引用類型值和Object構造函數時,instanceof操作符始終會返回true

4.2執行壞境和作用域

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

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

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

4.2.1 延長作用域鏈

有些語句可以在作用域鏈的前端臨時增加一個變量對象,該變量對象會在代碼執行后被移除。有兩種情況下會發生這種現象。

try-catch 語句中的 catch 塊

with 語句

對 with 來說,將會指定對象添加到作用域鏈中。對 catch 來說,會創建一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明。

var oMyself = {
    sFirstname: "Aidan",
    sLastName: "Dai"
}

function create(){
    var sLastName = "Wen"
    with(oMyself){
        //將oMyself作為自己的執行環境
        sAllName = sFirstname +" " + sLastName;
    }
    return sAllName;
}
var sMyName = create();
console.log(sMyName); //Aidan Dai
4.2.2 沒有塊級作用域

對于有塊級作用域的語言來說,for語句初始化變量的表達式所定義的變量,只會存在于循環的環境之中。而對于JavaScript來說,由for語句創建的變量i即使在for循環執行結束后,也依舊會存在于循環外部的執行環境中。

1. 聲明變量
使用var聲明的變量會自動被添加到最接近的環境中。在函數內部,最接近環境的就是函數的局部環境;在with語句中,最接近的環境就是函數環境。如果初始化變量時沒有使用var聲明,該變量會自動被添加到全局環境。

注意:在編寫JavaScript中,不聲明而直接初始化變量時一個錯誤的做法,因為這樣可能會導致意外。在嚴格模式下,初始化未經聲明的變量會導致錯誤。

2.查詢標識符
搜索過程從作用域鏈的前端開始,向上逐級查詢與給定名字匹配的標識符。如果在局部環境找到,搜索過程停止,變量就緒。如果在局部環境中沒有找到該變量名,則繼續沿作用域向上搜索。搜索過程將一直追溯到全局環境的變量對象。在全局環境也沒找到的話則說明該變量尚未聲明。

var color = "blue";

function getColor() {
    return color;
}

console.log(getColor()); //"blue";
4.3 垃圾收集

JavaScript具有自動垃圾收集機制,也就是說,執行環境會負責管理代碼執行過程中使用的內存。
JavaScript垃圾回收的機制很簡單:找出不再使用的變量,然后釋放掉其占用的內存,但是這個過程不是時時的,因為其開銷比較大,所以垃圾回收器會按照固定的時間間隔周期性的執行。

變量生命周期

什么叫不再使用的變量?不再使用的變量也就是生命周期結束的變量,當然只可能是局部變量,全局變量的生命周期直至瀏覽器卸載頁面才會結束。局部變量只在函數的執行過程中存在,而在這個過程中會為局部變量在?;蚨焉戏峙湎鄳目臻g,以存儲它們的值,然后再函數中使用這些變量,直至函數結束(閉包中由于內部函數的原因,外部函數并不能算是結束

一旦函數結束,局部變量就沒有存在必要了,可以釋放它們占用的內存。貌似很簡單的工作,為什么會有很大開銷呢?這僅僅是垃圾回收的冰山一角,就像剛剛提到的閉包,貌似函數結束了,其實還沒有,垃圾回收器必須知道哪個變量有用,哪個變量沒用,對于不再有用的變量打上標記,以備將來回收。用于標記無用的策略有很多,常見的有兩種方式

4.3.1 標記清除

這是JavaScript最常見的垃圾回收方式,當變量進入執行環境的時候,比如函數中聲明一個變量,垃圾回收器將其標記為“進入環境”,當變量離開環境的時候(函數執行結束)將其標記為“離開環境”。至于怎么標記有很多種方式,比如特殊位的反轉、維護一個列表等,這些并不重要,重要的是使用什么策略,原則上講不能夠釋放進入環境的變量所占的內存,它們隨時可能會被調用的到。

垃圾回收器會在運行的時候給存儲在內存中的所有變量加上標記,然后去掉環境中的變量以及被環境中變量所引用的變量(閉包),在這些完成之后仍存在標記的就是要刪除的變量了,因為環境中的變量已經無法訪問到這些變量了,最后,垃圾收集器完成內存清除工作,銷毀那些帶標記的值并回收它們所占用的內存空間。

4.3.2 引用計數

在低版本IE中經常會出現內存泄露,很多時候就是因為其采用引用計數方式進行垃圾回收。引用計數的策略是跟蹤記錄每個值被使用的次數,當聲明了一個變量并將一個引用類型賦值給該變量的時候這個值的引用次數就加1,如果該變量的值變成了另外一個,則這個值得引用次數減1,當這個值的引用次數變為0的時候,說明沒有變量在使用,這個值沒法被訪問了,因此可以將其占用的空間回收,這樣垃圾回收器會在運行的時候清理掉引用次數為0的值占用的空間。

4.3.3 性能問題

垃圾收集器是周期性運行的,而且如果為變量分配的內存數量很可觀,那么回收工作量也是相當大的。在這種情況下,確定垃圾收集的時間間隔是一個非常重要的問題。

事實上,在有的瀏覽器中可以觸發垃圾收集過程,但我們不建議這樣做。在IE中調用window.CollectGarbage()方法會立即執行垃圾收集。在Opera7及更高版本中,調用window.opera.collect()也會啟動垃圾收集例程。

4.3.4管理內存

確保占用最少的內存可以讓頁面獲得更好的性能。而優化內存占用的最佳方式,就是為執行中的代碼只保存必要數據。一旦數據不再可用,最好通過將其值設置為null來釋放其引用——這個方法叫做解除引用(dereferencing)。這一做法適用于大多數全局變量和全局對象屬性。局部變量會在它們離開執行環境時自動被解除引用。

function createPerson(name) {
     var localPerson = new Object();
     localPerson.name = name;
     return localPerson;
}
var globalPerson = createPerson("Nicholas");

//手工解除globalPerson的引用

globalPerson = null;
4.4 小結

基本類型值和引用類型值具有以下特點:

基本類型值在內存中占據固定大小的空間,因此被保存在棧內存中;

從一個變量向另一個變量復制基本類型的值,會創建這個值得一個副本;

引用類型的值是對象,保存在堆內存中;

包含引用類型值得變量實際上包含的并不是對象本身,而是指向該對象的指針;

從一個變量向另一個變量復制引用類型的值,復制的其實是指針,因此兩個變量最終都指向同一個對象;

確定一個值是哪種基本類型可以使用typeof操作符,而確定一個值是哪種引用類型可以使用instanceof操作符。
所有變量(包括基本類型和引用類型)都存在于一個執行環境(也稱為作用域)當中,這個執行環境決定了變量的生命周期,以及哪一部分代碼可以訪問其中的變量。

最后,如有錯誤和疑惑請指出,多謝各位大哥

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

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82338.html

相關文章

  • 《你不知道的javascript筆記_作用域與閉包

    摘要:建筑的頂層代表全局作用域。實際的塊級作用域遠不止如此塊級作用域函數作用域早期盛行的立即執行函數就是為了形成塊級作用域,不污染全局。這便是閉包的特點吧經典面試題下面的代碼輸出內容答案個如何處理能夠輸出閉包方式方式下一篇你不知道的筆記 下一篇:《你不知道的javascript》筆記_this 寫在前面 這一系列的筆記是在《javascript高級程序設計》讀書筆記系列的升華版本,旨在將零碎...

    galaxy_robot 評論0 收藏0
  • 你不知道的JavaScript上卷之作用域與閉包·讀書筆記

    摘要:的分句會創建一個塊作用域,其聲明的變量僅在中有效。而閉包的神奇作用是阻止此事發生。依然持有對該作用域的引用,而這個引用就叫做閉包。當然,無論使用何種方式對函數類型的值進行傳遞,當函數在別處被調用時都可以觀察到閉包。 date: 16.12.8 Thursday 第一章 作用域是什么 LHS:賦值操作的目標是誰? 比如: a = 2; RHS:誰是賦值操作的源頭? 比如: conso...

    Raaabbit 評論0 收藏0
  • 先有蛋還是先有雞?JavaScript 作用域與閉包探析

    摘要:而閉包的神奇之處正是可以阻止事情的發生。拜所聲明的位置所賜,它擁有涵蓋內部作用域的閉包,使得該作用域能夠一直存活,以供在之后任何時間進行引用。依然持有對該作用域的引用,而這個引用就叫閉包。 引子 先看一個問題,下面兩個代碼片段會輸出什么? // Snippet 1 a = 2; var a; console.log(a); // Snippet 2 console.log(a); v...

    elisa.yang 評論0 收藏0
  • javaScript作用域與閉包

    摘要:閉包里面保存的變量只有被方法引用了的變量這個例子里,閉包里只有并沒有。那最后來說說的問題閉包到底是什么閉包是一個作用域。鑒于在的調試窗口,是放在下面的那閉包這個作用域是個什么范圍被后代方法子方法,孫子方法。。。 首先給js的作用域這個話題打標簽:2,var, 全局變量,局部變量,函數,undefined, 作用域提升,賦值不會提升,ReferenceError, 同名覆蓋。打完標簽之后...

    Reducto 評論0 收藏0
  • JavaScript語法理論知識點

    摘要:方法用于刪除原數組中的一部分元素,并可以在被刪除的位置添加新數組成員,返回值是被刪除的元素。遞歸函數函數內部調用函數自身,稱之為遞歸。在函數內使用聲明的變量是局部變量,是人為的聲明。 前言 這個筆記不知道什么時候記下的反正很有意思,很基礎,很理論。 JavaScript學習筆記1 第一章 JavaScript概述 1.1 什么是JavaScript JavaScript是一種輕量級的腳...

    Amos 評論0 收藏0

發表評論

0條評論

lavnFan

|高級講師

TA的文章

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