問(wèn)題
一拇颅、什么是閉包(closure)? 有什么作用
** 閉包概念**
- 簡(jiǎn)而言之,閉包就是有權(quán)訪(fǎng)問(wèn)另一個(gè)函數(shù)內(nèi)部參數(shù)和變量的函數(shù)
- 只有函數(shù)中的內(nèi)部函數(shù)才能一直訪(fǎng)問(wèn)函數(shù)作用域中的局部變量荧恍,即使在其外部函數(shù)被返回了之后镰烧,那么這個(gè)內(nèi)部函數(shù)就可以理解為閉包(創(chuàng)建了閉包)
- 本質(zhì)上拢军,閉包就是溝通函數(shù)內(nèi)部和函數(shù)外部的途徑
閉包作用
- 實(shí)現(xiàn)封裝,閉包可以訪(fǎng)問(wèn)和操作函數(shù)內(nèi)部作用域中的參數(shù)和變量怔鳖,使變量長(zhǎng)期駐留內(nèi)存(使用不當(dāng)會(huì)造成內(nèi)存泄漏)
注意:閉包會(huì)在父函數(shù)外部茉唉,改變父函數(shù)內(nèi)部變量的值。所以结执,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用度陆,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value)昌犹,這時(shí)一定要小心坚芜,不要隨便改變父函數(shù)內(nèi)部變量的值。
全局變量:
var globalVar;
function outerFn() {
console.log("Outer function");
function innerFn() {
console.log("Inner function");
}
globalVar = innerFn;//給了一個(gè)句柄讓他出來(lái)
}
outerFn();
globalVar();//必須先執(zhí)行outerFn(),innerFn在outerFn外執(zhí)行
返回值:
function outerFn() {
Var Outer function =1;//還有被引用的可能 斜姥,不會(huì)被釋放
function innerFn() {
console.log("Outer function");
}
return innerFn;
}
var fnRef = outerFn();//fnRef變成函數(shù)innerFn
fnRef();
fnRef = null;//釋放內(nèi)存
二鸿竖、setTimeout 0 有什么作用
一些概念
- JavaScript是單線(xiàn)程執(zhí)行的,無(wú)法同時(shí)執(zhí)行多段代碼铸敏。當(dāng)某一段代碼正在執(zhí)行的時(shí)候缚忧,所有后續(xù)的任務(wù)都必須等待,形成一個(gè)隊(duì)列(主線(xiàn)程)杈笔。一旦當(dāng)前任務(wù)執(zhí)行完畢闪水,再?gòu)年?duì)列中取出下一個(gè)任務(wù),這也常被稱(chēng)為 “阻塞式執(zhí)行”蒙具。
- setTimeout只能保證瀏覽器在指定的時(shí)間過(guò)后將任務(wù)(需要執(zhí)行的函數(shù))插入隊(duì)列等候球榆,并不保證這個(gè)任務(wù)在什么時(shí)候執(zhí)行朽肥。如果這個(gè)時(shí)間設(shè)為 0,就代表立即插入隊(duì)列持钉,但不是立即執(zhí)行衡招,仍然要等待前面代碼執(zhí)行完畢。所以 setTimeout 并不能保證執(zhí)行的時(shí)間每强,是否及時(shí)執(zhí)行取決于 JavaScript 線(xiàn)程是擁擠還是空閑始腾。執(zhí)行javascript的線(xiàn)程會(huì)在空閑的時(shí)候,自行從隊(duì)列中取出任務(wù)然后執(zhí)行它空执。javascript通過(guò)這種隊(duì)列機(jī)制浪箭,給我們制造一個(gè)異步執(zhí)行的假象。
- setTimeout的第二參數(shù)如果是0辨绊,就意味著瀏覽器要立刻執(zhí)行該函數(shù)奶栖。
這個(gè)立刻的結(jié)果是:瀏覽器會(huì)在文檔內(nèi)容處于穩(wěn)定狀態(tài)后立刻執(zhí)行,這樣就達(dá)到了和<script defer='defer'>或者<body onload="fun()">一樣的效果门坷。更應(yīng)該看到的是驼抹,此函數(shù)更優(yōu)于上述方法,前面兩個(gè)函數(shù)只能靜態(tài)時(shí)刻處理(文檔第一次載入的時(shí)候)拜鹤,而在系統(tǒng)開(kāi)發(fā)中,常常由于查詢(xún)流椒、更新等操作后敏簿,文檔的內(nèi)容會(huì)刷新,這樣就可以利用setTimeout的特點(diǎn)宣虾,使代碼能在頁(yè)面內(nèi)容穩(wěn)定后再執(zhí)行惯裕。 - 因?yàn)榇嬖谧钚r(shí)間粒度,0毫秒實(shí)際是達(dá)不到的绣硝,根據(jù)實(shí)際測(cè)試這個(gè)值是4毫秒左右(和瀏覽器及操作系統(tǒng)有關(guān))蜻势。HTML5定義的最小時(shí)間間隔是4毫秒。這是為了防止多個(gè) setTimeout(f, 0)語(yǔ)句連續(xù)執(zhí)行造成性能問(wèn)題鹉胖。
作用
應(yīng)用場(chǎng)景:
假如當(dāng)前 JavaScript線(xiàn)程正在執(zhí)行一段很耗時(shí)的代碼握玛,此時(shí)發(fā)生了一次鼠標(biāo)點(diǎn)擊,那么事件處理程序就被阻塞甫菠,用戶(hù)也無(wú)法立即看到反饋挠铲,事件處理程序會(huì)被放入任務(wù)隊(duì)列,直到前面的代碼結(jié)束以后才會(huì)開(kāi)始執(zhí)行寂诱。
例如:
<inputtype="text"onkeydown="show(this.value)">
<div></div>
<scripttype="text/javascript">
functionshow(val){
document.getElementsByTagName('div')[0].innerHTML=val;
}
</script>
這里綁定了keydown事件拂苹,意圖是當(dāng)用戶(hù)在文本框里輸入字符時(shí),將輸入的內(nèi)容實(shí)時(shí)地在 <div> 中顯示出來(lái)痰洒。但是實(shí)際效果并非如此可以發(fā)現(xiàn)瓢棒,每按下一個(gè)字符時(shí)浴韭,<div> 中只能顯示出之前的內(nèi)容,無(wú)法得到當(dāng)前的字符脯宿。這時(shí)就可以用setTimeout(0)
:當(dāng)用戶(hù)按下按鍵的時(shí)候念颈,JavaScript 引擎需要執(zhí)行keydown的事件處理程序,然后更新文本框的value值嗅绰,這兩件事也需要按順序來(lái)舍肠,事件處理程序執(zhí)行時(shí),更新value值的任務(wù)則進(jìn)入隊(duì)列等待窘面。所以我們?cè)趉eydown的事件處理程序里是無(wú)法得到更新后的value的翠语,利用setTimeout,我們把取value的操作放入隊(duì)列财边,放在更新value值以后肌括,這樣便達(dá)到了目的。
參考1
參考2
代碼題
一酣难、下面的代碼輸出多少谍夭?修改代碼讓fnArri輸出 i。使用兩種以上的方法
輸出:
方法一:
var fnArr = [];
for (var i = 0; i < 10; i ++) {
(function(n){
fnArr[n] = function(){
return n;
};
})(i);//立即執(zhí)行函數(shù),必須傳入?yún)?shù)不然執(zhí)行完畢后(十次)憨募,依然在外層找i
}
console.log( fnArr[3]() );
方法二:
var fnArr = [];
for (var i = 0; i < 10; i ++) {
(function(){
var s=i;
fnArr[i] = function(){
return s;
};
})();//每次執(zhí)行的時(shí)候紧索,將外層i的值傳入內(nèi)層
}
console.log( fnArr[3]() );
方法三:
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = ( function(){
var n=i;
return function(){
return n;
}
})()//形成十個(gè)閉包,每次執(zhí)行時(shí)i的值傳入內(nèi)層
}
console.log( fnArr[3]() );
二菜谣、使用閉包封裝一個(gè)汽車(chē)對(duì)象珠漂,可以通過(guò)如下方式獲取汽車(chē)狀態(tài)
var Car = (function(){
var speed = 0;
function setSpeed (num) {
speed = num;
}
function getSpeed () {
console.log(speed);
}
function accelerate () {
speed = speed + 10;
}
function decelerate () {
speed = speed - 10;
}
function getStatus () {
if (speed > 0) {
return "running"
} else {
return "stop"
}
}
return{
'setSpeed':setSpeed,
'getSpeed':getSpeed,
'accelerate':accelerate,
'decelerate':decelerate,
'getStatus':getStatus,
'speed':'error'
}
})();
三、寫(xiě)一個(gè)函數(shù)使用setTimeout模擬setInterval的功能
var i=0;
function setint(){
setTimeout(function(){
console.log(i++);
setint();
},1000);
}
setint();//毫秒數(shù)不能為參數(shù)
四尾膊、寫(xiě)一個(gè)函數(shù)媳危,計(jì)算setTimeout平均[備注:新加]最小時(shí)間粒度
function getMini(){
var i =0;
var start = Date.now();
var clock = setTimeout(function(){
i++;
if(i ===1000){
clearTimeout(clock);
var end = Date.now();
console.log((end-start)/i);
}
clock =setTimeout(arguments.callee, 0)
},0)
}
五、下面這段代碼輸出結(jié)果是? 為什么?
輸出:
代碼等價(jià)于:
var a;
a = 1;
console.log(a);
a = 3;
console.log(a);
setTimeout(function(){
a = 2;
console.log(a);
}, 0);
setTimeout是異步函數(shù) 冈敛,將任務(wù)代碼放到任務(wù)列隊(duì)中待笑,當(dāng)主線(xiàn)程的任務(wù)執(zhí)行完畢后再去執(zhí)行任務(wù)列隊(duì)的。所以先執(zhí)行主線(xiàn)程任務(wù)抓谴,打印兩次a的值暮蹂,程序返回undefined,再?gòu)娜蝿?wù)列隊(duì)中獲取任務(wù)癌压,執(zhí)行setTimeout.
六椎侠、下面這段代碼輸出結(jié)果是? 為什么?
輸出:
沒(méi)有任何輸出
等價(jià)于
陷入死循環(huán)
七、下面這段代碼輸出措拇?如何輸出delayer: 0, delayer:1...(使用閉包來(lái)實(shí)現(xiàn))
輸出:
保存運(yùn)行中的臨時(shí)變量
修改:
for(var i=0;i<5;i++){
(function(){
var n =i;
setTimeout(function(){
console.log('delayer:' + n);
}, 0);
})()
console.log(i);
}