每天一道面试题系列


文章内容输出来源:拉勾前端高薪训练营

20200714 阅读下面代码,写出打印结果(只考虑浏览器环境)
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的静态方法,所以是打印结果为2之前没想通函数为啥可以像对象一样调用方法,后来一想函数就是特殊的对象当然可以像对象一样添加和调用方法和属性了
Foo.getName();
// 先调用Foo()返回this是window,因为Foo()其实是window调用的只不过window调用可以省略不写,因为我们调用Foo的时候对window下的getName重新赋值所以打印结果为1,如果在node环境跑是会报错的因为node环境没有window对象
Foo().getName();
// 因为上一行代码已经对window下getName重新赋值了,所以打印结果为1
getName();
// 下面三道题要注意运算符优先级的问题,new Foo()和.运算符和new Foo的优先级依次递减
// 先调用Foo.getName();所以打印结果为2。然后在执行new关键字,关键字依次做了这几件事1.创建一个临时的空对象,为了表述方便,我们命名为 fn,让对象 fn 的隐式原型指向函数 Foo 的显式原型;2.执行函数 Foo(),将 this 指向对象 fn,并传入参数 args,得到执行结果 result;3.判断上一步的执行结果 result,如果 result 为非空对象,则返回 result,否则返回 fn。
new Foo.getName();
// 先调用new Foo()得到一个Foo的实例对象,为了表述方便也暂时称为fn,然后再fn.getName()进行调用,fn自己没有,然后去原型找所以打印结果为3
new Foo().getName(); 
// 第一步先调用new Foo()得到一个Foo的实例对象,为了表述方便也暂时称为fn,第二步再fn.getName()进行调用,fn自己没有,然后去原型找所以打印结果为3,最后再执行new 第二步fn.getName()得到的函数
new new Foo().getName();

答案为:2、1、1、2、3、3

20200715 阅读下面代码,写出打印顺序及原因
function Foo(){
    for (var i = 0; i < 5; i++) {
        setTimeout(function timer() {
            console.log(i);
        }, 1000);

    }
    console.log(i);
}
Foo()

解析:i使用的是var声明,没有块级作用域,所以每次加1都是修改的同一个i,至于打印顺序的话则是使用了settimeout异步函数,它会先把settimeout里面的代码块放到一个队列中去,然后继续执行同步代码,等到所有同步代码都执行完毕后再去队列中执行时间到了代码块。所以先打印最后一个console结果为5,然后等待1s后打印五次5

// 自执行函数的两种写法
// (function () { console.log(3333);}());
// (function () { console.log(222);})();
function Foo(){
    for (var i = 0; i < 5; i++) {
        (function(i){
            setTimeout(function timer() {
                console.log(i);
            }, 1000);
        })(i)
    }
    console.log(i);
}
Foo()

解析:这里虽然也是用的var去声明但是它有个自执行函数形成了一个闭包每循环一次都会单独形成作用域,并依次保存Foo作用域下的i的值,它会先把settimeout里面的代码块放到一个队列中去,然后继续执行同步代码,等到所有同步代码都执行完毕后再去队列中执行时间到了代码块。所以先打印最后一个console结果为5,然后等待1s后依次0,1,2,3,4。

setTimeout(function() {
    console.log('bbbb');
}, 1000);
console.log('cccc');
setTimeout(function() {
    console.log('dddd');
}, 0);

解析:setTimeout指在指定的时间后执行一段代码;所以先执行同步打印出cccc后执行等待时间较短的异步打印dddd,最后在1s后打印bbbb

setTimeout(function() {
    console.log('bbbb');
}, 1000);
const start = new Date();
while (new Date() - start < 3000) {}
console.log('cccc');
setTimeout(function() {
    console.log('dddd');
}, 0);

