js經典題目學習總結
1.foo與getName
function foo() {
getName = function () {
console.log(1);
}
return this;
}
foo.getName = function () {
console.log(2);
}
foo.prototype.getName = function () {
console.log(3);
}
var getName = function () {
console.log(4);
}
function getName() {
console.log(5);
}
foo.getName();//2
getName();//4
foo().getName();//1
getName();//1
new foo.getName();//2
new foo().getName();//3
new new foo().getName();//3
foo.getName();
直接找到foo.getName然後執行函數輸出2getName()
由於預解析時函數聲明提前於變量聲明,所以順序是這樣- 聲明一個有名函數getName
- 聲明一個變量getName將有名函數getName覆蓋,並指向了一個輸出4的匿名函數
- 運行到getName(),輸出4
foo().getName()
執行順序:- 執行foo,由於getName沒有用var/let定義,去全局變量找,所以將上一個輸出4的getName覆蓋,全局getName的輸出爲1
- foo執行完返回this,由於沒有其他對象調用foo,返回的this就是window
- 等價於執行window.getName(),輸出1
getName()
由上一步可知此時全局getName輸出1new foo.getName()
運算符優先級new foo() > foo() > foo
,所以等價於
vat a=foo.getName;
new a();//new的時候會將a執行一次,輸出2
new foo().getName()
根據優先級,執行步驟等價於
var a=new foo();//返回foo對象,注意new執行過程中this爲a
a.getName();//去原型鏈找到getName,輸出3
new new foo().getName()
等價代碼如下:
var a=new foo();//foo對象
var b=a.getName;//foo對象原型鏈上的getName
new b();//以b作爲構造函數生成新對象,此時會執行一次b輸出3
2.輸出順序
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0);
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
})
console.log('script end');
/*
結果:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/
題目涉及到異步編程,事件循環,宏任務,微任務等知識點,可以先去另一篇博客瞭解:事件循環&nextTick原理&異步渲染
以上代碼執行順序如下:
- 執行所有的同步代碼,特別注意Promise的then以前的代碼async函數第一個條await語句裏面的代碼立即執行,是屬於同步代碼的
script start
async1 start
async2
promise1
script end
- 根據代碼順序執行微任務代碼(Promise的then函數,await返回的也是Promise對象)
async1 end
promise2
- 執行宏任務回調(SetTimeout,setInmidiate等)
setTimeout
以上爲個人理解,如有錯誤歡迎指正。