前言
面試問(wèn)題:
- 說(shuō)一下對(duì)變量提升的理解
- 說(shuō)明this的幾種不同的使用場(chǎng)景
- 創(chuàng)建10個(gè)
<a>
標(biāo)簽,點(diǎn)擊的時(shí)候彈出來(lái)對(duì)應(yīng)的序號(hào) - 如何理解作用域
- 實(shí)際開(kāi)發(fā)中閉包的應(yīng)用
涉及到的知識(shí)點(diǎn):
- 執(zhí)行上下文
- this
- 作用域
- 作用域鏈
- 閉包
執(zhí)行上下文
執(zhí)行上下文主要有兩種情況:
- 全局代碼: 一段
<script>
標(biāo)簽里获高,有一個(gè)全局的執(zhí)行上下文兼都。所做的事情是:變量定義、函數(shù)聲明 - 函數(shù)代碼:每個(gè)函數(shù)里有一個(gè)上下文。所做的事情是:變量定義、函數(shù)聲明、this码党、arguments
PS:注意“函數(shù)聲明”和“函數(shù)表達(dá)式”的區(qū)別。
全局執(zhí)行上下文
在執(zhí)行全局代碼前將window確定為全局執(zhí)行上下文斥黑。
(1)對(duì)全局?jǐn)?shù)據(jù)進(jìn)行預(yù)處理:(并沒(méi)有賦值)
- var定義的全局變量==>undefined, 添加為window的屬性
- function聲明的全局函數(shù)==>賦值(fun), 添加為window的方法
- this==>賦值(window)
(2)開(kāi)始執(zhí)行全局代碼
函數(shù)執(zhí)行上下文
在調(diào)用函數(shù), 準(zhǔn)備執(zhí)行函數(shù)體之前, 創(chuàng)建對(duì)應(yīng)的函數(shù)執(zhí)行上下文對(duì)象(虛擬的, 存在于棧中)揖盘。
(1)對(duì)局部數(shù)據(jù)進(jìn)行預(yù)處理:
- 形參變量==>賦值(實(shí)參)==>添加為執(zhí)行上下文的屬性
- arguments==>賦值(實(shí)參列表), 添加為執(zhí)行上下文的屬性
- var定義的局部變量==>undefined, 添加為執(zhí)行上下文的屬性
- function聲明的函數(shù) ==>賦值(fun), 添加為執(zhí)行上下文的方法
- this==>賦值(調(diào)用函數(shù)的對(duì)象)
(2)開(kāi)始執(zhí)行函數(shù)體代碼
執(zhí)行上下文棧
- 1.在全局代碼執(zhí)行前, JS引擎就會(huì)創(chuàng)建一個(gè)棧來(lái)存儲(chǔ)管理所有的執(zhí)行上下文對(duì)象
- 2.在全局執(zhí)行上下文(window)確定后, 將其添加到棧中(壓棧)
- 3.在函數(shù)執(zhí)行上下文創(chuàng)建后, 將其添加到棧中(壓棧)
- 4.在當(dāng)前函數(shù)執(zhí)行完后,將棧頂?shù)膶?duì)象移除(出棧)
- 5.當(dāng)所有的代碼執(zhí)行完后, 棧中只剩下window
this
this指的是,調(diào)用函數(shù)的那個(gè)對(duì)象锌奴。this永遠(yuǎn)指向函數(shù)運(yùn)行時(shí)所在的對(duì)象兽狭。
解析器在調(diào)用函數(shù)每次都會(huì)向函數(shù)內(nèi)部傳遞進(jìn)一個(gè)隱含的參數(shù),這個(gè)隱含的參數(shù)就是this鹿蜀。
根據(jù)函數(shù)的調(diào)用方式的不同箕慧,this會(huì)指向不同的對(duì)象:【重要】
- 1.以函數(shù)的形式調(diào)用時(shí),this永遠(yuǎn)都是window茴恰。比如
fun();
相當(dāng)于window.fun();
- 2.以方法的形式調(diào)用時(shí)颠焦,this是調(diào)用方法的那個(gè)對(duì)象
- 3.以構(gòu)造函數(shù)的形式調(diào)用時(shí),this是新創(chuàng)建的那個(gè)對(duì)象
- 4.使用call和apply調(diào)用時(shí)往枣,this是指定的那個(gè)對(duì)象
需要特別提醒的是:this的指向在函數(shù)定義時(shí)無(wú)法確認(rèn)伐庭,只有函數(shù)執(zhí)行時(shí)才能確定。
this的幾種場(chǎng)景:
- 1分冈、作為構(gòu)造函數(shù)執(zhí)行
例如:
function Foo(name) {
//this = {};
this.name = name;
//return this;
}
var foo = new Foo();
- 2圾另、作為對(duì)象的屬性執(zhí)行
var obj = {
name: 'A',
printName: function () {
console.log(this.name);
}
}
obj.printName();
- 3、作為普通函數(shù)執(zhí)行
function fn() {
console.log(this); //this === window
}
fn();
- 4丈秩、call apply bind
作用域
作用域指一個(gè)變量的作用范圍盯捌。它是靜態(tài)的(相對(duì)于上下文對(duì)象), 在編寫(xiě)代碼時(shí)就確定了淳衙。
作用:隔離變量蘑秽,不同作用域下同名變量不會(huì)有沖突。
作用域的分類(lèi):
- 全局作用域
- 函數(shù)作用域
- 沒(méi)有塊作用域(ES6有了)
if (true) {
var name = 'smyhvae';
}
console.log(name);
上方代碼中箫攀,并不會(huì)報(bào)錯(cuò)肠牲,因?yàn)椋弘m然 name 是在塊里面定義的,但是 name 是全局變量靴跛。
全局作用域
直接編寫(xiě)在script標(biāo)簽中的JS代碼缀雳,都在全局作用域。
在全局作用域中:
在全局作用域中有一個(gè)全局對(duì)象window梢睛,它代表的是一個(gè)瀏覽器的窗口肥印,它由瀏覽器創(chuàng)建我們可以直接使用识椰。
創(chuàng)建的變量都會(huì)作為window對(duì)象的屬性保存。
創(chuàng)建的函數(shù)都會(huì)作為window對(duì)象的方法保存深碱。
全局作用域中的變量都是全局變量腹鹉,在頁(yè)面的任意的部分都可以訪(fǎng)問(wèn)到。
變量的聲明提前:(變量提升)
使用var關(guān)鍵字聲明的變量( 比如 var a = 1
)敷硅,會(huì)在所有的代碼執(zhí)行之前被聲明(但是不會(huì)賦值)功咒,但是如果聲明變量時(shí)不是用var關(guān)鍵字(比如直接寫(xiě)a = 1
),則變量不會(huì)被聲明提前绞蹦。
舉例1:
console.log(a);
var a = 123;
打印結(jié)果:undefined
舉例2:
console.log(a);
a = 123; //此時(shí)a相當(dāng)于window.a
程序會(huì)報(bào)錯(cuò):
函數(shù)的聲明提前:
- 使用
函數(shù)聲明
的形式創(chuàng)建的函數(shù)function foo(){}
力奋,會(huì)被聲明提前。
也就是說(shuō)幽七,它會(huì)在所有的代碼執(zhí)行之前就被創(chuàng)建景殷,所以我們可以在函數(shù)聲明之前,調(diào)用函數(shù)澡屡。
- 使用
函數(shù)表達(dá)式
創(chuàng)建的函數(shù)var foo = function(){}
滨彻,不會(huì)被聲明提前,所以不能在聲明前調(diào)用挪蹭。
很好理解亭饵,因?yàn)榇藭r(shí)foo被聲明了,且為undefined梁厉,并沒(méi)有給其賦值function(){}
辜羊。
所以說(shuō),下面的例子词顾,會(huì)報(bào)錯(cuò):
函數(shù)作用域
調(diào)用函數(shù)時(shí)創(chuàng)建函數(shù)作用域八秃,函數(shù)執(zhí)行完畢以后,函數(shù)作用域銷(xiāo)毀肉盹。
每調(diào)用一次函數(shù)就會(huì)創(chuàng)建一個(gè)新的函數(shù)作用域昔驱,他們之間是互相獨(dú)立的。
在函數(shù)作用域中可以訪(fǎng)問(wèn)到全局作用域的變量上忍,在全局作用域中無(wú)法訪(fǎng)問(wèn)到函數(shù)作用域的變量骤肛。
在函數(shù)中要訪(fǎng)問(wèn)全局變量可以使用window對(duì)象。(比如說(shuō)窍蓝,全局作用域和函數(shù)作用域都定義了變量a腋颠,如果想訪(fǎng)問(wèn)全局變量,可以使用window.a
)
提醒1:
在函數(shù)作用域也有聲明提前的特性:
- 使用var關(guān)鍵字聲明的變量吓笙,是在函數(shù)作用域內(nèi)有效淑玫,而且會(huì)在函數(shù)中所有的代碼執(zhí)行之前被聲明
- 函數(shù)聲明也會(huì)在函數(shù)中所有的代碼執(zhí)行之前執(zhí)行
因此,在函數(shù)中,沒(méi)有var聲明的變量都會(huì)成為全局變量絮蒿,而且并不會(huì)提前聲明尊搬。
舉例1:
var a = 1;
function foo() {
console.log(a);
a = 2; // 此處的a相當(dāng)于window.a
}
foo();
console.log(a); //打印結(jié)果是2
上方代碼中,foo()的打印結(jié)果是1
土涝。如果去掉第一行代碼毁嗦,打印結(jié)果是Uncaught ReferenceError: a is not defined
提醒2:定義形參就相當(dāng)于在函數(shù)作用域中聲明了變量。
function fun6(e) {
console.log(e);
}
fun6(); //打印結(jié)果為 undefined
fun6(123);//打印結(jié)果為123
作用域與執(zhí)行上下文的區(qū)別
區(qū)別1:
- 全局作用域之外回铛,每個(gè)函數(shù)都會(huì)創(chuàng)建自己的作用域狗准,作用域在函數(shù)定義時(shí)就已經(jīng)確定了。而不是在函數(shù)調(diào)用時(shí)
- 全局執(zhí)行上下文環(huán)境是在全局作用域確定之后, js代碼馬上執(zhí)行之前創(chuàng)建
- 函數(shù)執(zhí)行上下文是在調(diào)用函數(shù)時(shí), 函數(shù)體代碼執(zhí)行之前創(chuàng)建
區(qū)別2:
- 作用域是靜態(tài)的, 只要函數(shù)定義好了就一直存在, 且不會(huì)再變化
- 執(zhí)行上下文是動(dòng)態(tài)的, 調(diào)用函數(shù)時(shí)創(chuàng)建, 函數(shù)調(diào)用結(jié)束時(shí)就會(huì)自動(dòng)釋放
聯(lián)系:
- 執(zhí)行上下文(對(duì)象)是從屬于所在的作用域
- 全局上下文環(huán)境==>全局作用域
- 函數(shù)上下文環(huán)境==>對(duì)應(yīng)的函數(shù)使用域
作用域鏈
當(dāng)在函數(shù)作用域操作一個(gè)變量時(shí)茵肃,它會(huì)先在自身作用域中尋找腔长,如果有就直接使用(就近原則)。如果沒(méi)有則向上一級(jí)作用域中尋找验残,直到找到全局作用域捞附;如果全局作用域中依然沒(méi)有找到,則會(huì)報(bào)錯(cuò)ReferenceError您没。
外部函數(shù)定義的變量可以被內(nèi)部函數(shù)所使用鸟召,反之則不行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
//只要是函數(shù)就可以創(chuàng)造作用域
//函數(shù)中又可以再創(chuàng)建函數(shù)
//函數(shù)內(nèi)部的作用域可以訪(fǎng)問(wèn)函數(shù)外部的作用域
//如果有多個(gè)函數(shù)嵌套氨鹏,那么就會(huì)構(gòu)成一個(gè)鏈?zhǔn)皆L(fǎng)問(wèn)結(jié)構(gòu)欧募,這就是作用域鏈
//f1--->全局
function f1(){
//f2--->f1--->全局
function f2(){
//f3---->f2--->f1--->全局
function f3(){
}
//f4--->f2--->f1---->全局
function f4(){
}
}
//f5--->f1---->全局
function f5(){
}
}
</script>
</head>
<body>
</body>
</html>
理解:
- 多個(gè)上下級(jí)關(guān)系的作用域形成的鏈, 它的方向是從下向上的(從內(nèi)到外)
- 查找變量時(shí)就是沿著作用域鏈來(lái)查找的
查找一個(gè)變量的查找規(guī)則:
var a = 1
function fn1() {
var b = 2
function fn2() {
var c = 3
console.log(c)
console.log(b)
console.log(a)
console.log(d)
}
fn2()
}
fn1()
- 在當(dāng)前作用域下的執(zhí)行上下文中查找對(duì)應(yīng)的屬性, 如果有直接返回, 否則進(jìn)入2
- 在上一級(jí)作用域的執(zhí)行上下文中查找對(duì)應(yīng)的屬性, 如果有直接返回, 否則進(jìn)入3
- 再次執(zhí)行2的相同操作, 直到全局作用域, 如果還找不到就拋出找不到的異常
閉包
閉包就是能夠讀取其他函數(shù)內(nèi)部數(shù)據(jù)(變量/函數(shù))的函數(shù)。
只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量仆抵,因此可以把閉包簡(jiǎn)單理解成"定義在一個(gè)函數(shù)內(nèi)部的函數(shù)"跟继。
上面這兩句話(huà),是阮一峰的文章里的镣丑,你不一定能理解舔糖,來(lái)看下面的講解和舉例。
如何產(chǎn)生閉包
當(dāng)一個(gè)嵌套的內(nèi)部(子)函數(shù)引用了嵌套的外部(父)函數(shù)的變量或函數(shù)時(shí), 就產(chǎn)生了閉包莺匠。
閉包到底是什么?
使用chrome調(diào)試查看
- 理解一: 閉包是嵌套的內(nèi)部函數(shù)(絕大部分人)
- 理解二: 包含被引用變量 or 函數(shù)的對(duì)象(極少數(shù)人)
注意: 閉包存在于嵌套的內(nèi)部函數(shù)中金吗。
產(chǎn)生閉包的條件
- 1.函數(shù)嵌套
- 2.內(nèi)部函數(shù)引用了外部函數(shù)的數(shù)據(jù)(變量/函數(shù))。
來(lái)看看條件2:
function fn1() {
function fn2() {
}
return fn2;
}
fn1();
上面的代碼不會(huì)產(chǎn)生閉包趣竣,因?yàn)閮?nèi)部函數(shù)fn2并沒(méi)有引用外部函數(shù)fn1的變量摇庙。
PS:還有一個(gè)條件是外部函數(shù)被調(diào)用,內(nèi)部函數(shù)被聲明期贫。比如:
function fn1() {
var a = 2
var b = 'abc'
function fn2() { //fn2內(nèi)部函數(shù)被提前聲明跟匆,就會(huì)產(chǎn)生閉包(不用調(diào)用內(nèi)部函數(shù))
console.log(a)
}
}
fn1();
function fn3() {
var a = 3
var fun4 = function () { //fun4采用的是“函數(shù)表達(dá)式”創(chuàng)建的函數(shù)异袄,此時(shí)內(nèi)部函數(shù)的聲明并沒(méi)有提前
console.log(a)
}
}
fn3();
常見(jiàn)的閉包
- 將一個(gè)函數(shù)作為另一個(gè)函數(shù)的返回值
- 將函數(shù)作為實(shí)參傳遞給另一個(gè)函數(shù)調(diào)用通砍。
閉包1:將一個(gè)函數(shù)作為另一個(gè)函數(shù)的返回值
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1(); //執(zhí)行外部函數(shù)fn1,返回的是內(nèi)部函數(shù)fn2
f() // 3 //執(zhí)行fn2
f() // 4 //再次執(zhí)行fn2
當(dāng)f()第二次執(zhí)行的時(shí)候,a加1了封孙,也就說(shuō)明了:閉包里的數(shù)據(jù)沒(méi)有消失迹冤,而是保存在了內(nèi)存中。如果沒(méi)有閉包虎忌,代碼執(zhí)行完倒數(shù)第三行后泡徙,變量a就消失了。
上面的代碼中膜蠢,雖然調(diào)用了內(nèi)部函數(shù)兩次堪藐,但是,閉包對(duì)象只創(chuàng)建了一個(gè)挑围。
也就是說(shuō)礁竞,要看閉包對(duì)象創(chuàng)建了一個(gè),就看:外部函數(shù)執(zhí)行了幾次(與內(nèi)部函數(shù)執(zhí)行幾次無(wú)關(guān))杉辙。
閉包2. 將函數(shù)作為實(shí)參傳遞給另一個(gè)函數(shù)調(diào)用
function showDelay(msg, time) {
setTimeout(function() { //這個(gè)function是閉包模捂,因?yàn)槭乔短椎淖雍瘮?shù),而且引用了外部函數(shù)的變量msg
alert(msg)
}, time)
}
showDelay('atguigu', 2000)
上面的代碼中蜘矢,閉包是里面的funciton狂男,因?yàn)樗乔短椎淖雍瘮?shù),而且引用了外部函數(shù)的變量msg品腹。
閉包的作用
- 作用1. 使用函數(shù)內(nèi)部的變量在函數(shù)執(zhí)行完后, 仍然存活在內(nèi)存中(延長(zhǎng)了局部變量的生命周期)
- 作用2. 讓函數(shù)外部可以操作(讀寫(xiě))到函數(shù)內(nèi)部的數(shù)據(jù)(變量/函數(shù))
我們讓然拿這段代碼來(lái)分析:
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
}
return fn2;
}
var f = fn1(); //執(zhí)行外部函數(shù)fn1岖食,返回的是內(nèi)部函數(shù)fn2
f() // 3 //執(zhí)行fn2
f() // 4 //再次執(zhí)行fn2
作用1分析:
上方代碼中,外部函數(shù)fn1執(zhí)行完畢后舞吭,變量a并沒(méi)有立即消失县耽,而是保存在內(nèi)存當(dāng)中。
作用2分析:
函數(shù)fn1中的變量a镣典,是在fn1這個(gè)函數(shù)作用域內(nèi)兔毙,因此外部無(wú)法訪(fǎng)問(wèn)。但是通過(guò)閉包兄春,外部就可以操作到變量a澎剥。
達(dá)到的效果是:外界看不到變量a,但可以操作a赶舆。
比如上面達(dá)到的效果是:我看不到變量a哑姚,但是每次執(zhí)行函數(shù)后,讓a加1叙量。當(dāng)然,如果我真想看到a九串,我可以在fn2中將a返回即可绞佩。
回答幾個(gè)問(wèn)題:
- 問(wèn)題1. 函數(shù)執(zhí)行完后, 函數(shù)內(nèi)部聲明的局部變量是否還存在?
答案:一般是不存在, 存在于閉包中的變量才可能存在寺鸥。
閉包能夠一直存在的根本原因是f
,因?yàn)?code>f接收了fn1()
品山,這個(gè)是閉包胆建,閉包里有a。注意肘交,此時(shí)笆载,fn2并不存在了,但是里面的對(duì)象(即閉包)依然存在涯呻,因?yàn)橛?code>f接收了凉驻。
- 問(wèn)題2. 在函數(shù)外部能直接訪(fǎng)問(wèn)函數(shù)內(nèi)部的局部變量嗎?
不能,但我們可以通過(guò)閉包讓外部操作它复罐。
閉包的生命周期
- 產(chǎn)生: 嵌套內(nèi)部函數(shù)fn2被聲明時(shí)就產(chǎn)生了(不是在調(diào)用)
- 死亡: 嵌套的內(nèi)部函數(shù)成為垃圾對(duì)象時(shí)沿侈。(比如f = null,就可以讓f成為垃圾對(duì)象市栗。意思是缀拭,此時(shí)f不再引用閉包這個(gè)對(duì)象了)
閉包的應(yīng)用:定義具有特定功能的js模塊
- 將所有的數(shù)據(jù)和功能都封裝在一個(gè)函數(shù)內(nèi)部(私有的),只向外暴露一個(gè)包含n個(gè)方法的對(duì)象或函數(shù)填帽。
- 模塊的使用者, 只需要通過(guò)模塊暴露的對(duì)象調(diào)用方法來(lái)實(shí)現(xiàn)對(duì)應(yīng)的功能蛛淋。
方式一
(1)myModule.js:(定義一個(gè)模塊,向外暴露多個(gè)函數(shù)篡腌,供外界調(diào)用)
function myModule() {
//私有數(shù)據(jù)
var msg = 'Smyhvae Haha'
//操作私有數(shù)據(jù)的函數(shù)
function doSomething() {
console.log('doSomething() ' + msg.toUpperCase()); //字符串大寫(xiě)
}
function doOtherthing() {
console.log('doOtherthing() ' + msg.toLowerCase()) //字符串小寫(xiě)
}
//通過(guò)【對(duì)象字面量】的形式進(jìn)行包裹褐荷,向外暴露多個(gè)函數(shù)
return {
doSomething1: doSomething,
doOtherthing2: doOtherthing
}
}
上方代碼中,外界可以通過(guò)doSomething1和doOtherthing2來(lái)操作里面的數(shù)據(jù)嘹悼,但不讓外界看到叛甫。
(2)index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>05_閉包的應(yīng)用_自定義JS模塊</title>
</head>
<body>
<!--
閉包的應(yīng)用 : 定義JS模塊
* 具有特定功能的js文件
* 將所有的數(shù)據(jù)和功能都封裝在一個(gè)函數(shù)內(nèi)部(私有的)
* 【重要】只向外暴露一個(gè)包含n個(gè)方法的對(duì)象或函數(shù)
* 模塊的使用者, 只需要通過(guò)模塊暴露的對(duì)象調(diào)用方法來(lái)實(shí)現(xiàn)對(duì)應(yīng)的功能
-->
<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
var module = myModule();
module.doSomething1();
module.doOtherthing2();
</script>
</body>
</html>
方式二
同樣是實(shí)現(xiàn)方式一種的功能,這里我們采取另外一種方式杨伙。
(1)myModule2.js:(是一個(gè)立即執(zhí)行的匿名函數(shù))
(function () {
//私有數(shù)據(jù)
var msg = 'Smyhvae Haha'
//操作私有數(shù)據(jù)的函數(shù)
function doSomething() {
console.log('doSomething() ' + msg.toUpperCase())
}
function doOtherthing() {
console.log('doOtherthing() ' + msg.toLowerCase())
}
//外部函數(shù)是即使運(yùn)行的匿名函數(shù)其监,我們可以把兩個(gè)方法直接傳給window對(duì)象
window.myModule = {
doSomething1: doSomething,
doOtherthing2: doOtherthing
}
})()
(2)index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>05_閉包的應(yīng)用_自定義JS模塊2</title>
</head>
<body>
<!--
閉包的應(yīng)用2 : 定義JS模塊
* 具有特定功能的js文件
* 將所有的數(shù)據(jù)和功能都封裝在一個(gè)函數(shù)內(nèi)部(私有的)
* 只向外暴露一個(gè)包信n個(gè)方法的對(duì)象或函數(shù)
* 模塊的使用者, 只需要通過(guò)模塊暴露的對(duì)象調(diào)用方法來(lái)實(shí)現(xiàn)對(duì)應(yīng)的功能
-->
<!--引入myModule文件-->
<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
myModule.doSomething1()
myModule.doOtherthing2()
</script>
</body>
</html>
上方兩個(gè)文件中,我們?cè)?code>myModule2.js里直接把兩個(gè)方法直接傳遞給window對(duì)象了限匣。于是抖苦,在index.html中引入這個(gè)js文件后,會(huì)立即執(zhí)行里面的匿名函數(shù)米死。在index.html中把myModule直接拿來(lái)用即可锌历。
總結(jié):
當(dāng)然,方式一和方式二對(duì)比后峦筒,我們更建議采用方式二究西,因?yàn)楹芊奖恪?/p>
但無(wú)論如何,兩種方式都采用了閉包物喷。
閉包的缺點(diǎn)及解決
缺點(diǎn):函數(shù)執(zhí)行完后, 函數(shù)內(nèi)的局部變量沒(méi)有釋放卤材,占用內(nèi)存時(shí)間會(huì)變長(zhǎng)遮斥,容易造成內(nèi)存泄露。
解決:能不用閉包就不用商膊,及時(shí)釋放伏伐。比如:
f = null; // 讓內(nèi)部函數(shù)成為垃圾對(duì)象 -->回收閉包
總而言之宠进,你需要它晕拆,就是優(yōu)點(diǎn);你不需要它材蹬,就成了缺點(diǎn)实幕。
內(nèi)存溢出和內(nèi)存泄露
內(nèi)存溢出
內(nèi)存溢出:一種程序運(yùn)行出現(xiàn)的錯(cuò)誤。當(dāng)程序運(yùn)行需要的內(nèi)存超過(guò)了剩余的內(nèi)存時(shí), 就出拋出內(nèi)存溢出的錯(cuò)誤堤器。
代碼舉例:
var obj = {};
for (var i = 0; i < 10000; i++) {
obj[i] = new Array(10000000); //把所有的數(shù)組內(nèi)容都放到obj里保存昆庇,導(dǎo)致obj占用了很大的內(nèi)存空間
console.log("-----");
}
內(nèi)存泄漏
內(nèi)存泄漏:占用的內(nèi)存沒(méi)有及時(shí)釋放。
注意闸溃,內(nèi)存泄露的次數(shù)積累多了整吆,就容易導(dǎo)致內(nèi)存溢出。
常見(jiàn)的內(nèi)存泄露:
- 1.意外的全局變量
- 2.沒(méi)有及時(shí)清理的計(jì)時(shí)器或回調(diào)函數(shù)
- 3.閉包
情況1舉例:
// 意外的全局變量
function fn() {
a = new Array(10000000);
console.log(a);
}
fn();
情況2舉例:
// 沒(méi)有及時(shí)清理的計(jì)時(shí)器或回調(diào)函數(shù)
var intervalId = setInterval(function () { //啟動(dòng)循環(huán)定時(shí)器后不清理
console.log('----')
}, 1000)
// clearInterval(intervalId); //清理定時(shí)器
情況3舉例:
<script type="text/javascript">
function fn1() {
var a = 4;
function fn2() {
console.log(++a)
}
return fn2
}
var f = fn1()
f()
// f = null //讓內(nèi)部函數(shù)成為垃圾對(duì)象-->回收閉包
</script>