JS基础-原型原型链和宏任务/微任务/事件机制

1、原型原型链

 题1:

熟悉使用new创建对象的原理:

(1)新对象的隐式原型执向构造函数的显示原型

B._proto_ = A.protoType;

(2)将构造函数指向新实例对象。

A.call(B)

题2:

输出--》2, undefined

解析:(1)a.x首先要在自己的构造函数中查找,没有采取原型上找,这里有this.x = x.所以a.x = 2;

题3:

console.log(typeof ''.prototype);
console.log(typeof ''.__proto__); 
console.log(typeof ''.__proto__ === typeof ''.prototype); 

登录—专业IT笔试面试备考平台_牛客网

undefined、object、false

(1)字符串’‘,是String构造函数实例化的,实例上有隐式原型_proto_, 没有显示原型protoType,所以是undefined。

(2)''.__proto__ = String.protoType = Object类型;

(3) typeof ''.prototype 即typeof  undefined 为undefined

题4:

function Person(age){
       this.age = age;
 }

Person.prototype = {
       constructor:Person,
       getAge:function(){
            console.log(this.age);
        },
 }

var ldh = new Person(24);
Person.prototype.age = 18;
Object.prototype.age = 20;
ldh.getAge();

对象在调用方法和属性时依靠原型链的顺序进行查找,先从自身查找,然后是构造函数的原型对象,接着是Object的原型对象,一旦找到时停止查找,找不到则返回undefined。同时,原型对象中的this仍然指向实例对象,而非原型对象,在本题中,实例对象先调用原型对象的getAge()方法,然后输出age属性值,由于该实例对象已经有age属性,同时其原型链中,原型对象和原型对象的原型对象即Object对象均有age属性,依据上述查找规则,最终输出结果为实例对象的age,即为24。

题5:

class Phone{
  constructor(brand){
    this.brand = brand;
}
  call(){}...①
}
function playGame(){console.log("我可以打游戏")};
function photo(){console.log("我可以拍照")};
console.log(typeof Phone);...②
var p = new Phone('华为');
console.log(p.brand);...③

1、call方法是定义在类Phone的prototype对象上

2、类的本质是函数,实际上,ES6中的类可以视为ES5中构造函数的另一种写法,所以②式的输出结果为function而不是Object。

3、p为类的实例对象,该对象有一个属性brand,属性值为华为

4、若想一次性给类添加playGame和photo两个实例方法,可以使用Object.assign(Phone.prototype,{playGame,photo})

题6:

function father() {
  this.num = 935;
  this.work = ['read', 'write', 'listen'];
}
function son() {}
son.prototype = new father();
let son1 = new son();
let son2 = new son();
son1.num = 117;
son1.work.pop();
console.log(son2.num);
console.log(son2.work);






// 解析

function father() {
    this.num = 935;
    this.work = ['read', 'write', 'listen'];
}
function son() {}
son.prototype = new father(); 
// son.prototype = {  num:935, work:['read','write','listen'] }

let son1 = new son(); // son1 = { }
let son2 = new son(); // son2 = { }

son1.num = 117; // son1 = { num: 117 }

son1.work.pop(); 
//son1自己没有work,去原型里找到work,并删除work里的最后一项,
//此时son.prototype = {  num:935, work:['read','write'] }

console.log(son2.num);// son2自己没有num,去原型里找,有num:935

console.log(son2.work);
//son1和son2原型是同一个,所以此时原型里的work是['read', 'write']

---》935、 ['read', 'write']


 2、setTimeout异步任务/宏任务/微任务/事件机制

Promise面试题汇总_CUG-GZ的博客-CSDN博客_promise面试题

题1:

 

->>> 0 1 2 2

(1)上面的循环和下面的循环,都是同步代码,先执行上面的,往事件队列中放了两个异步的宏任务,任务1和任务2(RW1,RW2:都是使用let定义的块级作用域,所以RW1中i为0,RW2中i为1)

此时事件队列【RW1(i=0),RW2(i=1)】

(2)下面的循环,继续往事件队列中添加宏任务RW3、RW4,不过使用的var,没有块级作用域,定时器执行时,需要使用外面作用域中i的值。

此时事件队列【RW1(i=0),RW2(i=1),RW3(i=?),RW4(i=?)】

(3)外层的i为2,因为当i=0,i=1时,设定了两个任务,i=2时不符合条件,但也执行了,同步代码执行完毕,i=2最后。

(4)开始执行事件队列,输出0,1,2,2。

补充:如果上面定时器事件都是2000ms,也即是2s,那么结果是分别输出,还是一起输出》?

实际验证:等待2s之后,直接全部输出0,1,2,2。

题2:

for(var i=0;i<3;++i){
    setTimeout(function(){
        console.log(i);
    },100);
}
3,3,3(100ms后一次输出3个3)

这道题涉及了异步、作用域、闭包

settimeout是异步执行,100ms后往任务队列里面添加一个任务,只有主线上的全部执行完,才会执行任务队列里的任务,当主线执行完成后,i是3,所以此时再去执行任务队列里的任务时,i全部是3了。对于打印3次是:

 每一次for循环的时候,settimeout都执行一次,但是里面的函数没有被执行,而是被放到了任务队列里面,等待执行,for循环了3次,就放了3次,当主线程执行完成后,才进入任务队列里面执行。

题3:

for(var i = 0; i < 5; i++){
    setTimeout(function(){
        console.log(i);
    }, 1000 * i);
}
5 5 5 5 5(立即输出一个5,在每隔1s输出一个5)