解析:因为while是同步的所以会阻塞,又因为结束循环的条件是实时获取的时间减去代码执行那一刻获取的时间要大于等于3000所以这里因为会一直执行到3秒在循环期间第一个settimeout已经在队列中循环结束后先打印cccc然后再去执行第一个settimeout打印出bbbb,之后再把最后一个添加到队列中,之后执行打印dddd

20200716 阅读下面代码,写出打印结果及原因
var a = 10;
var obj = {
    a: 100,
    pro:{
        getpro: () => {
            console.log(this.a);
        }
    }
}
obj.pro.getpro()

解析:因为getpro是一个箭头函数,所以getpro自己是不会创建this的,他会从自己作用域也就是getpro作用域的上一层即全局作用域继承this,因为js没有块及作用域,除了全局作用域外,只有函数才能创建作用域,所以this指向window。所以打印出来的结果是10。如果把全局a的声明换成let那么则打印结果是undefined,因为let声明得变量不会挂载到window上而是挂载到script上

var a = 10;
var obj = {
    a: 100,
    pro:{
        getpro: function() {
            console.log(this.a);
        }
    }
}
obj.pro.getpro()

解析:当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象,此时this指向pro,而此时pro中没有定义a所以结果打印为undefined,this 的绑定只受最靠近的成员引用的影响,并不会说pro没有往上一层obj中寻找

var a = {n:1}
var b = a;
a.x = a = {n:2};
console.log(a.x);
console.log(b.x);

解析:为了表述方便把{n:1}称为A对象;{n:2}称为B对象。题中a的引用地址指向同一个A对象后,又把a的引用地址赋值给b,所以a和b都指向同一个A对象;而后就是一个连续赋值的表达式,首先我们需要知道“.”运算符的优先级是要高于“=”赋值符的,所以会先执行“a.x”这部分代码,即给A对象添加一个key为x的属性,并且因为还未赋值所以默认是undefined,此时a和b引用地址还是指向A对象即{n:1,x:undefined};“.”运算符执行完后就要开始执行“=”赋值符了,而“=”赋值符的执行顺序是从右往左,所以先计算“a = {n:2}”这部分,这里把a重新赋值的一个新的引用地址指向B对象即{n:2}。最后执行最左边的“=”赋值符即“a.x = a”,因为前面已经了执行“.”运算符,这里不会在执行“.”运算符了,所以此时的“a.x”其实就是指的A对象中的x属性即b变量的x属性,而并不是我们表面看到的a变量的x属性,而这时候的变量a的引用地址指向B对象即{n:2},所以此时“a.x = a”相当于“b.x = {n:2}”。最后结果是a = {n:2};b = {n:1,x:{n:2}};即最后打印为a.x的结果为“undefined”,b.x的结果为“{n:2}”

20200717 阅读下面代码,写出打印结果及原因
function a(xx) {
    this.x = xx;
    return this;
}
var x = a(5);
var y = a(6);
console.log(x.x);
console.log(y.x);

解析:函数a是window调用的所以this指向window,而函数a在window对象添加了x属性所以此时window.x=5,但是函数返回了this即window对象然后又重新赋值给了用var声明的全局变量x此时x为window对象,第二次调用函数的时候在又把全局变量x赋值为6,然后返回添加了x属性的window对象并赋值给y。所以x.x打印为undefined;y.x打印为6。

var length = 10;
function fn() {
    console.log(this.length);
}
var obj = {
    length: 5,
    method: function(fn){
        fn();
        arguments[0]();
    }
}
obj.method(fn,1)

解析:obj调用了method方法,本着谁调用this就是谁的原则,method函数内部的this确实指向obj,所以很容易以为fn()时打印的结果是5,其实并不是,因为fn虽然在method函数中调用的,但是又没有使用apply,call,bind这些方法改变this的指向,又因为fn函数是在全局中声明的,它绑定的是window,即window.fn(),而全局变量length = 10;所以打印结果为10。接着又调用 arguments[0]();arguments指的是当前函数的实参他是一个伪数组,他的每一项就是实际穿过来的参数“obj.method(fn,1)”是两个,而 arguments[0]就是fn这个函数,所以其实是arguments调用的fn(),所以this指向arguments,而arguments的length为2,所以第二次打印为2。即会执行2次fn函数,结果为10,2。

