1因苹、何為閉包奸攻?有什么用王悍?
閉包專業(yè)的解釋:
閉包是有權(quán)訪問另一個函數(shù)作用域的變量的函數(shù)泰佳。 簡單的說萎胰,Javascript允許使用內(nèi)部函數(shù)—即函數(shù)定義和函數(shù)表達式位于另一個函數(shù)的函數(shù)體內(nèi)纲熏。而且妆丘,這些內(nèi)部函數(shù)可以訪問它們所在的外部函數(shù)中聲明的所有局部變量、參數(shù)和聲明的其他內(nèi)部函數(shù)局劲。當(dāng)其中一個這樣的內(nèi)部函數(shù)在包含它們的外部函數(shù)之外被調(diào)用時勺拣,就會形成閉包。
但我相信初學(xué)者看到上面的解釋絕對是一臉懵逼鱼填,閉包應(yīng)該是大家在JavaScript的學(xué)習(xí)中所遇到的第一個理解起來相對復(fù)雜的概念药有。我們先用一個經(jīng)典的BUG來引出為什么要使用閉包:
function power() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(function () {
return i * i;
});
}
return arr;
}
var results = power();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
按照我們正常的邏輯,所希望得到的結(jié)果應(yīng)為:
f1(); //1
f2(); //4
f3(); //9
但實際上卻是:
f1(); //16
f2(); //16
f3(); //16
為什么?
原因在于將result[0]
,result[1]
,result[2]
分別綁定給f1
,f2
,f3
時愤惰,綁定的僅僅是power()
函數(shù)苇经,并未立即計算。當(dāng)獲取結(jié)果時(即f1()
)宦言,開始執(zhí)行函數(shù)扇单,此時的i
已經(jīng)變成了4,故所有的結(jié)果均為16蜡励。
怎么辦令花?
從上述分析可知,根源在于函數(shù)綁定時沒有即時保存結(jié)果凉倚,所以我們添加立即執(zhí)行函數(shù)and閉包,讓每一次變量都單獨賦值嫂沉,且最終結(jié)果不會發(fā)生改變:
function power() {
var arr = [];
for (var i = 1; i <= 3; i++) {
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
}
var results = power();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
這時稽寒,所得到的結(jié)果就是我們先前所期望的值。我們著重分析一下閉包的結(jié)構(gòu)與特性:
- 函數(shù)中套用函數(shù)趟章;
- 父函數(shù)的返回值為子函數(shù)杏糙;
- 子函數(shù)能調(diào)用父函數(shù)以及更加外層的變量;
- 外部亦能隨意給子函數(shù)傳遞參數(shù)以實現(xiàn)功能蚓土;
- 當(dāng)父函數(shù)返回子函數(shù)時宏侍,子函數(shù)所接收的變量與參數(shù)均被保存在其中。
有什么用蜀漆?
由特性可以看出來谅河,閉包可以調(diào)用并保存外部參數(shù)、變量确丢,并且能夠保證返回函數(shù)中變量的獨立性绷耍。這就可以給我們封裝功能,實現(xiàn)模塊化提供了巨大的便利鲜侥。由于JS沒有私有函數(shù)這一個概念褂始,我們可以用這種辦法來模擬出私有函數(shù),具體實現(xiàn)過程可以看第4題描函。
2崎苗、setTimeout 0 有什么作用
-
setTimeout(function(){},ms)
是延遲函數(shù),其內(nèi)部有兩個參數(shù)舀寓,第一個參數(shù)為所需要實現(xiàn)的功能函數(shù)胆数,第二個參數(shù)為延遲的毫秒數(shù)。 -
setInterval(function(){},ms)
是間隔函數(shù)基公,參數(shù)設(shè)置與setTimeout
一樣幅慌,區(qū)別在于setTimeout
只會在延遲XX毫秒后執(zhí)行一次函數(shù),而setInterval
會在延遲XX毫秒后執(zhí)行函數(shù)后繼續(xù)在延遲XX毫秒執(zhí)行函數(shù)轰豆,無限循環(huán)胰伍。
3齿诞、下面的代碼輸出多少?修改代碼讓fnArr[i]()
輸出 i
骂租。使用兩種以上的方法
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i;
};
}
console.log( fnArr[3]() ); // 10
代碼1:
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = (function (n){
return function () {
return n;
}
}(i));
}
console.log( fnArr[3]() );
代碼2:
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = (function (){
var n=i;
return function () {
return n;
}
}());
}
console.log( fnArr[3]() );
4祷杈、使用閉包封裝一個汽車對象,可以通過如下方式獲取汽車狀態(tài)
var Car = //todo;
Car.setSpeed(30);
Car.getSpeed(); //30
Car.accelerate();
Car.getSpeed(); //40;
Car.decelerate();
Car.decelerate();
Car.getSpeed(); //20
Car.getStatus(); // 'running';
Car.decelerate();
Car.decelerate();
Car.getStatus(); //'stop';
//Car.speed; //error
答案:
var Car = (function () {
var speeed = 0;
function setSpeed(n) {
speed = n;
return speed;
}
function accelerate() {
speed += 10;
return speed;
}
function decelerate() {
speed -= 20;
return speed;
}
function getSpeed() {
return speed;
}
function getStatus() {
if (speed > 0){
return "running";
}
if (speed == 0){
return "stop";
}
if (speed < 0){
return "error!";
}
}
return {
accelerate: accelerate,
decelerate: decelerate,
getSpeed: getSpeed,
getStatus: getStatus,
setSpeed: setSpeed
};
}());
5渗饮、寫一個函數(shù)使用setTimeout
模擬setInterval
的功能
var i = 0;
function intv(){
clock = setTimeout(function(){
console.log(i++);
intv();
},1000);
}
function stop(){
clearTimeout(clock);
}
intv();
6但汞、寫一個函數(shù),計算setTimeout最小時間粒度
function getMini(){
var start = new Date().getTime();
var i = 0;
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);
}
getMini();
7互站、下面這段代碼輸出結(jié)果是? 為什么?
var a = 1;
setTimeout(function(){
a = 2;
console.log(a);
}, 0);
var a ;
console.log(a);
a = 3;
console.log(a);
依次輸出 1,3,2.setTimeout函數(shù)的優(yōu)先級最低私蕾,會放在程序最后執(zhí)行,故實際上代碼應(yīng)該是:
var a;
a = 1 ;
console.log(a);
a = 3;
console.log(a);
setTimeout(function(){
a = 2;
console.log(a);
}, 0);
8胡桃、下面這段代碼輸出結(jié)果是? 為什么?
var flag = true;
setTimeout(function(){
flag = false;
},0)
while(flag){}
console.log(flag);
什么都輸出不了進入死循環(huán)踩叭,代碼其實是這樣的:
var flag = true;
while(flag){}
console.log(flag);
setTimeout(function(){
flag = false;
},0)
9、下面這段代碼輸出翠胰?如何輸出delayer: 0, delayer:1...(使用閉包來實現(xiàn))
for(var i=0;i<5;i++){
setTimeout(function(){
console.log('delayer:' + i );
}, 0);
console.log(i);
}
setTimeout優(yōu)先級最低容贝,故先出0,1,2,3,4五個console.log(i)
的值,循環(huán)完畢i=5
之景,故輸出5個delayer: 5;
閉包實現(xiàn)需求:
for(var i=0;i<5;i++){
setTimeout((function(i){
return function(){
console.log('delayer:' + i );
}
})(i), 0);
console.log(i);
}