摘要:如果在局部環境沒有找到該變量名,則繼續沿作用域鏈向上搜索。很明顯,訪問局部變量要比訪問全局變量更快,因為不用向上搜索作用域鏈。
按照ECMA-262定義,JavaScript的變量松散類型的本質,決定了:
它還只是在特定時間用于保存特定值的一個名字而已。
變量的值及其數據類型可以再腳本的生命周期內改變。
4.1 基本類型和引用類型的值
基本類型
簡單的數據段(Undefined、Null、Bollean、Number和String);
基本類型變量按值進行訪問,因為可以操作保存在變量中的實際的值。
引用類型
可能由多個值構成的對象;
是保存在內存中的對象;
與其它語言不同,JavaScript不允許直接訪問內存中的位置,即不能直接操作對象所在的內存空間。實際操作時,是在操作對象的引用而不是實際的對象。這就是所謂的 引用類型的值是按引用訪問的。(書中注釋提出:這種說法不嚴密,當復制保存這對象的某個變量時,操作是對對象的引用。但在為對象添加屬性時,操作的是實際的對象。----圖靈社區"壯壯的前端之路"注)
4.1.1 動態的屬性參考:http://segmentfault.com/a/1190000002789651
基本類型和引用類型變量定義方式相同:創建一個變量并為該變量賦值。
而不同類型值可執行操作則大相徑庭,例如只能給引用類型值動態地添加屬性:
引用值類型
var person = new Object(); person.name = "Nicholas"; alert(person.name); //"Nicholas" //如果對象不被銷毀或name屬性不被刪除,則這個屬性將永遠存在。
基本類型
var name = "Nicholas"; name.age = 27; alert(name.age); //undefined //為基本類型變量添加屬性不會報錯,但無法訪問。4.1.2 復制變量值
基本類型
基本類型的變量存放在棧區(內存中的棧內存)的。
從一個變量向另一個變量復制時,會在變量對象上創建一個新值,然后把該值復制到為新變量分配的位置上。兩個變量可以參與任何操作而不相互影響。例如:
var num1 = 5; var num2 = num1; num1= 6; console.log(num2); //5
引用類型
引用類型的值同時保存在棧內存和堆內存中。棧區保存變量標識符和指向對內存中該對象的指針。
從一個變量向另一個變量復制時,會將存儲在變量對象中的只復制一份放到為新變量分配的空間中。但,這個值實際上是一個指針,這個指針指向存儲在堆中的一個新對象。復制結束后,兩個變量實際上將引用同一個對象。改變其中一個變量,就會影響另一個,例如:
var obj1 = new Object(); var obj2 = obj1; obj1.name = "Nicholas"; alert(obj2.name); //"Nicholas"4.1.3 傳遞參數
ECMAScript中所有函數的參數都是按值傳遞的。即把函數外部的值賦值給函數內部的參數,等價于把值從一個變量復制到另一個變量一樣??梢园袳CMAScript函數的參數理解為局部變量。例如:
基本類型
function addTen(num){ num += 10; return num; } var count = 20; var result = addTen(count); alert(count); //20 alert(result); //30
引用類型
function setName(obj){ obj.name = "Nicholas"; obj = new Object(); obj.name = "Greg"; } var person = new Object(); setName(person); alert(person.name); //"Nicholas" //即使在函數內部修改了參數的值,但愿是的引用仍然保持不變。 //實際上,當在函數內部重寫obj時,這個變量引用改的就是一個局部對象了。而這個局部對象會在函數執行完畢后立即被銷毀。4.1.4 檢測類型
typeof 檢測基本數據類型
var s = "Nicholas"; var b = true; var i = 22; var u; var n = null; //注意:使用typeof操作符,null返回object var o = new Object; alert(typeof s); //string alert(typeof b); //bollean alert(typeof i); //number alert(typeof u); //undefined alert(typeof n); //object alert(typeof o); //object
instanceof 檢測引用值類型值是何種類型的對象
語法:result = variable instanceof constructor
如果變量是給定引用類型(根據它的原型鏈來識別,見第6章)的實例,instanceof返回true.
var person = new (); var color = ["blue","yellow","red"]; alert(person instanceof Object);//true alert(color instanceof Array); //true4.2 執行環境及作用域
執行環境 定義了變量或函數有權訪問的其他數據,決定了他們各自的行為。每個執行環境都有一個與之關聯的變量對象(variable object), 環境中定義的所有變量和函數都保存在這個對象中。(這個對象我們無法訪問,解析器在處理數據時在后臺使用。)
全局執行環境 表示最外圍的執行環境。在Web瀏覽器中,全局環境被認為是window對象(詳見第7章)。因此所有全局變量和函數都是作為window對象的屬性和方法創建的。某個執行環境中的所有代碼執行完畢后,該環境被銷毀,保存在其中的所有變量和函數定義也隨之銷毀(全局執行環境知道應用程序退出---例如關閉網頁或瀏覽器時才會被銷毀)。
每個函數都有自己的執行環境。當執行流進入一個函數時,函數的環境會被推入一個環境棧中。而函數執行之后,棧將其彈出,把控制權返回給之前的執行環境。ECMAScript程序中的執行流正是由這個方便的機制控制著。
作用域鏈(scope chain)---代碼在一個環境中執行時,會創建變量對象的作用域鏈。用于保證對執行環境有權訪問的所有變量和函數的有序訪問。作用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象,如果是個函數,則將其活動對象(activation object)作為變量對象?;顒訉ο笤谧铋_始時只包含一個變量,即arguments對象(這個對象在全局環境中不存在)。作用域鏈中的下一個變量對象來自包含(外部)環境,而再下一個變量對象則來自下一個包含環境。這樣,一直延續到全局環境 ;全局執行環境的變量始終都是自作用域鏈中的最后一個對象。
標識符解析 是沿著作用域鏈一級一級地搜索標識符的過程。搜索過程始終從更作用域鏈的前端開始,然后逐級地向后回溯,知道找到標識符位置(若找不到,通常會導致錯誤)。
例如:
var color = "blue"; function changeColor(){ if(color === "blue"){ color = "red"; }else{ color = "blue"; } } //changeColor()的作用域鏈包含兩個對象:他自己的變量對象(其中定義著arguments對象)和全局環境的變量對象。 //可以在函數內部訪問變量color,就是因為可以在這個作用域中找到它。 changeColor(); alert("Color is now" + color);
此外,在局部作用域定義的變量可以再局部環境中與全局變量互換使用。例如:
var color = "blue"; function changeColor(){ var anotherColor = "red"; function swapColors(){ var tempColor = anotherColor; anotherColor = color; color = tempColor; //這里可以訪問color,anotherColor和tempColor } //這里可以訪問color和anotherColor, 但不能訪問tempColor } //這里只能訪問color changeColor();
內部環境可以通過作用域鏈訪問所有的外部環境,但外部環境不能訪問內部環境中的任何變量和函數;
這些環境之間是線性的、由次序的;
每個環境都可以向上搜索作用域鏈,以查詢變量和函數名,但任何環境都不能通過向下搜索作用域鏈而進入另一個執行環境;
函數參數也被當作變量來對待,因此訪問規則與執行環境中的其他變量相同。
4.2.1 延長作用域鏈下列兩個語句可以再作用域鏈的前端臨時增加一個變量對象,該變量對象會在代碼執行后被移除。具體來說,就是當執行流進入下列任一語句時,作用域鏈就會得到加長。
//try-catch語句的catch塊; //with語句;
例如:
function buildUrl(){ var qs = "?debug=true"; with(location){ var url = href + qs; } return url; }
4.2.2 沒有塊級作用域 1. 聲明變量with語句接受了一個location對象,因此其變量對象中就包含了location對象的所有屬性和方法,而這個變量對象被添加到了作用域鏈的前端。
buildUrl()函數中定義了了一個變量qs.當在with語句中引用變量href時(實際引用的是location.href),可以再當前執行環境的變量對象中找到。當引用了變量qs時,引用改的則是在buiildUrl()中定義的那個變量,而該變量位于函數換進改的變量對象中。
至于with語句內部,則定義了一個名為url的變量,因而url變量就成了函數執行環境的一部分,所以可以作為函數的值被返回。
使用var聲明的變量會自動被添加到最接近的環境中。在函數內部,最接近的環境就是函數的局部環境;在with語句中,最接近的環境是函數環境。
如果初始化變量時沒有使用var, 改變兩會自動被添加到全局變量。
例如:
function add(num1, num2){ var sum = num1 + num2; return sum; } var result = add(10, 20); //30 alert(sum); //報錯 //若省略第二行的var, 那么add()執行完畢后,sum依然可以被訪問到,alert(sum)返回30.
在嚴格模式下,初始化未聲明變量會導致錯誤。
2. 查詢標識符查詢標識符過程:
當在某個環境中為了讀取或寫入而引用一個標識符時,必須通過搜索來確定該標識符實際代表什么。搜索過程從作用域鏈的前端開始,向上 逐級查詢與給定名字匹配的標識符。如果在局部環境中找到了該標識符,搜索過程停止,變量就緒。如果在局部環境沒有找到該變量名,則繼續沿作用域鏈向上搜索。搜索過程一直追溯到全局環境的變量對象。
例如:
var color = "blue"; function getColor(){ return color; } alert(getColor()); //"blue"
調用getColor()時,用到了變量color.為了確定變量color的值,將開始搜索:
搜索getColor()的變量對象,查找其中是否包含一個名為color的標識符,沒找到,進行第2步;
向上繼續搜索到了定義這個變量的變量對象(全局環境的變量對象),找到了名為color的標識符。
注:在這個過程中,如果存在一個局部的變量的定義,則搜索自動停止,不再進入另一個變量對象。即如果局部環境中存在著同名標識符,就不會使用位于父環境中的標識符。
例如:
var color = "blue"; function getColor(){ var color = "red"; return color; } alert(getColor()); //"red"
4.3 垃圾收集查詢變量是有代價的。很明顯,訪問局部變量要比訪問全局變量更快,因為不用向上搜索作用域鏈。JavaScript引擎在優化標識符查詢方面做得不錯,因此這個差別在將來恐怕就可以忽略不計了。
JavaScript具有自動垃圾收集機制。(執行環境會負責管理代碼執行過程中使用的內存)
函數中局部變量的正常生命周期:
局部變量只在函數執行的過程中存在。而在這個過程中,會為局部變量在棧(或堆)內存上分配相應的空間,以便存儲他們的值;
然后在函數中使用這些變量,直至函數執行結束;
此時,便可以釋放局部變量的內存以供將來使用。
不同瀏覽器,通常有兩個策略:標記清除、引用計數。
4.3.1 標記清除JavaScript中最常用的垃圾收集方式是標記清除(mark-and-sweep). 當變量進入環境(例如,在函數中聲明一個變量)時,就將這個變量標記為“進入環境”。從邏輯上說,永遠不能釋放進入環境的變量所占用的內存,因為只要執行流進入相應的環境,就可能會用到他們。而當變量離開環境是,則將其標記為“離開環境” 。
可以使用任何方式來標記變量。比如,通過翻轉某個特殊的位來記錄一個變量何時進入環境,或者使用一個“進入環境的”變量列表一個“離開環境的”變量列表來跟蹤那個變量發生了變化。采取什么策略比如何標記更重要。
垃圾收集器的內存清除工作:
垃圾收集器 在運行的時候會給存儲在內存中的所有變量都加上標記(當然,可以使用任何標記方式);
它會去掉環境中的變量以及被環境中的變量引用的變量的標記;
而在此之后再被加上標記的變量將被視為準備刪除的變量。因為環境中的變量已經無法訪問到這些變量了;
最后,垃圾收集器完成內存清除工作,銷毀那些代表及的值并回收他們所占用的內存空間。
注:到2008年為止,IE、Firefox、Opera、Chrome和Safari的JavaScript實現使用的都是標記清除是的垃圾回收策略(或類似的策略),只不過垃圾收集的時間間隔互有不同。
4.3.2 引用計數(不太常見)跟蹤記錄每一個值被引用的次數。
當生命了一個變量并將一個引用類型值賦給該變量是,則這個值的引用次數就是1. 在賦給另一個變量,引用次數再+1. 相反,如果包含對這個值引用的變量又取得了另外一個值,則這個值的引用次數-1. 當這個值的引用次數變成0時,說明沒辦法在訪問這個值,因而就可以將其占用的內存回收回來。如此,當垃圾收集器再次運行時,他就會釋放哪些引用次數為0的值所占用的內存。
這種方式可能遇到循環引用的問題,例如Netscape Navigator 3.0是最早使用引用計數策略的瀏覽器:
function problem(){ var objectA = new Object(); var objectB = new Object(); objectA.someOtherObject = objectB; objectB.someOtherObject = objectA; } //當problem()函數執行完畢后,objectA和objectB引用次數為2. 若函數反復調用,便會導致大量內存的不到回收。 //Netscape在Navigator4.0之后便改用標記清除策略。
IE9+,把BOM和DOM對象都轉換成了真正的JavaScript對象。避免了兩種垃圾收集算法并存導致的問題,也消除了常見的內存泄露現象。
4.3.3 性能問題垃圾回收機制是周期性運行的,而且 如果為變量分配的內存數量很可觀,那么回收工作量也是相當大的。此時,確定垃圾回收間隔時間非常重要。
在有的瀏覽器中可以觸發垃圾收集機制,但不建議這么做。在IE中,調用window.CollectGarbge()方法會立即執行垃圾收集。在Opera7及更高版本中,調用window.opera.collect()也會啟動垃圾收集機制。
4.3.4 管理內存分配給Web瀏覽器的可用內存數量通常比分配給桌面應用程序的少。主要是出于安全方面的考慮,防止運行JavaScript的網頁耗盡全部系統內存導致系統崩潰。
內存限制問題不僅會影響給變量分配內存,也會影響調用棧以及在一個線程中能夠同時執行的語句數量。
優化內存占用的最佳方式,就是為執行中的戴嘛只保存必要的數據。一旦數據不在游泳,最好通過最好將其設置為null來釋放其引用----- 解除引用 (dereferencing)。適用于大多數全局變量和全局對象的屬性。
例如:
function createPerson(name){ var localPerson = new Object(); localPerson.name = name; return localPerson; } var globalPerson = createPerson("Nicholas"); globalPerson = null;//手動解除globalPerson的引用
注:解除一個值的引用并不意味著自動回收該值所占用的內存。真正作用是讓值脫離執行環境,以便垃圾回收器下次運行時將其回收。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86264.html
摘要:不允許直接訪問內存中的位置,也就是說不能直接操作對象的內存空間。在操作對象時,實際上是在操作對象的引用而不是實際的對象。解除引用的真正作用是讓值脫離執行環境,以便垃圾收集器下次運行時將其回收 1 基本類型和引用類型的值 基本數據類型是按值訪問的,因為可以操作保存在變量中的實際的值 基本類型值在內存中占據固定大小的空間,因此被保存在棧內存中 引用類型的值是保存在內存中的對象。JavaSc...
摘要:在中雖然對象通過標記清除的方式進行垃圾收,但與對象卻是通過引用計數回收垃圾的,也就是說只要涉及及就會出現循環引用問題。如果垃圾收集例程回收的內存分配量低于,則變量字面量和或數組元素的臨界值就會加倍。 只挑本人重要的寫(有夾雜其他補充) 基本類型和引用類型的值 描述:基本類型值指的是簡單的數據段,而引用類型值指那些可能由多個值構成的對象。 動態的屬性 引用類型的值,我們可以為其添加屬性和...
摘要:閉包的出現正好結合了全局變量和局部變量的優點。這就是閉包的一個使用場景保存現場。 前言 什么是閉包,其實閉包是可以重用一個對象,又保護對象不被篡改的一種機制。什么是重用一個對象又保護其不被篡改呢?請看下面的詳解。 作用域和作用域鏈 注意理解作用域和作用域鏈對理解閉包有非常大的幫助,所以我們先說一下作用域和作用域鏈 什么是作用域作用域表示的是一個變量的可用范圍、其實它是一個保存變量的對象...
摘要:允許在塊級作用域內聲明函數。上面代碼中,存在全局變量,但是塊級作用域內又聲明了一個局部變量,導致后者綁定這個塊級作用域,所以在聲明變量前,對賦值會報錯。 ES5的作用域 變量起作用的范圍,js中能創建作用域的只能是函數 { let a = 1; var b = 2; } console.log(a); // a is not defined console.log(b); //...
摘要:執行環境的類型有兩種全局全局執行環境局部函數執行環境每個環境都可以向上搜索作用域鏈,以查詢變量和函數名但任何環境都不能通過向下搜索作用域鏈而進入另一個執行環境。內部可通過作用域鏈訪問外部,外部不能訪問內部。 變量、作用域和內存問題 ECMAScript 數據類型 基本類型(5種): Undefined,Null,Boolean,Number,String typeof() 檢測...
閱讀 2418·2021-11-16 11:44
閱讀 1877·2021-10-12 10:12
閱讀 2160·2021-09-22 15:22
閱讀 3008·2021-08-11 11:17
閱讀 1505·2019-08-29 16:53
閱讀 2653·2019-08-29 14:09
閱讀 3474·2019-08-29 14:03
閱讀 3301·2019-08-29 11:09