人類在白色的底色上描繪圖畫,地球在黑色的底色上創(chuàng)造生命。
變量、作用域與內(nèi)存
JavaScript的變量可以說是獨(dú)樹一幟。只需要一個(gè)(或兩個(gè)等)關(guān)鍵字(const,let)就可以創(chuàng)建變量,創(chuàng)建時(shí)不考慮變量的類型,這是其他語言少有的強(qiáng)大功能。當(dāng)然強(qiáng)大的功能總是伴隨著問題。
值
原始值:Undefined,Null,Boolean,Number,String,Symbol。它們都是按值訪問的,因此我們操作的就是儲(chǔ)存在變量中的實(shí)際值。
引用值:保存在內(nèi)存中的對(duì)象。引用值是按引用訪問的(類似指針),我們操作的實(shí)際上是引用而不是對(duì)象本身。
兩者的不同:
- 原始值沒有屬性,引用值有,但如果使用
new
關(guān)鍵字,就會(huì)為原始值創(chuàng)造對(duì)象,從而使他們可以獲得屬性。(實(shí)際上字符串原始值,即使不用構(gòu)造函數(shù)創(chuàng)建也有l(wèi)ength等屬性和方法。但是想要添加屬性和方法就必須要使用包裝類型對(duì)象) - 原始值在復(fù)制的時(shí)候會(huì)獲得一個(gè)獨(dú)立的副本,而引用值只是復(fù)制了這個(gè)引用,它們指向同一個(gè)對(duì)象。(類似指針那樣理解)
傳遞參數(shù)
ECMAscript中所有的函數(shù)參數(shù)都是按值傳遞的,也就說明函數(shù)內(nèi)的參數(shù)和函數(shù)外的變量沒有什么關(guān)系,只是值的賦值。即使是引用值也只是傳值(雖然他的賦值是傳址的),這里我的理解是傳入指針本身的內(nèi)容而非指向的地址本身。實(shí)際上,函數(shù)內(nèi)部的參數(shù)是一個(gè)局部變量。
下面的例子,可以幫助我們理解這個(gè)問題:
function setName(obj){
obj.name = 'jake'; //obj這個(gè)"指針"指向的地址(假設(shè)地址為1,則person的地址也為1)的name為jake,則1地址內(nèi)的name被修改
obj = new Object(); //obj不再指向1,而是指向其他地址(假設(shè)為2),1中的name值obj取不到了
obj.name = 'james'; //2中的name為james,與1無關(guān),也就與person無關(guān)
}
let person = new Object();
setName(person);
console.log(person.name);//jake
console.log(obj);//obj沒有定義,他在函數(shù)執(zhí)行結(jié)束的時(shí)候就被銷毀了
instance用來確定對(duì)象的類型
上下文與作用域
執(zhí)行上下文
紅寶書中對(duì)于執(zhí)行上下文的解釋并沒有那么深入,我在學(xué)習(xí)的過程中也在網(wǎng)上閱讀了一些相關(guān)文章,對(duì)于JavaScript的執(zhí)行上下文邏輯有了一點(diǎn)淺顯的理解。日后在對(duì)js有更深入了解之后,會(huì)詳細(xì)介紹,現(xiàn)在先簡單說一下我的理解,如果后續(xù)發(fā)現(xiàn)不嚴(yán)謹(jǐn)?shù)牡胤綍?huì)隨時(shí)修改,也歡迎指正。
執(zhí)行上下文本質(zhì)是一個(gè)變量對(duì)象,包含了上下文中定義的所有變量和函數(shù)。JavaScript在運(yùn)行代碼之前,會(huì)對(duì)整個(gè)代碼進(jìn)行一次掃描,進(jìn)行一些配置(提升,配置全局上下文等),在配置全局上下文的時(shí)候,會(huì)將整個(gè)代碼的變量和函數(shù)聲明全部放入其中,但不會(huì)進(jìn)行語句操作(賦值等),同時(shí),全局上下文會(huì)永遠(yuǎn)放在執(zhí)行上下文棧的最底部。在完成這些后,才會(huì)運(yùn)行每一個(gè)語句。當(dāng)代碼執(zhí)行流進(jìn)入函數(shù)時(shí),就會(huì)創(chuàng)建函數(shù)上下文,當(dāng)然它也是個(gè)對(duì)象,它會(huì)將當(dāng)前函數(shù)中的變量和函數(shù)聲明放入其中,同時(shí)它也會(huì)被放入執(zhí)行上下棧中。函數(shù)上下文和全局上下文的一些操作是相通,如果函數(shù)中還有函數(shù),那就是不斷往上下文棧中push的套娃過程。當(dāng)函數(shù)執(zhí)行完畢,它的上下文就會(huì)被銷毀。因此外部無法訪問內(nèi)部的變量,但是內(nèi)部卻可以訪問外部的。
當(dāng)你在函數(shù)內(nèi)部使用變量時(shí),如果它并沒有在該函數(shù)內(nèi)部定義,那么JavaScript引擎會(huì)幫你找該函數(shù)的外層函數(shù),這樣一層層的找下去直到最外層。這和對(duì)象中屬性和方法的搜索機(jī)制很像(一個(gè)是作用域鏈一個(gè)是原型鏈)。作用域鏈就是把上下文棧串了起來,前面的內(nèi)部的(棧上層的)上下文,可以使用后面的外部的(棧下層的)上下文中的一切,反之則不行。
除了上面兩種執(zhí)行上下文外,eval()調(diào)用內(nèi)部存在第三種上下文(在這里就不細(xì)說了)。還有一些語句會(huì)在作用域鏈前端臨時(shí)添加一個(gè)上下文。
- try/catch語句中的catch塊:在前端創(chuàng)建一個(gè)新的對(duì)象,包含要拋出的錯(cuò)誤對(duì)象聲明
- with語句:在前端添加指定對(duì)象
作用域
變量如果不使用任何關(guān)鍵字聲明,會(huì)被添加到全局上下文。
- var的函數(shù)作用域聲明
使用var會(huì)將變量自動(dòng)添加到最近的執(zhí)行上下文中。 - let的塊級(jí)作用域聲明
作用域由最近一對(duì)花括號(hào)界定(對(duì)于for循環(huán)等,在循環(huán)條件中l(wèi)et聲明的對(duì)象,{}內(nèi)可以使用) - const的常量聲明(也是塊作用域)
使用const聲明變量時(shí)必須同時(shí)進(jìn)行初始化。并且此變量不允許修改,但如果變量是對(duì)象,則對(duì)象中的屬性或方法可以修改。這個(gè)特點(diǎn)讓我聯(lián)想到Vue中的props配置,同樣是不允許修改,但是如果props傳遞了對(duì)象,則可以通過v-model對(duì)這個(gè)對(duì)象內(nèi)部進(jìn)行修改。不知道是Vue開發(fā)者留下的問題,還是原生js的const聲明的問題(現(xiàn)在看來關(guān)系不大,雖然兩者看起來還挺像)。
- 這個(gè)問題今天面試官給我做了部分解釋:不允許修改是因?yàn)榉奖銛?shù)據(jù)的維護(hù),一個(gè)父組件會(huì)有多個(gè)子組件,如果多個(gè)子組件同時(shí)修改數(shù)據(jù)會(huì)導(dǎo)致數(shù)據(jù)紊亂。雖然沒有解釋Vue開發(fā)人員為什么留下一個(gè)小“漏洞”。(這個(gè)問題等我入職的時(shí)候再問(手動(dòng)狗頭))
- 寫上面這句話的時(shí)候,我又覺得和OS中獨(dú)木橋類問題有些類似。多個(gè)線程走一個(gè)獨(dú)木橋時(shí),必須通過原語保證過橋順序不亂,當(dāng)只有當(dāng)一個(gè)線程放開對(duì)橋的使用時(shí),下一個(gè)線程才可以上橋。這個(gè)方法就實(shí)現(xiàn)了多個(gè)線程過一個(gè)橋的方法。如果組件也可以自由修改props數(shù)據(jù),那就要采取類似方法,保證修改時(shí)沒有其他組件正在使用,也不允許其他組件“插隊(duì)”。
無論JavaScript是否對(duì)變量聲明進(jìn)行提升,都不建議在變量未聲明的時(shí)候就使用變量。
垃圾回收
對(duì)代碼中不再使用的變量進(jìn)行及時(shí)銷毀,釋放其內(nèi)存。其中的關(guān)鍵就是對(duì)于變量的追蹤與標(biāo)記。瀏覽器的發(fā)展史上主要使用過兩種標(biāo)記策略:標(biāo)記清理和引用計(jì)數(shù)
標(biāo)記清理:這也是JavaScript最常用的垃圾回收策略。使用標(biāo)記方法對(duì)不再使用的變量進(jìn)行標(biāo)記。
引用計(jì)數(shù):變量每次被引用,值+1,當(dāng)沒有變量引用該變量時(shí)(值為0),則進(jìn)行回收。如果兩個(gè)對(duì)象對(duì)彼此互相引用,則會(huì)造成循環(huán)引用的問題
性能提升
多使用const和let聲明變量
共享和刪除:避免“先創(chuàng)建再補(bǔ)充”式的動(dòng)態(tài)屬性賦值,在構(gòu)造函數(shù)中一次性聲明所有屬性,同時(shí)避免delete動(dòng)態(tài)刪除屬性,而是將不在使用的屬性值設(shè)置為null
內(nèi)存泄漏:閉包中的回調(diào)會(huì)引用外部的變量,導(dǎo)致外部變量一直被引用無法回收。文章來源:http://www.zghlxwxcb.cn/news/detail-480771.html
靜態(tài)分配:在函數(shù)中創(chuàng)建對(duì)象,如果函數(shù)不斷使用,就會(huì)不斷有對(duì)象內(nèi)存需要回收,從而不斷調(diào)用垃圾回收程序??梢栽谕獠縿?chuàng)建好對(duì)象,再將其以參數(shù)的形式傳入函數(shù),從而進(jìn)行靜態(tài)分配。文章來源地址http://www.zghlxwxcb.cn/news/detail-480771.html
到了這里,關(guān)于前端進(jìn)化筆記-JavaScript(三)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!