本章我們主要是通過(guò)變量來(lái)使用基礎(chǔ)值和引用值粘姜,理解執(zhí)行上下文(作用域)和理解垃圾回收等
原始值和引用值
js有兩種不同類型的數(shù)據(jù):原始值和引用值。
原始值就是最簡(jiǎn)單的值(undefined熔酷、string孤紧、number、boolean拒秘、Null号显、smybol)
引用值就是由多個(gè)值構(gòu)成的對(duì)象
我認(rèn)為這兩個(gè)值我們最容易弄錯(cuò)的便是他們?cè)趥髦档臅r(shí)候
//基礎(chǔ)值在傳值的時(shí)候是把值賦值給另一個(gè)變量,相當(dāng)于副本
var a = 2;
varb b = a;
//如果把b重新賦值躺酒,a不會(huì)受任何影響押蚤;
//而引用值就不一樣了
var obj1 = new Object();
let obj2 = obj1;
obj2.name="哈哈";
console.log(obj1.name) //輸入哈哈
//引用值傳值的時(shí)候是把復(fù)制的對(duì)象的指針,相當(dāng)于我復(fù)制了一把我家的門鑰匙阴颖,都還是指向同一個(gè)地址活喊,所以修改一個(gè),另一個(gè)對(duì)象也會(huì)受影響
再提一句:其實(shí)我們只要記住原生值和引用值的特性量愧,不管在傳值還是傳參數(shù)钾菊,一般都不會(huì)弄錯(cuò)
執(zhí)行上下文與作用域
有些東西可能不知道什么是執(zhí)行上下文,其實(shí)我們就可以把他理解為當(dāng)前代碼的運(yùn)行環(huán)境(也就是作用域)在瀏覽器中偎肃,我們所說(shuō)的全局上下文煞烫,就是window對(duì)象。
這里提上一嘴:var定義的全局變量和函數(shù)是window對(duì)象的屬性和方法累颂,但是let和const的頂級(jí)聲明就不會(huì)定義在全局上下文(也就是window對(duì)象)中滞详,但在作用域中解析的效果是一樣的。
函數(shù)參數(shù)是當(dāng)前上下文中的變量紊馏。
為什么要說(shuō)一下這個(gè)呢料饥,是因?yàn)橐屛覀兠靼鬃饔糜虻膯?wèn)題,子作用域可以訪問(wèn)父級(jí)作用域朱监,但父級(jí)作用域不能訪問(wèn)子級(jí)作用域
var a = 1;
function fn(){
var b = 2;
console.log(a)岸啡;//輸出1,子級(jí)訪問(wèn)父級(jí)
}
console.log(b)//報(bào)錯(cuò)赫编,因?yàn)楦讣?jí)不能訪問(wèn)子級(jí)作用域
變量聲明
注意:在初始化變量之前一定要先聲明變量巡蘸,不然會(huì)被瀏覽器當(dāng)成全局作用域處理
function fn(){
age=12;
}
console.log(age) //輸出12
這里在說(shuō)一下var 、let 擂送、const
var聲明會(huì)被拿到函數(shù)或者全局作用域的頂部悦荒,位于作用域所有代碼之前,這個(gè)現(xiàn)象叫做提升嘹吨,提升的好處是在統(tǒng)一作用域下不必考慮變量是否聲明就可以直接使用搬味。但是這種在實(shí)踐中,建議不要使用
let聲明和var聲明相似,let有塊級(jí)作用域身腻,for产还、while匹厘、if等嘀趟,甚至連單獨(dú)的塊({})都是let聲明變量的作用域
for(var i = 0 ; i<10;i++){
}
console.log(i)// 輸出10
for(let i = 0 ; i<10;i++){
}
console.log(i)// 報(bào)錯(cuò)
//這里就可以說(shuō)明var只有函數(shù)作用域,在for這些中愈诚,沒(méi)有作用域她按,使用var聲明的迭代變量會(huì)泄漏到循環(huán)外部
const聲明在使用時(shí)勋桶,必須初始化為某個(gè)值
建議在實(shí)踐中汁尺,如果開發(fā)流程并不會(huì)因此受很大的影響疟呐,就應(yīng)該盡可能的使用const聲明變量豆挽,除非將來(lái)確實(shí)會(huì)修改這個(gè)變量
垃圾回收和性能提升
在js中币他,垃圾回收是計(jì)算機(jī)自己執(zhí)行的挖胃,但在什么時(shí)候執(zhí)行啃洋,這個(gè)不確定碾盐。我們有兩種垃圾回收的方法:標(biāo)記清理和引用清理
標(biāo)記清理時(shí)我們最常用的清理方式欢嘿,在運(yùn)行時(shí)衰琐,會(huì)將內(nèi)存中所有的變量進(jìn)行標(biāo)記,然后炼蹦,它會(huì)將所有在本作用域的變量以及被引用的變量的標(biāo)記去掉羡宙,如果在此之后被加上標(biāo)記的變量就會(huì)被清理
引用清理時(shí)將一個(gè)變量賦予一個(gè)引用值,該值為1掐隐,然后這個(gè)值被賦值給另一個(gè)就加一狗热,被覆蓋就減一,如果一個(gè)變量的引用值為0時(shí)虑省,就會(huì)被清理掉(不建議使用)
最佳實(shí)踐:如果一個(gè)數(shù)據(jù)不再有必要匿刮,可以把他設(shè)為null
function fn(name){
let obj = new Object();
obj.name=name;
return obj
}
let myfn = fn("haha");
//因?yàn)楹竺娌辉儆斜匾褂眠@個(gè)了 我們就可以把這個(gè)變量設(shè)置為null
//解除myfn對(duì)值的引用
myfn = null
//解除引用的關(guān)鍵是在于確保相關(guān)的值已經(jīng)不再上下文(作用域)里面了
內(nèi)存泄漏的幾種可能
- 意外聲明全局變量,是最常見的也是最容易修改的內(nèi)存泄漏問(wèn)題
function fn(){
name="哈哈"
}
- 定時(shí)器也可能會(huì)導(dǎo)致內(nèi)存泄漏
let name="haha",
setInterval(()=>{
console.log(name)
},1000)
//只要定時(shí)器一直運(yùn)轉(zhuǎn)探颈,回調(diào)函數(shù)中引用的name就一直占用著內(nèi)存熟丸,垃圾回收程序就不會(huì)清理
- 使用js閉包很容易在不知不覺間造成內(nèi)存泄漏
let out = function(){
let name = "haha";
return function(){
return name;
};
};
//調(diào)用out()會(huì)導(dǎo)致分配給name的內(nèi)存被泄露,以上代碼創(chuàng)建了一個(gè)閉包膝擂,只要返回的函數(shù)存在就不會(huì)清理name虑啤,因?yàn)殚]包一直在引用它,如果name的內(nèi)容很大,就可能出現(xiàn)問(wèn)題
總結(jié)
js的變量可以保存兩種數(shù)據(jù)類型的值:基礎(chǔ)值和引用值,基礎(chǔ)值包含Boolean,undefined架馋、null狞山、number、String叉寂、Symbol萍启。下面我們所說(shuō)這兩種值的特點(diǎn):
- 原始值大小固定,因此保存在棧內(nèi)存中;而引用值是對(duì)象勘纯,保存在堆內(nèi)存中
- 從一個(gè)變量到另一個(gè)變量復(fù)制原始值相當(dāng)于是創(chuàng)建了這個(gè)值的副本
- 從一個(gè)變量到另一個(gè)變量復(fù)制引用值實(shí)際上只會(huì)復(fù)制指針局服,因此結(jié)果是兩個(gè)變量都指向同一個(gè)對(duì)象
- 包含引用值的變量,實(shí)際上是包含的這個(gè)對(duì)象的指針而不是對(duì)象本身
- typeof操作符可以確定值的原始類型驳遵,引用類型都返回object淫奔;而instanceof操作符用于確保值的引用類型
任何變量都存在于某個(gè)執(zhí)行上下文中(也就是作用域),這個(gè)上下文決定了變量的生命周期堤结,以及他們可以訪問(wèn)的內(nèi)容有哪些
- 執(zhí)行上下文分全局上下文唆迁、塊級(jí)上下文、函數(shù)上下文(局部作用域包括塊級(jí)和函數(shù)作用域)
- 代碼執(zhí)行每一個(gè)上下文時(shí)竞穷,都會(huì)創(chuàng)建一個(gè)作用域鏈唐责,用于搜索變量和函數(shù)
- 局部作用域不僅可以訪問(wèn)自己作用域內(nèi)的變量,還可以訪問(wèn)上層作用域乃至全局作用域的內(nèi)容
- 全局作用域只能訪問(wèn)全局作用域的變量和函數(shù)瘾带,不能訪問(wèn)局部上下文中的任何數(shù)據(jù)
- 變量的執(zhí)行上下文用于確定什么時(shí)候釋放內(nèi)存
js是使用垃圾回收的編程語(yǔ)言鼠哥,我們不需要擔(dān)心內(nèi)存分配和回收。
- 離開當(dāng)前我們?cè)L問(wèn)作用域的值看政,會(huì)被標(biāo)記為可回收朴恳,然后在垃圾回收期間被刪除
- 主流的垃圾回收算法是標(biāo)記清理,即先給當(dāng)前不使用的值加上標(biāo)簽帽衙,再回來(lái)回收他的內(nèi)存
- 引用計(jì)數(shù)是另一種垃圾回收策略菜皂,需要記錄值被引用了多少次。js引擎已經(jīng)不再使用這種算法了厉萝,但在一些舊版本的IE仍然會(huì)受這種算法的影響恍飘,原因是js會(huì)訪問(wèn)非原生js對(duì)象(如DOM元素)
- 解除變量的引用不僅僅可以消除循環(huán)引用而且對(duì)垃圾回收也有幫助。為促進(jìn)內(nèi)存回收谴垫,全局對(duì)象章母、全局對(duì)象的屬性和循環(huán)引用都應(yīng)該在不需要時(shí)解除引用。