20200718 阅读下面代码,写出打印顺序及原因
console.log('aaa');
setTimeout(() => {
    console.log('bbb');
}, 1000);
const start = new Date()
while (new Date() -start < 3000) {
    
}
console.log('ccc');
setTimeout(() => {
    console.log('ddd');
}, 0);
new Promise((resolve, reject) => {
    console.log('eee');
    foo.bar(100)
})
.then(()=> console.log('fff'))
.then(()=> console.log('ggg'))
.catch(()=> console.log('hhh'))
console.log('iii');

解析:把所有代码加载后先从上往下执行所有同步代码,然后是所有微任务(Promise等等),最后是所有宏任务(setTimeout等等)。
也就是说微任务是可以插队的,虽然可能宏任务在队列中排第一但是遇到任何一个微任务他就得让位。

这里的循环也是同步的所以先打印同步代码aaa、ccc、eee、iii。注意Promise中的参数是一个回调函数,这个回调函数的内部的代码是同步的所以在输出aaa后输出eee,接着执行foo.bar(100);进行调用因为没有这个方法所以他会把错误抛给reject,也就是catch,Promise的异步核心在于resolve和reject,又因为它们是微任务所以优先于宏任务,低于同步任务所以暂时不会执行catch抛出错误而是继续执行未执行完的同步。等到最后一个同步iii执行完后执行catch打印hhh,此时所有微任务执行完了,接着执行宏任务,由于循环执行了3秒而第一个宏任务只要等待一秒,所以虽然循环后面的那个宏任务是等待0秒但是还是会先执行第一个,所以打印bbb,接着打印ddd.
最后结果为:aaa、ccc、eee、iii、hhh、bbb、ddd

20200720 阅读下面代码,写出打印顺序
    new Promise(function (resolve, reject) {
        console.log('a');
        resolve();
    })
        .then(function () {
            new Promise(function (resolve, reject) {
                console.log('b');
                resolve();
            })
                .then(function (resolve, reject) {
                    console.log('c');
                })
                .then(function () {
                    new Promise(function (resolve, reject) {
                        console.log('d');
                        resolve();
                    })
                        .then(function (resolve, reject) {
                            console.log('e');
                        })
                        .then(function (resolve, reject) {
                            console.log('f');
                        })
                    console.log('g');
                })
            console.log('h');
        })
        .then(function (resolve, reject) {
            console.log('i');
        })
    new Promise(function (resolve, reject) {
        console.log('j');
        resolve();
    })
        .then(function (resolve, reject) {
            console.log('k');
        })
        .then(function (resolve, reject) {
            console.log('l');
        })

解析:

// 第一层第一个
    new Promise(function (resolve, reject) {
        console.log('a');
        resolve();
    })
    // 第一轮第一个微任务
        .then(function () {
            new Promise(function (resolve, reject) {
                console.log('b');
                resolve();
            })
            // 第二轮第一个微任务    若‘第一轮第一个微任务’中没有有new Promise在第三轮第一个
                .then(function (resolve, reject) {
                    console.log('c');
                })
                // 第三轮第一个
                .then(function () {
                    new Promise(function (resolve, reject) {
                        console.log('d');
                        resolve();
                    })
                    // 第四轮第一个
                        .then(function (resolve, reject) {
                            console.log('e');
                        })
                        // 第五轮第一个
                        .then(function (resolve, reject) {
                            console.log('f');
                        })
                    console.log('g');
                })
            console.log('h');
        })
        // 第二轮第二个微任务
        .then(function (resolve, reject) {
            console.log('i');
        })

    // 第一层第二个
    new Promise(function (resolve, reject) {
        console.log('j');
        resolve();
    })
    // 第一轮第二个微任务
        .then(function (resolve, reject) {
            console.log('k');
        })
        // 第二轮第三个微任务
        .then(function (resolve, reject) {
            console.log('l');
        })
