1.理解詞法作用域和動(dòng)態(tài)作用域
2.理解JavaScript的作用域和作用域鏈
3.理解JavaScript的執(zhí)行上下文棧九巡,可以應(yīng)用堆棧信息快速定位問(wèn)題
4.this的原理以及幾種不同使用場(chǎng)景的取值
5.閉包的實(shí)現(xiàn)原理和作用掂骏,可以列舉幾個(gè)開(kāi)發(fā)中閉包的實(shí)際應(yīng)用
6.理解堆棧溢出和內(nèi)存泄漏的原理,如何防止
7.如何處理循環(huán)的異步操作
1.理解詞法作用域和動(dòng)態(tài)作用域
詞法作用域阅嘶,也叫靜態(tài)作用域儒将,它的作用域是指在詞法分析階段就確定了,不會(huì)改變烘嘱。動(dòng)態(tài)作用域是在運(yùn)行時(shí)根據(jù)程序的流程信息來(lái)動(dòng)態(tài)確定的鹦付,而不是寫(xiě)代碼時(shí)進(jìn)行靜態(tài)確定的尚粘。
需要明確的是,Javascript并不具有動(dòng)態(tài)作用域敲长,它只有詞法作用域背苦,簡(jiǎn)單明了。但是潘明,它的 eval()
、with
秕噪、this
機(jī)制某種程度上很像動(dòng)態(tài)作用域钳降,使用上要特別注意。
2.理解JavaScript的作用域和作用域鏈
作用域是在運(yùn)行代碼中的某些特定部分中的變量腌巾,函數(shù)和對(duì)象的可訪問(wèn)性遂填。作用于決定了代碼區(qū)塊中變量和其他資源的可見(jiàn)性。作用域最大的用處就是隔離變量澈蝙,不同作用域下同名變量不會(huì)有沖突吓坚。
ES6 之前 JavaScript 沒(méi)有塊級(jí)作用域,只有全局作用域和函數(shù)作用域。ES6 的到來(lái)灯荧,為我們提供了‘塊級(jí)作用域’,可通過(guò)新增命令 let 和 const 來(lái)體現(xiàn)礁击。
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈逗载。由子級(jí)作用域返回父級(jí)作用域中尋找變量哆窿,就叫做作用域鏈。作用域鏈?zhǔn)潜WC執(zhí)行環(huán)境有權(quán)訪問(wèn)的所有變量和函數(shù)的有序訪問(wèn)厉斟。
延長(zhǎng)作用域鏈:
執(zhí)行環(huán)境的類(lèi)型只有兩種挚躯,全局和局部(函數(shù))。但是有些語(yǔ)句可以在作用域鏈的前端臨時(shí)增加一個(gè)變量對(duì)象擦秽,該變量對(duì)象會(huì)在代碼執(zhí)行后被移除码荔。
具體來(lái)說(shuō)就是執(zhí)行這兩個(gè)語(yǔ)句時(shí),作用域鏈都會(huì)得到加強(qiáng)感挥。
1缩搅、try - catch 語(yǔ)句的catch塊;會(huì)創(chuàng)建一個(gè)新的變量對(duì)象链快,包含的是被拋出的錯(cuò)誤對(duì)象的聲明誉己。
2、with 語(yǔ)句域蜗。with 語(yǔ)句會(huì)將指定的對(duì)象添加到作用域鏈中
3.理解JavaScript的執(zhí)行上下文棧巨双,可以應(yīng)用堆棧信息快速定位問(wèn)題
執(zhí)行上下文是評(píng)估和執(zhí)行JavaScript代碼的環(huán)境的抽象概念噪猾。每當(dāng)JavaScript代碼在運(yùn)行的時(shí)候,它都是在執(zhí)行上下文中運(yùn)行筑累。
執(zhí)行棧袱蜡,也就是其他編程語(yǔ)言中所說(shuō)的“調(diào)用棧”慢宗,是一種擁有LIFO(后進(jìn)先出)數(shù)據(jù)結(jié)構(gòu)的棧坪蚁,被用來(lái)存儲(chǔ)代碼運(yùn)行時(shí)創(chuàng)建的所有執(zhí)行上下文。
執(zhí)行上下文總共有三種類(lèi)型
- 全局執(zhí)行上下文
- 函數(shù)執(zhí)行上下文
- Eval函數(shù)執(zhí)行上下文
執(zhí)行上下文的聲明周期包含三個(gè)階段創(chuàng)建階段
→執(zhí)行階段
→回收階段
創(chuàng)建階段
- 創(chuàng)建變量對(duì)象
- 創(chuàng)建作用域鏈
- 確定this 指向
4.this的原理以及幾種不同使用場(chǎng)景的取值
場(chǎng)景一:構(gòu)造函數(shù)
所謂構(gòu)造函數(shù)就是用來(lái)new對(duì)象的函數(shù)镜沽。其實(shí)嚴(yán)格來(lái)說(shuō)敏晤,所有的函數(shù)都可以new一個(gè)對(duì)象,但是有些函數(shù)的定義是為了new一個(gè)對(duì)象缅茉,而有些函數(shù)則不是嘴脾。另外注意,構(gòu)造函數(shù)的函數(shù)名第一個(gè)字母大寫(xiě)(規(guī)則約定)蔬墩。例如:Object译打、Array、Function等
function Foo(){
this.name='Apple'拇颅,
this.type='fruit';
console.log(this);//Foo{name:'Apple',type:'fruit'}
}
var f1=new Foo();
console.log(f1.name,f1.type)//Apple,fruit
以上代碼中奏司,如果函數(shù)作為構(gòu)造函數(shù)用,那么其中的this就代表它即將new出來(lái)的對(duì)象樟插。
注意:
以上僅限new Foo()的情況韵洋,即Foo函數(shù)作為構(gòu)造函數(shù)的情況。如果直接調(diào)用Foo函數(shù)岸夯,而不是new Foo()麻献,情況就大不一樣了。
function Foo(){
this.name='Apple'猜扮,
this.type='fruit';
console.log(this);//Window{top:Window,window:Window……}
}
Foo();
這種情況下this是window勉吻。
在構(gòu)造函數(shù)的prototype中,this代表什么旅赢。
function Fn(){
this.name='Apple'齿桃,
this.type='fruit';
}
Fn.prototype.getName=function(){
console.log(this.name)
}
var f1=new Fn();
f1.getName();//Apple
如上代碼,在Fn.prototype.getName函數(shù)中煮盼,this指向的是f1對(duì)象短纵。因此可以通過(guò)this.name獲取f1.name的值。
其實(shí)僵控,不僅僅是構(gòu)造函數(shù)的prototype香到,即便是在整個(gè)原型鏈中,this代表的也都是當(dāng)前對(duì)象的值。
場(chǎng)景二:函數(shù)作為對(duì)象的一個(gè)屬性
如果函數(shù)作為對(duì)象的一個(gè)屬性時(shí)悠就,并且作為對(duì)象的一個(gè)屬性被調(diào)用時(shí)千绪,函數(shù)中的this指向該對(duì)象。
var obj={
x:10,
fn:function(){
console.log(this);//Object{x:10,fn:function}
}
}
obj.fn();
以上代碼中梗脾,fn不僅作為一個(gè)對(duì)象的一個(gè)屬性荸型,而且的確是作為對(duì)象的一個(gè)屬性被調(diào)用。結(jié)果this就是obj對(duì)象炸茧。
var obj={
x:10,
fn:function(){
console.log(this); //Window{top:Window,window:Window……}
}
}
var fn1=obj.fn;
fn1();
以上代碼瑞妇,如果fn函數(shù)被復(fù)制到了另一個(gè)變量中,并沒(méi)有作為obj的一個(gè)屬性被調(diào)用梭冠,那么this的值就是window辕狰,this.x就是undefined。
場(chǎng)景三:函數(shù)用call或者apply調(diào)用
當(dāng)一個(gè)函數(shù)被call和apply調(diào)用時(shí)控漠,this的值就取傳入的對(duì)象的值柳琢。
var obj = {
x:10
}
var fn = function(){
console.log(this); //Object{x:10}
console.log(this.x); //10
}
fn.call(obj);
場(chǎng)景四:全局&調(diào)用普通函數(shù)
在全局環(huán)境下,this永遠(yuǎn)是window润脸。
console.log(this === window);//true
普通函數(shù)在調(diào)用時(shí),其中的this也都是window他去。
var x= 10;
var fn = function (){
console.log(this);//Window{top:Window;window:Window……}
console.log(this.x);//10
}
fn()
下面情況要注意:
var obj = {
x:10,
fn:function(){
function f(){
console.log(this)//Window{top:Window;window:Window……}
console.log(this.x);//undefined
}
f();
}
}
obj.fn();
函數(shù)f雖然是在obj.fn內(nèi)部定義的毙驯,但是它仍然是一個(gè)普通的函數(shù),this仍然指向window灾测。
5.閉包的實(shí)現(xiàn)原理和作用爆价,可以列舉幾個(gè)開(kāi)發(fā)中閉包的實(shí)際應(yīng)用
閉包是一個(gè)擁有許多變量和綁定了這些變量的環(huán)境表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分媳搪。換句話說(shuō)铭段,JavaScript中所有的function都是一個(gè)閉包。
一般來(lái)說(shuō)秦爆,嵌套的function所產(chǎn)生的閉包更為強(qiáng)大序愚。
function a() {
var i = 0;
function b() { alert(++i); } //函數(shù)b嵌套在函數(shù)a內(nèi)部;
return b;//函數(shù)a返回函數(shù)b等限。
}
var c = a();
c();
這樣在執(zhí)行完var c=a()后爸吮,變量c實(shí)際上是指向了函數(shù)b,再執(zhí)行c()后就會(huì)彈出一個(gè)窗口顯示i的值(第一次為1)望门。這段代碼其實(shí)就創(chuàng)建了一個(gè)閉包形娇,為什么?因?yàn)楹瘮?shù)a外的變量c引用了函數(shù)a內(nèi)的函數(shù)b筹误,就是說(shuō):
當(dāng)函數(shù)a的內(nèi)部函數(shù)b被函數(shù)a外的一個(gè)變量引用的時(shí)候桐早,就創(chuàng)建了一個(gè)閉包.
所謂“閉包”,就是在構(gòu)造函數(shù)體內(nèi)定義另外的函數(shù)作為目標(biāo)對(duì)象的方法函數(shù),而這個(gè)對(duì)象的方法函數(shù)反過(guò)來(lái)引用外層函數(shù)體中的臨時(shí)變量哄酝。這使得只要目標(biāo) 對(duì)象在生存期內(nèi)始終能保持其方法友存,就能間接保持原構(gòu)造函數(shù)體當(dāng)時(shí)用到的臨時(shí)變量值。盡管最開(kāi)始的構(gòu)造函數(shù)調(diào)用已經(jīng)結(jié)束炫七,臨時(shí)變量的名稱(chēng)也都消失了爬立,但在目 標(biāo)對(duì)象的方法內(nèi)卻始終能引用到該變量的值,而且該值只能通這種方法來(lái)訪問(wèn)万哪。即使再次調(diào)用相同的構(gòu)造函數(shù)侠驯,但只會(huì)生成新對(duì)象和方法,新的臨時(shí)變量只是對(duì)應(yīng)新 的值奕巍,和上次那次調(diào)用的是各自獨(dú)立的吟策。
簡(jiǎn)而言之,閉包的作用就是在a執(zhí)行完并返回后的止,閉包使得Javascript的垃圾回收機(jī)制GC不會(huì)收回a所占用的資源檩坚,因?yàn)閍的內(nèi)部函數(shù)b的執(zhí)行需要依賴(lài)a中的變量。這是對(duì)閉包作用的非常直白的描述诅福,不專(zhuān)業(yè)也不嚴(yán)謹(jǐn)匾委,但大概意思就是這樣,理解閉包需要循序漸進(jìn)的過(guò)程氓润。
當(dāng)定義函數(shù)a的時(shí)候赂乐,js解釋器會(huì)將函數(shù)a的作用域鏈(scope chain)
設(shè)置為定義a時(shí)a所在的“環(huán)境”,如果a是一個(gè)全局函數(shù)咖气,則scope chain
中只有window對(duì)象挨措。
當(dāng)執(zhí)行函數(shù)a的時(shí)候,a會(huì)進(jìn)入相應(yīng)的執(zhí)行環(huán)境(excution context)
崩溪。
在創(chuàng)建執(zhí)行環(huán)境的過(guò)程中浅役,首先會(huì)為a添加一個(gè)scope
屬性,即a的作用域
伶唯,其值就為第1步中的scope chain觉既。即a.scope=a的作用域鏈
。
然后執(zhí)行環(huán)境會(huì)創(chuàng)建一個(gè)活動(dòng)對(duì)象(call object)乳幸》芫龋活動(dòng)對(duì)象也是一個(gè)擁有屬性的對(duì)象,但它不具有原型而且不能通過(guò)JavaScript代碼直接訪問(wèn)反惕。創(chuàng)建完活動(dòng)對(duì)象后尝艘,把活動(dòng)對(duì)象添加到a的作用域鏈的最頂端。此時(shí)a的作用域鏈包含了兩個(gè)對(duì)象:a的活動(dòng)對(duì)象和window對(duì)象姿染。
下一步是在活動(dòng)對(duì)象上添加一個(gè)arguments屬性背亥,它保存著調(diào)用函數(shù)a時(shí)所傳遞的參數(shù)秒际。
最后把所有函數(shù)a的形參和內(nèi)部的函數(shù)b的引用也添加到a的活動(dòng)對(duì)象上。在這一步中狡汉,完成了函數(shù)b的的定義娄徊,因此如同第3步,函數(shù)b的作用域鏈被設(shè)置為b所被定義的環(huán)境盾戴,即a的作用域寄锐。
到此,整個(gè)函數(shù)a從定義到執(zhí)行的步驟就完成了尖啡。此時(shí)a返回函數(shù)b的引用給c橄仆,又函數(shù)b的作用域鏈包含了對(duì)函數(shù)a的活動(dòng)對(duì)象的引用,也就是說(shuō)b可以訪問(wèn)到a中定義的所有變量和函數(shù)衅斩。函數(shù)b被c引用盆顾,函數(shù)b又依賴(lài)函數(shù)a,因此函數(shù)a在返回后不會(huì)被GC回收畏梆。
當(dāng)函數(shù)b執(zhí)行的時(shí)候亦會(huì)像以上步驟一樣您宪。因此,執(zhí)行時(shí)b的作用域鏈包含了3個(gè)對(duì)象:b的活動(dòng)對(duì)象奠涌、a的活動(dòng)對(duì)象和window對(duì)象宪巨,如下圖所示:
如圖所示,當(dāng)在函數(shù)b中訪問(wèn)一個(gè)變量的時(shí)候溜畅,搜索順序是:
先搜索自身的活動(dòng)對(duì)象揖铜,如果存在則返回,如果不存在將繼續(xù)搜索函數(shù)a的活動(dòng)對(duì)象达皿,依次查找,直到找到為止贿肩。
如果函數(shù)b存在prototype原型對(duì)象峦椰,則在查找完自身的活動(dòng)對(duì)象后先查找自身的原型對(duì)象,再繼續(xù)查找汰规。這就是Javascript中的變量查找機(jī)制汤功。
如果整個(gè)作用域鏈上都無(wú)法找到,則返回undefined溜哮。
小結(jié)滔金,本段中提到了兩個(gè)重要的詞語(yǔ):函數(shù)的定義與執(zhí)行。文中提到函數(shù)的作用域是在定義函數(shù)時(shí)候就已經(jīng)確定茂嗓,而不是在執(zhí)行的時(shí)候確定餐茵。用一段代碼來(lái)說(shuō)明這個(gè)問(wèn)題
function f(x) {
var g = function () { return x; }
return g;
}
var h = f(1);
alert(h());
這段代碼中變量h指向了f中的那個(gè)匿名函數(shù)(由g返回)。
假設(shè)函數(shù)h的作用域是在執(zhí)行alert(h())確定的述吸,那么此時(shí)h的作用域鏈?zhǔn)牵篽的活動(dòng)對(duì)象->alert的活動(dòng)對(duì)象->window對(duì)象忿族。
假設(shè)函數(shù)h的作用域是在定義時(shí)確定的,就是說(shuō)h指向的那個(gè)匿名函數(shù)在定義的時(shí)候就已經(jīng)確定了作用域。那么在執(zhí)行的時(shí)候道批,h的作用域鏈為:h的活動(dòng)對(duì)象->f的活動(dòng)對(duì)象->window對(duì)象错英。
如果第一種假設(shè)成立,那輸出值就是undefined隆豹;如果第二種假設(shè)成立椭岩,輸出值則為1。
運(yùn)行結(jié)果證明了第2個(gè)假設(shè)是正確的璃赡,說(shuō)明函數(shù)的作用域確實(shí)是在定義這個(gè)函數(shù)的時(shí)候就已經(jīng)確定了判哥。
閉包的應(yīng)用場(chǎng)景
保護(hù)函數(shù)內(nèi)的變量安全。以最開(kāi)始的例子為例鉴吹,函數(shù)a中i只有函數(shù)b才能訪問(wèn)姨伟,而無(wú)法通過(guò)其他途徑訪問(wèn)到,因此保護(hù)了i的安全性豆励。
在內(nèi)存中維持一個(gè)變量夺荒。依然如前例,由于閉包良蒸,函數(shù)a中i的一直存在于內(nèi)存中技扼,因此每次執(zhí)行c(),都會(huì)給i自加1嫩痰。
通過(guò)保護(hù)變量的安全實(shí)現(xiàn)JS私有屬性和私有方法(不能被外部訪問(wèn))
私有屬性和方法在Constructor外是無(wú)法被訪問(wèn)的
function Constructor(...) {
var that = this;
var membername = value;
function membername(...) {...}
}
以上3點(diǎn)是閉包最基本的應(yīng)用場(chǎng)景剿吻,很多經(jīng)典案例都源于此。
在Javascript中串纺,如果一個(gè)對(duì)象不再被引用丽旅,那么這個(gè)對(duì)象就會(huì)被GC回收。如果兩個(gè)對(duì)象互相引用纺棺,而不再被第3者所引用榄笙,那么這兩個(gè)互相引用的對(duì)象也會(huì)被回收。因?yàn)楹瘮?shù)a被b引用祷蝌,b又被a外的c引用茅撞,這就是為什么函數(shù)a執(zhí)行后不會(huì)被回收的原因。
var聲明的變量由于不存在塊級(jí)作用域所以可以在全局環(huán)境中調(diào)用巨朦,而let聲明的變量由于存在塊級(jí)作用域所以不能在全局環(huán)境中調(diào)用米丘。
var a=[];
for(var i=0;i<10;i++){
a[i]=function(){
console.log(i);
};
}
a[6](); //10
var b=[];
for(let i=0;i<10;i++){
b[i]=function(){
console.log(i);
};
}
b[6]();//6
6.理解堆棧溢出和內(nèi)存泄漏的原理,如何防止
內(nèi)存泄露:指一塊被分配的內(nèi)存既不能使用糊啡,又不能回收拄查,直到瀏覽器進(jìn)程結(jié)束
JS的回收機(jī)制
JavaScript垃圾回收的機(jī)制很簡(jiǎn)單:找出不再使用的變量,然后釋放掉其占用的內(nèi)存棚蓄,但是這個(gè)過(guò)程不是實(shí)時(shí)的靶累,因?yàn)槠溟_(kāi)銷(xiāo)比較大腺毫,所以垃圾回收系統(tǒng)(GC)會(huì)按照固定的時(shí)間間隔,周期性的執(zhí)行。
到底哪個(gè)變量是沒(méi)有用的挣柬?所以垃圾收集器必須跟蹤到底哪個(gè)變量沒(méi)用潮酒,對(duì)于不再有用的變量打上標(biāo)記,以備將來(lái)收回其內(nèi)存邪蛔。用于標(biāo)記的無(wú)用變量的策略可能因?qū)崿F(xiàn)而有所區(qū)別急黎,通常情況下有兩種實(shí)現(xiàn)方式:標(biāo)記清除
和引用計(jì)數(shù)
。引用計(jì)數(shù)不太常用侧到,標(biāo)記清除較為常用
標(biāo)記清除
js中最常用的垃圾回收方式就是標(biāo)記清除勃教。當(dāng)變量進(jìn)入環(huán)境時(shí),例如匠抗,在函數(shù)中聲明一個(gè)變量故源,就將這個(gè)變量標(biāo)記為“進(jìn)入環(huán)境”。從邏輯上講汞贸,永遠(yuǎn)不能釋放進(jìn)入環(huán)境的變量所占用的內(nèi)存绳军,因?yàn)橹灰獔?zhí)行流進(jìn)入相應(yīng)的環(huán)境,就可能會(huì)用到它們矢腻。而當(dāng)變量離開(kāi)環(huán)境時(shí)门驾,則將其標(biāo)記為“離開(kāi)環(huán)境”。
function test(){
var a=10;//被標(biāo)記多柑,進(jìn)入環(huán)境
var b=20;//被標(biāo)記奶是,進(jìn)入環(huán)境
}
test();//執(zhí)行完畢之后a、b又被標(biāo)記離開(kāi)環(huán)境竣灌,被回收
引用計(jì)數(shù)
引用計(jì)數(shù)的含義是跟蹤記錄每個(gè)值被引用的次數(shù)聂沙。當(dāng)聲明了一個(gè)變量并將一個(gè)引用類(lèi)型值(function object array)賦給該變量時(shí),則這個(gè)值的引用次數(shù)就是1初嘹。如果同一個(gè)值又被賦給另一個(gè)變量及汉,則該值的引用次數(shù)加1。相反削樊,如果包含對(duì)這個(gè)值引用的變量又取得了另外一個(gè)值,則這個(gè)值的引用次數(shù)減1兔毒。當(dāng)這個(gè)值的引用次數(shù)變成0時(shí)漫贞,則說(shuō)明沒(méi)有辦法再訪問(wèn)這個(gè)值了,因而就可以將其占用的內(nèi)存空間回收回來(lái)育叁。這樣迅脐,當(dāng)垃圾回收器下次再運(yùn)行時(shí),它就會(huì)釋放那些引用次數(shù)為0的值所占用的內(nèi)存豪嗽。
function test(){
var a={};//a的引用次數(shù)為0
var b=a;//a的引用次數(shù)加1谴蔑,為1
var c=a;//a的引用次數(shù)加1豌骏,為2
var b={};//a的引用次數(shù)減1,為1
}
哪些情況會(huì)造成內(nèi)存泄露
1.意外的全局變量引起的內(nèi)存泄露
function leak(){
leak="xxx";//leak成為一個(gè)全局變量隐锭,不會(huì)被回收
}
2.閉包引起的泄露
function bindEvent(){
var obj=document.createElement("XXX");
obj.οnclick=function(){
//Even if it's a empty function
}
}
閉包可以維持函數(shù)內(nèi)局部變量窃躲,使其得不到釋放。 上例定義事件回調(diào)時(shí)钦睡,由于是函數(shù)內(nèi)定義函數(shù)蒂窒,并且內(nèi)部函數(shù)--事件回調(diào)的引用外暴了,形成了閉包荞怒。
解決之道洒琢,將事件處理函數(shù)定義在外部,解除閉包,或者在定義事件處理函數(shù)的外部函數(shù)中褐桌,刪除對(duì)dom的引用衰抑。
//將事件處理函數(shù)定義在外部
function onclickHandler(){
//do something
}
function bindEvent(){
var obj=document.createElement("XXX");
obj.οnclick=onclickHandler;
}
//在定義事件處理函數(shù)的外部函數(shù)中,刪除對(duì)dom的引用
function bindEvent(){
var obj=document.createElement("XXX");
obj.οnclick=function(){
//Even if it's a empty function
}
obj=null;
}
3.沒(méi)有清理的DOM元素引用
var elements={
button: document.getElementById("button"),
image: document.getElementById("image"),
text: document.getElementById("text")
};
function doStuff(){
image.src="http://some.url/image";
button.click():
console.log(text.innerHTML)
}
function removeButton(){
document.body.removeChild(document.getElementById('button'))
}
- 被遺忘的定時(shí)器或者回調(diào)
var someResouce=getData();
setInterval(function(){
var node=document.getElementById('Node');
if(node){
node.innerHTML=JSON.stringify(someResouce)
}
},1000)
這樣的代碼很常見(jiàn), 如果 id 為 Node 的元素從 DOM 中移除, 該定時(shí)器仍會(huì)存在, 同時(shí), 因?yàn)榛卣{(diào)函數(shù)中包含對(duì) someResource 的引用, 定時(shí)器外面的 someResource 也不會(huì)被釋放荧嵌。
5.子元素存在引起的內(nèi)存泄露
黃色是指直接被 js變量所引用呛踊,在內(nèi)存里,紅色是指間接被 js變量所引用完丽,如上圖恋技,refB 被 refA 間接引用,導(dǎo)致即使 refB 變量被清空逻族,也是不會(huì)被回收的子元素 refB 由于 parentNode 的間接引用蜻底,只要它不被刪除,它所有的父元素(圖中紅色部分)都不會(huì)被刪除聘鳞。
怎樣避免內(nèi)存泄露
1)減少不必要的全局變量薄辅,或者生命周期較長(zhǎng)的對(duì)象,及時(shí)對(duì)無(wú)用的數(shù)據(jù)進(jìn)行垃圾回收抠璃;
2)注意程序邏輯站楚,避免“死循環(huán)”之類(lèi)的 ;
3)避免創(chuàng)建過(guò)多的對(duì)象 原則:不用了的東西要及時(shí)歸還搏嗡。
7.如何處理循環(huán)的異步操作
1.不需要等待結(jié)果的異步循環(huán)
async function processArray(array) {
array.forEach(async (item) => {
await func(item);
})
console.log('Done!');
}
function delay() {
return new Promise(resolve => setTimeout(resolve, 300));
}
async function delayedLog(item) {
// notice that we can await a function
// that returns a promise
await delay();
console.log(item);
}
async function processArray(array) {
array.forEach(async (item) => {
await delayedLog(item);
})
console.log('Done!');
}
processArray([1, 2, 3]);
結(jié)果輸出為
Done!
1
2
3
如果不需要等結(jié)果這樣寫(xiě)是ok的窿春,但是在大多數(shù)案例里這不是個(gè)很好的邏輯。
2.線性處理數(shù)組
要等待結(jié)果采盒,我們應(yīng)該返回到老式的 for 循環(huán)旧乞,但這一次為了更好的可讀性我們可以使用現(xiàn)代寫(xiě)法 for..of
。
sync function processArray(array) {
for (const item of array) {
await delayedLog(item);
}
console.log('Done!');
}
結(jié)果輸出:
1
2
3
Done!
該代碼將依次處理每一項(xiàng)磅氨。但是我們可以使用并行運(yùn)行尺栖。
3.并行處理數(shù)組
async function processArray(array) {
// map array to promises
const promises = array.map(delayedLog);
// wait until all promises are resolved
await Promise.all(promises);
console.log('Done!');
}
這段代碼將并行運(yùn)行許多delayLog 任務(wù)。但是對(duì)于非常大的數(shù)組要小心(并行的任務(wù)太多對(duì)CPU或內(nèi)存來(lái)說(shuō)可能比較吃力)烦租。
也不要混淆“并行”與真正的線程和并行延赌。該代碼不能保證真正的并行執(zhí)行除盏。這取決于您的 item函數(shù)(在本演示中是delayedLog)。網(wǎng)絡(luò)請(qǐng)求挫以、webworker 和其他一些任務(wù)可以并行執(zhí)行者蠕。
參考鏈接:
http://www.reibang.com/p/70b38c7ab69c
http://www.reibang.com/p/2c3c8890dff0
http://www.frontopen.com/1702.html
https://www.cnblogs.com/wangfupeng1988/p/3988422.html
https://blog.csdn.net/michael8512/article/details/77888000
http://www.reibang.com/p/9dd1014f7f1c