javascript基礎(chǔ)知識(shí)問(wèn)答-作用域和閉包

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)型

  1. 全局執(zhí)行上下文
  2. 函數(shù)執(zhí)行上下文
  3. 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ì)象宪巨,如下圖所示:


函數(shù)b的作用鏈

如圖所示,當(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)景

  1. 保護(hù)函數(shù)內(nèi)的變量安全。以最開(kāi)始的例子為例鉴吹,函數(shù)a中i只有函數(shù)b才能訪問(wèn)姨伟,而無(wú)法通過(guò)其他途徑訪問(wèn)到,因此保護(hù)了i的安全性豆励。

  2. 在內(nèi)存中維持一個(gè)變量夺荒。依然如前例,由于閉包良蒸,函數(shù)a中i的一直存在于內(nèi)存中技扼,因此每次執(zhí)行c(),都會(huì)給i自加1嫩痰。

  3. 通過(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'))
}
  1. 被遺忘的定時(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)存泄露


圖片.png

黃色是指直接被 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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屡贺,隨后出現(xiàn)的幾起案子蠢棱,更是在濱河造成了極大的恐慌,老刑警劉巖甩栈,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泻仙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡量没,警方通過(guò)查閱死者的電腦和手機(jī)玉转,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)殴蹄,“玉大人究抓,你說(shuō)我怎么就攤上這事∠疲” “怎么了刺下?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)稽荧。 經(jīng)常有香客問(wèn)我橘茉,道長(zhǎng),這世上最難降的妖魔是什么姨丈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任畅卓,我火速辦了婚禮,結(jié)果婚禮上蟋恬,老公的妹妹穿的比我還像新娘翁潘。我一直安慰自己,他們只是感情好歼争,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布拜马。 她就那樣靜靜地躺著,像睡著了一般沐绒。 火紅的嫁衣襯著肌膚如雪俩莽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天洒沦,我揣著相機(jī)與錄音豹绪,去河邊找鬼价淌。 笑死申眼,一個(gè)胖子當(dāng)著我的面吹牛瞒津,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播括尸,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼巷蚪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了濒翻?” 一聲冷哼從身側(cè)響起屁柏,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎有送,沒(méi)想到半個(gè)月后淌喻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雀摘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年裸删,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阵赠。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡涯塔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出清蚀,到底是詐尸還是另有隱情匕荸,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布枷邪,位于F島的核電站榛搔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏齿风。R本人自食惡果不足惜药薯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望救斑。 院中可真熱鬧童本,春花似錦、人聲如沸脸候。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)运沦。三九已至泵额,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間携添,已是汗流浹背嫁盲。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烈掠,地道東北人羞秤。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓缸托,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親瘾蛋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子俐镐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345