js變量、作用域及內(nèi)存詳解
基本類型值有:undefined,NUll,Boolean,Number和String,這些類型分別在內(nèi)存中占有固定的大小空間,他們的值保存在??臻g,我們通過按值來訪問的。
(1)值類型:數(shù)值、布爾值、null、undefined。
(2)引用類型:對象、數(shù)組、函數(shù)。
如果賦值的是引用類型的值,則必須在堆內(nèi)存中為這個值分配空間。由于這種值的大小不固定(對象有很多屬性和方法),因此不能把他們保存到棧內(nèi)存中。但內(nèi)存地址大小是固定的,因此可以將內(nèi)存地址保存在棧內(nèi)存中。
<script type="text/javascript”> var box = new Object(); //創(chuàng)建一個引用類型 var box = "lee"; //基本類型值是字符串 box.age = 23; //基本類型值添加屬性很怪異,因為只有對象才可以添加屬性。 alert(box.age); //不是引用類型,無法輸出; </script>
簡而言之,堆內(nèi)存存放引用值,棧內(nèi)存存放固定類型值。

<script type="text/javascript"> var man = new Object();//man指向了棧內(nèi)存的空間地址 man.name = "Jack"; var man2 = man;//man2獲得了man的指向地址 alert(man2.name);//兩個都彈出Jack alert(man.name); </script>
復(fù)制變量值
再看下面這個例子:
<script type="text/javascript"> var man = new Object();//man指向了棧內(nèi)存的空間地址 man.name = "Jack"; var man2 = man;//man2獲得了man的指向地址 man2.name = "ming";//因為他們都指向同一個object,同一個name,不管修改誰,大家都修改了 alert(man2.name);//兩個都彈出ming alert(man.name); </script>
由以上可以得出:在變量復(fù)制方面,基本類型和引用類型也有所不同,基本類型復(fù)制的是值本身,而引用類型復(fù)制的是地址。
傳遞參數(shù)
ECMAScript中,所有函數(shù)的參數(shù)都是按值傳遞的,
<script type="text/javascript">
function box(num){ //按值傳遞
num+=10;
return num;
}
var num = 10;
var result = box(num);
alert(result); //如果是按引用傳遞,那么函數(shù)里的num會成為類似全局變量,把外面的number替換掉
alert(num); //也就是說,最后應(yīng)該輸出20(這里輸出10)
</script>
javascript沒有按引用傳遞的,如果存在引用傳遞的話,那么函數(shù)內(nèi)的變量將是全局變量,在外部也可以訪問。但這明顯是不可能的。
執(zhí)行環(huán)境及作用域
執(zhí)行環(huán)境是javascript中最為重要的概念之一,執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問其他數(shù)據(jù)。
全局執(zhí)行環(huán)境是最外圍的執(zhí)行環(huán)境,在web瀏覽器中,全局執(zhí)行環(huán)境是window對象,因此,所有的全局變量的函數(shù)都是作為window的屬性和方法創(chuàng)建的。
<script type="text/javascript">
var name = "Jack"; //定義全局變量
function setName(){
return "trigkit4";
}
alert(window.name); //全局變量,最外圍,屬于window屬性
alert(window.setName()); //全局函數(shù),最外圍,屬于window方法
</script>
當(dāng)執(zhí)行環(huán)境內(nèi)的代碼執(zhí)行完畢后,該環(huán)境被銷毀,保存其中的變量和函數(shù)也隨之銷毀,如果是全局環(huán)境,需所有程序執(zhí)行完畢或網(wǎng)頁完畢后才會銷毀。
去掉var的局部變量
<script type="text/javascript">
var name = "Jack";
function setName(){
name = "trigkit4"; //去掉var變成了全局變量
}
setName();
alert(name);//彈出trigkit4
</script>
通過傳參,也是局部變量
<script type="text/javascript">
var name = "Jack";
function setName(name){ //通過傳參,也是局部變量
alert(name);
}
setName("trigkit4");//彈出trigkit4
alert(name);//彈出Jack
</script>
函數(shù)體內(nèi)還包含函數(shù),只有這個函數(shù)才可以訪問內(nèi)一層的函數(shù)
<script type="text/javascript">
var name = "Jack";
function setName(){
function setYear(){ //setYear()方法的作用域在setName()內(nèi)
return 21;
}
}
alert(setYear());//無法訪問,出錯
</script>
可以通過如下方法進(jìn)行訪問:
<script type="text/javascript">
var name = "Jack";
function setName(){
function setYear(){ //setYear()方法的作用域在setName()內(nèi)
return 21;
}
return setYear();
}
alert(setName()); //彈出21
</script>
再一個作用域例子:
<script type="text/javascript">
var name = "Jack";
function setName(){
function setYear(){ //setYear()方法的作用域在setName()內(nèi)
var b = "hi"; //變量b的作用域在setYear()內(nèi)
return 21;
}
alert(b);//無法訪問
}
</script>
當(dāng)代碼在一個環(huán)境中執(zhí)行的時候,就會形成一種叫做作用域鏈的東西,它的用途是保證對執(zhí)行環(huán)境中有訪問權(quán)限的變量和函數(shù)進(jìn)行有序訪問(指按照規(guī)則層次來訪問),作用域鏈的前端,就是執(zhí)行環(huán)境的變量對象。
作用域
變量沒有在函數(shù)內(nèi)聲明或者聲明的時候沒有帶var就是全局變量,擁有全局作用域,window對象的所有屬性擁有全局作用域;在代碼任何地方都可以訪問,函數(shù)內(nèi)部聲明并且以var修飾的變量就是局部變量,只能在函數(shù)體內(nèi)使用,函數(shù)的參數(shù)雖然沒有使用var但仍然是局部變量。
沒有塊級作用域
沒有塊級作用域
// if語句:
<script type="text/javascript">
if(true){ //if語句的花括號沒有作用域的功能。
var box = "trigkit4";
}
alert(box);//彈出 trigkit4
</script>
for循環(huán)語句也是如此。
變量的查詢
在變量的查詢中,訪問局部變量要比全局變量來得快,因此不需要向上搜索作用域鏈。
如下例子:
<script type="text/javascript">
var name = "Jack";
function setName(){
var name = "trigkit4";
return name; //從底層向上搜索變量
}
alert(setName());
</script>
內(nèi)存問題
javascript具有自動垃圾回收機制,一旦數(shù)據(jù)不再使用,可以將其設(shè)為"null"來釋放引用
循環(huán)引用
一個很簡單的例子:一個DOM對象被一個Javascript對象引用,與此同時又引用同一個或其它的Javascript對象,這個DOM對象可能會引發(fā)內(nèi)存泄露。這個DOM對象的引用將不會在腳本停止的時候被垃圾回收器回收。要想破壞循環(huán)引用,引用DOM元素的對象或DOM對象的引用需要被賦值為null。
閉包
在閉包中引入閉包外部的變量時,當(dāng)閉包結(jié)束時此對象無法被垃圾回收(GC)。
var a = function() {
var largeStr = new Array(1000000).join('x');
return function() {
return largeStr;
}
}();
DOM泄露
當(dāng)原有的COM被移除時,子結(jié)點引用沒有被移除則無法回收。
var select = document.querySelector;
var treeRef = select('#tree');
//在COM樹中l(wèi)eafRef是treeFre的一個子結(jié)點
var leafRef = select('#leaf');
var body = select('body');
body.removeChild(treeRef);
//#tree不能被回收入,因為treeRef還在
//解決方法:
treeRef = null;
//tree還不能被回收,因為葉子結(jié)果leafRef還在
leafRef = null;
//現(xiàn)在#tree可以被釋放了。
Timers計(定)時器泄露
定時器也是常見產(chǎn)生內(nèi)存泄露的地方:
for (var i = 0; i < 90000; i++) {
var buggyObject = {
callAgain: function() {
var ref = this;
var val = setTimeout(function() {
ref.callAgain();
}, 90000);
}
}
buggyObject.callAgain();
//雖然你想回收但是timer還在
buggyObject = null;
}
調(diào)試內(nèi)存
Chrome自帶的內(nèi)存調(diào)試工具可以很方便地查看內(nèi)存使用情況和內(nèi)存泄露:
在 Timeline -> Memory 點擊record即可:
相關(guān)文章
javascript prototype原型詳解(比較基礎(chǔ))
prototype原型是javascript中特別重要的概念,屬于必須要掌握,如果沒有良好的掌握的話,進(jìn)一步用好或者學(xué)好js基本是不可能的實現(xiàn)的事情,并且此概念稍有難度,可能對于初次接觸的朋友來說有點困難,下面就通過代碼實例簡單介紹一下prototype原型的用法2016-12-12
JavaScript 學(xué)習(xí)筆記之?dāng)?shù)據(jù)類型
javascript數(shù)據(jù)類型非常簡單,僅僅包含undefined、null、string、Boolean、number以及object,今天我們就針對這幾個數(shù)據(jù)類型,一一進(jìn)行講解,方便大家理解記憶2015-01-01
JavaScript開發(fā)規(guī)范要求(規(guī)范化代碼)
作為一名開發(fā)人員(WEB前端JavaScript開發(fā)),不規(guī)范的開發(fā)不僅使日后代碼維護(hù)變的困難,同時也不利于團隊的合作,通常還會帶來代碼安全以及執(zhí)行效率上的問題。2010-08-08
JS中attr和prop屬性的區(qū)別以及優(yōu)先選擇示例介紹
這篇文章主要介紹了JS中attr和prop屬性的區(qū)別以及優(yōu)先選擇,需要的朋友可以參考下2014-06-06
JavaScript中將數(shù)組進(jìn)行合并的基本方法講解
這篇文章主要介紹了JavaScript中將數(shù)組進(jìn)行合并的基本方法講解,包括快速合并多個數(shù)組的方法,需要的朋友可以參考下2016-03-03
js常用的鍵盤事件有哪些(用法示例)_鍵碼keyCode對照表
用戶按下鍵盤上的鍵,首先會觸發(fā)keydown事件,然后是keypress事件,最后是keyup事件。其中,keydown和keypress事件是在文本框發(fā)生變化之前被觸發(fā);而keyup在文本框發(fā)生變化之后被觸發(fā)。如果用戶按下一個鍵不放,就會重復(fù)觸發(fā)keydown和keypress事件。2023-02-02
Javascript基礎(chǔ)教程之JavaScript語法
本文是javascript基礎(chǔ)教程的第一篇,給大家?guī)淼氖莏avascript的最基礎(chǔ)的東西--javascript的語法的注意事項,希望大家能夠喜歡2015-01-01