1000*i:这里的i实际不是异步任务要执行的函数,而是在塞进任务队列时,i值也即是定时执行的时间已经确认了。

待同步函数执行完之后,执行任务队列中的异步任务,异步任务的定时0s,1s,2s, 3s, 4s,所以可以看到的效果是,立即输出一个5,在每隔1s输出一个5

题4:

let date = new Date()
setTimeout(() => {
    console.log('1')
}, 2000)
setTimeout('console.log(2)',1000);
setTimeout(function() {
  console.log('3')
}, 1500);
while((new Date() - date) < 3000) {}

While所在是微任务,所以前3秒后在执行while函数,setTimeout函数虽然在各自对应时间后插入了队列,但是由于属于宏任务所以暂时还没有执行,直到while微任务完成,才按顺序输出。

--》3秒以后同时输出2 3 1

题5:

for(let i=0;i<2;i++){
    setTimeout(function(){
    console.log(i)
    },100);
}
for(var i=0;i<2;i++){
    setTimeout(function(){
        console.log(i)
    },100);
}

-登录—专业IT笔试面试备考平台_牛客网
 

-》0 1 2 2

题6:

console.log(1);
let a = setTimeout(() => {console.log(2)}, 0);
console.log(3);
Promise.resolve(4).then(b => {
    console.log(b);
    clearTimeout(a);
});
console.log(5);

promise对象的then()方法属于微任务,而setTimeout()定时器函数为宏任务。在执行顺序处理上,js会先执行所有同步代码,然后执行微任务队列中的所有微任务,最后再继续执行宏任务。在本题中,先执行同步代码并输出1 3 5,接着执行Promise.resolve().then()方法,输出4,由于在then()方法内删除了定时器函数,所以不会再输出2,最终输出结果为1 3 5 4

题7:

Promise.resolve().then(() => {
    console.log('promise1');
    const timer2 = setTimeout(() => {
        console.log('timer2')
    }, 0)
});
const timer1 = setTimeout(() => {
    console.log('timer1')
    Promise.resolve().then(() => {
        console.log('promise2')
    })
}, 0)
console.log('start');

解析:

代码从上至下进行执行:

(1)首先碰到的就是Promise,是个微任务,放入任务队列中【WR1】

(2)继续执行碰到了一个宏任务,放入任务队列中【WR1,HR1】

(3)同步代码,打印出start。

(4)同步代码执行完,开始执行任务队列中任务。

(5)先执行微任务WR1,输出“promise1“, 遇到宏任务2加入任务队列【HR1,HR2】

(6)WR1微任务执行完,执行宏任务HR1。

(7)执行HR1,先输出“timer1”, 碰到微任务WR2,任务队列【HR2, WR2】

(8)宏任务R1执行完,任务队列中还有一个宏任务和微任务

(9)先执行微任务WR2,输出promise2,在执行宏任务输出提timer2

----> start ->promise1->timer1->promise2->timer2

题8:

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log('ee--00:', x)), 1000))
  return p
}
function runReject (x) {
  const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log('ee--11:', x)), 1000 * x))
  return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
       .then(res => console.log('ee--22:', res))
       .catch(err => console.log('ee--33:', err))

输出结果:

可以看到Promise.all执行四个promise实例,其中有reject的,所以promise.all状态不会变为fullfiled,也即是不会调用.then的回调函数。

(1)runAsync(1),  runAsync(3), 是两个fullfied状态的实例,同时定时器为1s,所以1s之后同时输出1,3

(2)runReject(2)是reject状态,promise.all只要有一个实例状态变为reject,那么整体状态就是reject,会走.catch的回调,所以第2s会输出 2,Error: 2

(3)runReject(4)也是reject状态,但promise的实例状态不可逆,从reject->reject状态不变,不会触发调用回调函数,所有不会在执行.catch, 第4s输出4

// 1s后输出
ee--00: 1
ee--00: 3
// 2s后输出
ee--11:  2
ee--33:  Error: 2
// 4s后输出
ee--11:  4

题9: 

function runAsync(x) {
  const p = new Promise(r =>
    setTimeout(() => r(x, console.log(x)), 1000)
  );
  return p;
}
function runReject(x) {
  const p = new Promise((res, rej) =>
    setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)
  );
  return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log("result: ", res))
  .catch(err => console.log(err));

// 先输出

 0
Error: 0

// 1s后同时输出
1
2
3



结果:

2、

第一:首先区分浏览器端和服务器端的js模块化规范。

1)浏览器端的js模块化规范:AMD 和 CMD

2)服务器端的js模块化规范:CommonJS(注:由NodeJS实现)

第二:浏览器端的js模块化规范分别由什么实现。

1)AMD是由require.js实现的(记忆法,认为A是Async异步, 依赖前置,就是所有的依赖必须放在最前面)

2)CMD是由sea.js实现的(依赖就近,所有依赖需要了再引入

//CMD Common Module Definition define(function(require, exports, module) {
    var a = require('./a')
    a.doSomething()
    //...
    var b = require('./b') // 依赖可以就近书写
    b.doSomething()
    // ... 
    })

//AMD Asynchronous Module Definition define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
    a.doSomething()
    //...
    b.doSomething()
    //...
    })


Promise输出结果:


then 和finally的链式调用还没有理解!!! 

 

 

 

 


 

猜你喜欢

转载自blog.csdn.net/qq_39207948/article/details/125075276