前面的答案都是能做對的,但是后面的ES6和ES7真的一點都不會,該學習了!胖先森!
被打擊了,真心被打擊了!
最后面的語法還是不明所以,需要學習,學習,學習!
由一道簡單的JS面試題發起的一系列追問,看看你能答對幾個?
我們直接從簡單的代碼開始,看看它的運行結果是什么?
1. JS代碼
for (var i=0;i<5;i++){
window.setTimeout(function(){
console.log(new Date(),i)
},1000)
}
上面的題目很簡單,只要對作用域,閉包,異步執行[定時器]等知識有了解,應該會很快說出答案,在本地測試后,輸出的答案如下。
追問①
如果我們需要用箭頭->表示前后輸出有一秒的間隔,那么最終輸出的答案會是下面的結果1還是結果2呢?
for (var i=0;i<5;i++){
window.setTimeout(function(){
console.log(new Date(),i)
},1000)
}
console.log(new Date(),i,"->");
結果1. 5 -> 5 -> 5 -> 5 -> 5 -> 5
結果2. 5 -> 5, 5, 5, 5, 5
如果你對Javascript的定時器有所了解的話,會很快知道結果2
是正確的。
在循環時設置定時器,幾乎是同時生效的,一般情況下,這5個定時器會在同一秒內執行,結果會立即輸出,因此結果2是正確的。
追問②
如果我期望輸出以下的結果,該怎么做呢?
想要的結果: 5 -> 0, 1, 2, 3, 4
- 解決方案一:定義輸出函數
有的同學可能會很容易想到通過定義一個專門的輸出函數來執行。
var output = function(i){
window.setTimeout(function(){
console.log(new Date(),i)
},1000)
}
for (var i=0;i<5;i++){
output(i);//這里傳過去的i的值被復制了
}
console.log(new Date(),i)
- 解決方案二:立即執行函數+閉包
熟悉閉包的同學可能會覺得這種方案比較low,他們會想到利用立即執行函數來解決閉包產生的問題,于是會給出以下答案。
for (var i=0;i<5;i++){
(function(j){
window.setTimeout(function(){
console.log(new Date(),j)
},1000)
})(i);
}
console.log(new Date(),i,"->");
上面的答案利用了立即執行函數來解決閉包造成的問題,是一種比答案1略好的方法。
- 解決方案二:let關鍵字
如果熟悉ES6語法
的同學又會想到以上兩種方法對于代碼的改動都比較大,如果使用ES6的let命令會可能會更加容易,于是他們有了以下代碼。
針對web前端應該去掌握最新的ES6的語法
for (let i=0;i<5;i++){
window.setTimeout(function(){
console.log(new Date(),i)
},1000)
}
// console.log(new Date(),i,"->")
首先,我們要確定的是,上述代碼在運行中會報錯,因為let是塊級作用域,i只存在于循環體內部,最后一句i并未定義,在直接輸出時會報錯。
但是既然能夠想到用let命令去做,說明對ES6有一定的了解,很不錯。
追問③
如果對于上面的問題,覺得太簡單了,那進行下面一個問題。
需要輸出的形式如下,每隔一秒輸出一個數字,該如何實現?
預想得到的結果: 0 -> 1 -> 2 -> 3 -> 4 -> 5
可能會有很多同學想到使用如下簡單粗暴的方案去做。
for (var i=0;i<5;i++){
(function(j){
window.setTimeout(function(){
console.log(new Date(),j)
},1000*j);//修改0~4的時間
})(i);
}
// 增加定時器,超時設置為5秒
window.setTimeout(function(){
console.log(new Date(),i)
},1000*i)
對于以上的方案,雖然能實現這個簡單的需求,但是并不能算面試時的一個加分項。
如果考慮到異步執行后的操作,上面的代碼就會顯得很吃力,那該怎么辦呢?
- ES6的Promise
如果你對ES6的Promise有所了解,會很容易想到使用Promise去實現。
const tasks=[];
for (var i=0;i<5;i++){
((j)=>{
tasks.push(new Promise((resolve)=>{
window.setTimeout(()=>{
console.log(new Date(),j);
resolve();//這里需要執行resolve()
},1000*j);
}));
})(i);
}
Promise.all(tasks).then(()=>{
window.setTimeout(function(){
console.log(new Date(),i)
},1000);
});
沒有學習ES6,突然感覺打擊真大
追問④ 極限
既然掌握了ES6的Promise,我們可以更加追求代碼的極致,使用ES7的async和await。
這里給出使用async和await的解決方案。
//模擬其他語言中的sleep,實際上可以是任何的異步操作
const sleep = (time) => new Promise((resolve)=>{
window.setTimeout(resolve,time);
});
(async()=>{//聲明即執行的async函數表達式
for (var i=0;i<5;i++){
await sleep(1000);
console.log(new Date(),i);
}
await sleep(1000);
console.log(new Date(),i);
})();
最后說一下
本篇文章從一個簡單的Javascript面試題開始,進行了一系列的追問,想一想你能答對幾個追問呢?如果又不會的,記得回去查漏補缺,學習響應的知識。