/**
 * 首先第一层 new Promise,里面的同步代码先执行,所以依次打印 a j;
 * 然后开始执行‘第一轮第一个微任务’,
 * ‘第一轮第一个微任务’中又有new Promise,但是因为第一轮微任务已经开始了,所以会放到下一个微任务的第一个,
 * 然后还是先执行‘第一轮第一个微任务’的同步代码,所以依次打印 b h;,此时‘第一轮第一个微任务’执行完毕。
 * 接着执行‘第一轮第二个微任务’打印 k,此时第一轮全部执行完毕。
 * 接着执行‘第二轮第一个微任务’,打印 c;执行‘第二轮第二个微任务’打印 i,
 * 为什么会先打印c而后打印i呢因为‘第一轮第一个微任务’中又有new Promise,
 * 但是因为第一轮微任务已经开始了,所以会放到下一个微任务的第一个。
 * 接着执行‘第二轮第三个微任务’打印 l ,此时第二轮全部执行完毕。
 * 接着执行‘第三轮第一个’又有new Promise,但是因为第三轮微任务已经开始了,所以会放到下一个微任务的第一个,
 * 接着执行同步代码依次打印 d g ,此时第二轮全部执行完毕。
 * 接着执行‘第四轮第一个’打印 e 此时第四轮全部执行完毕。
 * 接着执行‘第五轮第一个’打印 f 此时第五轮全部执行完毕。
 * 最后打印结果:a j b h k c i l d g e f
 * 
*/

再看下面代码执行顺序


// 第一层第一个
new Promise(resolve => {
    console.log('1')
    resolve()
  })
        // 1.1
        // 第一轮第一个then下面嵌套的then
        .then(function() {
            console.log('3')
            new Promise(resolve => {
                console.log('4')
                resolve()
            })
                // 2.1
                .then(function() {
                    console.log('7')
        
                })
                    // 3.1
                    .then(function() {
                        console.log('12')
                    })
                        // 4.1
                        .then(function() {
                            console.log('15')
                        })
        })
            // 2.2
            // 第二轮第二个then下面嵌套的then就是第n+1轮第n个
            .then(function() {
                new Promise(resolve => {
                    console.log('8')
                    resolve()
                })
                    // 3.2
                    .then(function() {
                        console.log('13')
            
                    })
                        // 4.2
                        .then(function() {
                            console.log('16')
                            new Promise(resolve => {
                                console.log('17')
                                resolve()
                            })
                                // 5.2
                                .then(function() {
                                    console.log('19')
                                    new Promise(resolve => {
                                        console.log('20')
                                        resolve()
                                    })
                                        // 6.2
                                        .then(function() {
                                            console.log('21')
                                
                                        })
                                            // 7.2
                                            .then(function() {
                                                console.log('22')
                                        
                                            })
                                })
                        })
            })
// 第一层第一个
  new Promise(resolve => {
    console.log('2')
    resolve()
  })
        // 1.2
        .then(function() {
            console.log('5')
            new Promise(resolve => {
                console.log('6')
                resolve()
            })
                // 2.2
                .then(function() {
                    console.log('9')
        
                })
        })
            // 2.3
            .then(function() {
                console.log('10')
                new Promise(resolve => {
                    console.log('11')
                    resolve()
                })
                    // 3.3
                    .then(function() {
                        console.log('14')
            
                    })
                        // 4.3
                        .then(function() {
                            console.log('18')
                        })
            })

/**
 * 1.1表示第一轮第一个
 * 第x轮第y个then嵌套的Promise中的then为第x+1轮第y个
 * 当有重复的例如上面代码有两个2.2则按从上到下执行
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值