記錄下 js 函數的執行時機,困擾了我很久。面試
咱們一個一個例子的來看promise
let a = 1
function fn(){
console.log(a)
}
// 不會打印任何東西,由於函數沒有執行
複製代碼
let a = 1
function fn(){
console.log(a)
}
fn() // 1
//很簡單,一開始聲明瞭 a , a 的值爲1,而後調用函數 fn , 打印 a。
複製代碼
let a = 1
function fn(){
console.log(a)
}
a = 2
fn() // 2
// 一開始聲明瞭 a , a 的值爲1,而後在函數以前將 2 賦值給 a 。 因此打印出 a 的值爲2。
複製代碼
let a = 1
function fn(){
console.log(a)
}
fn() // 1
a = 2
// 此次是在函數執行後改變 a 的值,函數執行的時候,a 的值依舊爲1.
複製代碼
經過上面幾個例子能夠看出,函數中某個變量值的判斷,須要肯定函數執行的時機。一開始聲明的時候多是某個值,但在函數執行的時候這個值可能已經改變了。bash
再來看幾個 異步的例子。閉包
let a = 1
function fn(){
setTimeout(()=>{
console.log(a)
},0)
}
fn() // 2
a = 2
複製代碼
這個結果是爲2。由於setTimeout
函數會在隊列任務執行完後再執行。因此等到打印 a 的時候值已經變爲2了。異步
再來看個經典面試題:函數
for(var i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
// 會打印出 六個六。
複製代碼
這至關於post
let i
for( i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
複製代碼
由於 i 是全局的。for 循環執行完畢以後 i 的值爲6。有人奇怪 i 不該該是5嗎。i = 5 知足條件(<6)是最後一輪循環,而後i++,i 爲6。這個循環自始至終都是在改變一個 i 的值。因此會打印6個6。ui
那我就是想要符合預期的打印0、一、二、三、四、5呢?能夠這樣:spa
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
// 0 1 2 3 4 5
複製代碼
這裏 let 會單首創建一個做用域 至關於有6個 i 。code
(let i = 0) {
setTimeout(()=>{
console.log(i)
},0)
}
(let i = 1) {
setTimeout(()=>{
console.log(i)
},0)
}
(let i = 2) {
setTimeout(()=>{
console.log(i)
},0)
};
(let i = 3) {
setTimeout(()=>{
console.log(i)
},0)
}
(let i = 4) {
setTimeout(()=>{
console.log(i)
},0)
}
(let i = 5) {
setTimeout(()=>{
console.log(i)
},0)
};
複製代碼
咱們還能夠這樣解決:
for (var i = 0; i < 6; i++) {
setTimeout((function(i){
return function() {
console.log(i);
}
}(i)),0)
}
// 0 1 2 3 4 5
複製代碼
最後來看個例子,若是這個知道答案了,那基本上就懂了函數的執行時機:
function f1(){
let a = 1
function f2(){
let a = 2
function f3(){
console.log(a)
}
a = 22
f3()
}
console.log(a)
a = 100
f2()
}
f1()
複製代碼
這裏有不少 a ,a 有不少值。咱們只須要肯定咱們打印a的時候,a 是哪一個 a 以及 a 是什麼值。答案是1和22
。
執行 f1 函數的時候一開始第一個 a 爲1,而後有個 f2 函數沒執行就先跳過。而後打印 a 。因此咱們能夠肯定打印的值爲 1 。而後 a = 100 沒卵用。
再而後執行 f2 函數。f2 函數中聲明瞭 a = 2 。而後有個 f3 函數沒執行就跳過。而後 a = 22。因此以前聲明的那個 a 就是22。而後執行 f3 函數。打印 a 爲22。就是這麼簡單,這就是閉包,函數和函數內部可以訪問到的函數外的變量。
最後確定還有更加複雜的 setTimeout + promise + nextTick
的執行順序,涉及了宏任務跟微任務(其實很是簡單)
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
複製代碼
這裏是引用的另一個大佬的啦,有興趣能夠戳這裏哦