js经典题目学习总结
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
以上为个人理解,如有错误欢迎指正。