接下来,请听第一题:
function Foo(){
getName = function(){alert(1)};
return this;
}
Foo.getName = function(){ alert(2) };
Foo.prototype.getName = function(){alert(3)};
var getName = function(){alert(4)};
function getName(){alert(5)};
//写出以下的输出结果。先给出答案
Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3
这题,光是看着就吓银,硬着头皮,干!!
先来试试写出他的AO,这是全局中的
AO:{
Foo:function(){
getName = function(){alert(1)};
return this;
}
getName:function(){ alert(4) }
}
执行阶段:
- 第一个:Foo.getName();
通过Foo创建了一个可以通过类直接调用的静态方法getName,那就执行它弹出2.
- 第二个:getName();
直接getName函数,弹出4。这里存在变量提升,函数提升的问题,我有三个锦囊妙计,可以看看之前的文章。
- 第三个:Foo().getName();
即先执行Foo(),函数Foo()返回当前的Foo对象,直接在调用该对象的getName方法,这里考察的是this的指向问题,Foo()直接执行,返回的是window对象,window对象上的getName()方法,会在Foo()执行完后,被其内部的getName = function(){alert(1)};覆盖掉。所以会alert(1).
- 第四个:getName();
再次调用getName方法,此时getName已经被覆盖掉,所以还是alert(1)
- 第五个:new Foo.getName();
此处考察的是运算符优先级的问题,看下表
可以看到 . 运算符的优先级大于new运算符,所以相当于new (Foo.getName)(),即将静态方法getName作为构造函数执行,最终alert(“2”);
- 第六个:new Foo().getName();
也是运算符优先级的问题,new大于(),先new,该构造函数的返回值为this也就是代表当前的对象,相当于实例化了一个foo对象,然后调用该对象的getName方法,没有,怎么办,上原型链查找。所以alert(“3”)。
- 第七个:new new Foo().getName();
还是运算符优先级的问题
相当于new (new Foo()).getName();也就是将实例化对象的原型上的方法getName当作构造函数,再new,所以alert(“3”);
第二题:
async function async1(){
console.log('async1');
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");
这道题是不是更惊悚、更刺激…
这道题考察的是对底层运行机制的理解,事件轮询,同步异步,宏任务和微任务。
简单说说async,async会返回一个promise对象,好了完了。
再说说await,await意思是等待,它等待它右侧表达式返回一个结果。而且很重要的一个点,await会让出线程,会阻塞其后续代码的结果。但是不影响其后续代码的计算。等到返回结果后,会阻塞后面的代码先执行async外面的同步代码。
好了,分析分析,真的复杂:
1.先执行主线程上的同步代码。
console.log(“script start”);//打印script start,
2.然后是async1()执行,先打印async1 start,
遇到await async2,跳出当前,找到async2,执行async2函数,打印async2。此时await让出主线程,后面的代码被阻塞也就是console.log(‘async1 end’);会被阻塞。
3.然后继续执行同步代码new Promise(这是一个宏任务,前面的同步代码也是宏任务),打印Promise1,然后回调中有一个微任务console.log(“promise2”);,加入微任务队列。继续执行,打印"script end"。
4.本轮宏任务全部执行结束,检查微任务列表,还有一个微任务未执行,打印promise2。微任务也执行结束,回到await,await之前让出了主线程,现在开始执行await后面被阻塞的部分,即打印 async1 end。
5.OK,本轮所有任务全部执行完,开始下一轮的宏任务,即定时器异步任务setTimeout,它从事件列表中被添加到ESC执行栈,打印setTimeout。
看到这,也许你的内心是崩溃的,甚至内心里已经是一万头羊驼狂奔而过。
没事,问题不大,再看看涉及的知识点吧。