JS 變量松散類型的本質窟哺,決定了變量只是在特定時間保存特定值的一個名字而已,而不存在定義某個變量的數(shù)據(jù)類型的規(guī)則技肩,因此變量的值及其數(shù)據(jù)類型可以在腳本的生命周期內(nèi)發(fā)生改變且轨。
基本類型的值是指簡單的數(shù)據(jù)段,引用類型的值指由可能有多個值構成的對象虚婿。
基本數(shù)據(jù)類型有5種:string, number,boolean,null,undefined. 它們按值訪問旋奢,因此可以操作保存在變量中實際的值。
在賦值給變量時然痊,解析器必須確定這個值是基本類型的值還是引用類型的值至朗。
引用類型的值是保存在內(nèi)存中的對象。js 不允許直接訪問內(nèi)存中的位置玷过,也就是不能直接操作對象的內(nèi)存空間爽丹。因此筑煮,在操作對象時,是在操作對象的引用粤蝎,而不是實際的對象真仲。
動態(tài)屬性
對基本類型和引用類型的變量值進行操作的方法不同。
對于引用類型的值初澎,可以為其添加秸应、刪除、更改屬性和方法:
var person = new Object();
person.name = "Nicholas";
alert(person.name); //"Nicholas"
alert ()是一個函數(shù)碑宴。通過函數(shù)訪問屬性
如果對象不被銷毀或者這個屬性不被刪除软啼,則這個屬性將一直存在。
基本類型的值卻不能添加屬性祸挪。
復制變量值
從一個變量向另一個變量復制基本類型值和引用類型值時,也存在不同贞间。
復制基本類型的值時贿条,會在變量對象上創(chuàng)建一個新值增热,然后把該值復制到為新變量分配的位置上。
var num1 =5;
var num2 = num1;
復制引用類型的值時,也會將存儲在變量對象中的值復制一份放到為新變量分配的空間中。不同在于吭从,這個值的副本是一個指針,這個指針指向存儲在堆中的一個對象影锈。復制操作結束后鸭廷,兩個變量引用同一個對象。改變其中一個變量熔吗,會影響另一個辆床。
傳遞參數(shù)
ECMAScript 中所有函數(shù)的參數(shù)都是按值傳遞的。
在向參數(shù)傳遞基本類型的值時桅狠,被傳遞的值會被復制給一個局部變量(即命名參數(shù)讼载,arguments對象中的一個元素)轿秧。
在向參數(shù)傳遞引用類型的值時,會把值在內(nèi)存中的地址復制給一個局部變量咨堤,因此這個局部變量的變化會反映在函數(shù)的外部菇篡。
https://www.zhihu.com/question/27114726
檢測類型
檢測基本數(shù)據(jù)類型用 typeof s
如果變量的值是一個對象或null,則會返回object
檢測引用類型的值一喘,則用instanceof constructor
示例: alert(person instanceof Object/Array/RegExp) //true or false
所有引用類型的值都是Obeject的實例驱还。因此在檢測一個引用類型的值和Object構造函數(shù)時,instanceof 操作符始終返回true凸克。如果使用instanceof 操作符檢測基本類型的值议蟆,則返回false,因為基本類型不是對象萎战。
執(zhí)行環(huán)境及作用域
執(zhí)行環(huán)境咐容,execution context,簡稱環(huán)境蚂维,定義了變量或函數(shù)有權訪問的其他數(shù)據(jù)戳粒,決定了各自的行為。每個執(zhí)行環(huán)境都有一個與之關聯(lián)的變量對象(variable object)鸟雏,環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中享郊。編寫代碼無法訪問這個對象,但解析器在處理數(shù)據(jù)時會在后臺使用它孝鹊。
全局執(zhí)行環(huán)境是最外圍的一個執(zhí)行環(huán)境炊琉。在web瀏覽器中,全局執(zhí)行環(huán)境是Window對象又活,所有全局變量和函數(shù)都是作為 Window的屬性和方法創(chuàng)建的苔咪。某個執(zhí)行環(huán)境中所有代碼執(zhí)行完畢后,該執(zhí)行環(huán)境被銷毀柳骄,保存在其中的所有變量和函數(shù)定義也隨之銷毀团赏。
每個函數(shù)都有自己的執(zhí)行環(huán)境。當執(zhí)行流進入一個函數(shù)時耐薯,函數(shù)的環(huán)境就會被推入到一個環(huán)境棧中舔清,函數(shù)執(zhí)行之后,棧將其環(huán)境彈出曲初,把控制權返回給之前的執(zhí)行環(huán)境体谒。ECMAScript程序中的執(zhí)行流正是由這個方便的機制控制著。
當代碼在一個環(huán)境中執(zhí)行時臼婆,會創(chuàng)建變量對象的一個作用域鏈(scope chain)抒痒。用途是保證對執(zhí)行環(huán)境有權訪問的所有變量和函數(shù)的有序訪問。作用域鏈前端颁褂,是當前執(zhí)行代碼所在環(huán)境的變量對象故响。如果這個環(huán)境是函數(shù)傀广,則將其活動對象(activation object)作為變量對象〔式欤活動對象最開始只包含一個變量伪冰,即arguments對象。作用域鏈中下一個變量對象來自包含(外部)環(huán)境惨缆,再下一個變量對象在來自下一個包含環(huán)境糜值,最后是全局執(zhí)行環(huán)境。
標識符解析是沿著作用域鏈一級一級地搜索標識符的過程坯墨。
延長作用域鏈
可以在作用域鏈的前端臨時增加一個變量對象寂汇,該變量對象會在代碼執(zhí)行后被移除。
function buildUrl(){
var qs = "?debug=true";
with (location){
var url=href +qs;
}
return url;
}
with 語句接收的是location對象捣染,因此其變量對象中就包含了 location對象的所有屬性和方法骄瓣,
href實際上是location.href
沒有塊級作用域
在其中c語言中,由花括號封閉的代碼都有自己的作用域耍攘,因而支持根據(jù)條件來定義變量榕栏。比如以下例子中,color會在if語句執(zhí)行完畢后被銷毀
if(true) {
var color = "blue";
}
alert(color); //"blue"
但在js中蕾各,if語句中的變量聲明會將變量添加到當前的執(zhí)行環(huán)境扒磁,這里是全局環(huán)境。
for (var i=0;i<10;i++){
doSomething(i);
}
alert(i);//10
對于塊級作用域的語言來說式曲,for語句初始化變量的表達式所定義的變量妨托,只會存在于循環(huán)的局部環(huán)境。而對于JS來說吝羞,變量i即使在for循環(huán)結束后兰伤,也依舊會存在于循環(huán)外部的執(zhí)行環(huán)境中。
- 變量聲明
使用var聲明的變量會自動被添加到被接近的環(huán)境中钧排,在函數(shù)內(nèi)部敦腔,最接近的環(huán)境就是函數(shù)的局部環(huán)境。下面例子中恨溜,sum被var聲明符衔,添加到add()函數(shù)的局部環(huán)境中,因此在全局環(huán)境中訪問時糟袁,是導致錯誤柏腻。這里如果省略var,則sum會被添加到全局環(huán)境中系吭。
function add(num1,num2) {
var sum =num1 + num2 ;
return sum;
}
var result = add(10,20);//30
alert(sum); //導致錯誤
訪問局部變量要比訪問全局變量更快,因為不用向上搜索作用域鏈颗品。
垃圾收集
js具有自動垃圾收集機制肯尺,不用手動跟蹤內(nèi)存的使用情況沃缘。
函數(shù)中的局部變量只在函數(shù)執(zhí)行的過程中存在,因此在函數(shù)執(zhí)行完畢后则吟,對其打上標記槐臀,將來回收其內(nèi)存。
標記清除
mark-and-sweep
當變量進入環(huán)境時(例如在函數(shù)中聲明一個變量)氓仲,將這個變量標記為進入環(huán)境水慨,而當變量離開環(huán)境時,則將其標記為離開環(huán)境敬扛。
垃圾收集器在允許的時候回給存儲在內(nèi)存中的所有變量都加上標記晰洒,然后去掉環(huán)境中的變量以及被環(huán)境中變量引用的變量的標記,在此之后啥箭,再被加上標記的變量將被視為準備刪除的變量谍珊。最后,垃圾收集器完成內(nèi)存清楚工作急侥,銷毀那些帶標記的值回收其占用的內(nèi)存空間砌滞。
另一種是引用計數(shù)
性能問題
當環(huán)境中存在256個變量,4096個對象坏怪,64K的字符串贝润,任意一種都會觸發(fā)垃圾回收機制,但如果環(huán)境中一直都有這么多變量存在铝宵,那么垃圾回收器會一直工作打掘。
因此,如果垃圾回收器回收的內(nèi)存分配量低于程序占用內(nèi)存的15%捉超,說明大部分內(nèi)存不可被回收胧卤,那么把臨界條件翻倍,如果回收的內(nèi)存高于85%拼岳,說明大部分內(nèi)存該清理了枝誊,把臨界值重置為默認值。
管理內(nèi)存
JS中惜纸,分配給web瀏覽器的可用內(nèi)存數(shù)量通常比分配給桌面應用程序的少叶撒,處于安全考慮。內(nèi)存限制問題不僅會影響給變量分配內(nèi)存問題耐版,還會影響調用棧以及在一個線程中能夠同時執(zhí)行的語句數(shù)量祠够。
因此,為了讓頁面獲得更好的性能粪牲,我們需要占用最少的內(nèi)存古瓤,優(yōu)化內(nèi)存占用,只為執(zhí)行中的代碼保存必要的數(shù)據(jù)。一旦數(shù)據(jù)不再有用落君,可將其值設置為null釋放其引用穿香,這個做法叫做解除引用(dereferencing)。一般用于釋放全局變量的內(nèi)存绎速,因為局部變量在離開執(zhí)行環(huán)境時自動被解決引用皮获。