前端面试题汇总——JS篇

476 阅读5分钟
无论你要应聘的职位是初级、中级还是高级;js基础都是必须要考察的一项硬技能。只有js基础扎实了,才能更出色的完成开发任务和技能的提升。
下面我会尽量全面的涵盖要考察的知识点(作用域、闭包、算法、原型链等)。以题目:答案+解析的方式总结给大家。

话不多说,来看下我亲历的一些面试题。

PS:水平有限,我可以保证的是所有的答案都经过验证。是正解但不一定是最优解。所以欢迎各位大佬指导或补充。

题目1:

function b(){
    console.log(a);
}

function c(){
    var a = 'world';
    b();
}  

c();
---------------------------------------
var a = 'hello';        

function c(){
    var a = 'world';
    function b(){
        console.log(a);
    }
    b();
}

c();

问:c()运行结果分别输出什么?

解析:

本题考察的知识点:'js作用域'

Javascript采用的是词法作用域(静态作用域),所以它的函数的作用域在函数定义的时候就决定了;
与词法作用域相对的是动态作用域,它的函数的作用域是在函数调用的时候才决定的。

关键词:'定义的时候'
b()执行时要输出a,因此要在b定义的作用域去找a。
上面的b定义时,a的值为'hello';下面b定义时,a的值为'world'。
因此,前者输出'hello';  后者输出'world'。 

题目2:

var a = [];

for(var i = 0; i < 5; i++){
    a.push(function(){return console.log(i)})
}

问:a[0]();a[1]();会得到什么结果?

解析

本题考察的知识点:'js作用域--全局作用域、块级作用域、闭包';
1、这段代码中i是个全局变量。
2、push进去的匿名函数在for循环结束后调用
3、循环结束后i的值已变为5,因此循环外执行函数,得到的结果永远是5

'如何得到输出结果与序号相等?即:a[0]()==>0;a[1]()==>1……'
答案1
for(let i = 0; i < 5; i++){
    a.push(function(){return console.log(i)})
}1、let声明i,将i的作用域限制在for循环内;
2、闭包可以记住自己的作用域

答案2
for(var i = 0; i < 5; i++){
    a.push(function(){return console.log(i)});
    a[i]();
}
循环内部调用,此时i还未被重新赋值。

'相似的题型(这道题非常经典随处可查到解析):'
for(var i = 0; i < 5; i++){
     setTimeout(function(){
        console.log(i)
    },0)
}
输出结果:5次5

实现分别输出0,1,2,3,4的方法
答案1:
for(let i = 0; i < 5; i++){
     setTimeout(function(){
        console.log(i)
    },0)
}

答案2:
for(var i = 0; i < 5; i++){
     (function(j){
        setTimeout(function(){
            console.log(j)
        },0)      })(i)
}

我看到的最好的题目变型和解析:https://juejin.cn/post/6844903474212143117


题目3:

var m = true;

setTimeout(function(){
    m = false;
},3000);

while(m){};

alert(1);

'请问什么时候弹出1?'

解析:

本题考察的知识点:'event loop、while循环'
这里抛出一个链接来学习一个event loop:
http://latentflip.com/loupe/?code=dmFyIG0gPSB0cnVlOw0Kc2V0VGltZW91dChmdW5jdGlvbigpew0KICAgIG0gPSBmYWxzZTsNCn0sMzAwMCk7DQp3aGlsZShtKXt9Ow0KYWxlcnQoMSk7!!!Y29uc29sZS5sb2coJ3N0YXJ0Jyk7CglzZXRUaW1lb3V0KCgpPT57Cgljb25zb2xlLmxvZygnY2hpbGRyZW4yJyk7CglQcm9taXNlLnJlc29sdmUoKS50aGVuKCgpPT57Cgljb25zb2xlLmxvZygnY2hpbGRyZW4zJykKCX0pCn0sMCkKbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSxyZWplY3QpewoJY29uc29sZS5sb2coJ2NoaWxkcmVuNCcpCglzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7CgkJY29uc29sZS5sb2coJ2NoaWxkcmVuNScpCgkJcmVzb2x2ZSgnY2hpbGRyZW42JykKCX0sMCkKfSkudGhlbigocmVzKT0%2BewoJY29uc29sZS5sb2coJ2NoaWxkcmVuNycpCglzZXRUaW1lb3V0KCgpPT57Cgljb25zb2xlLmxvZyhyZXMpCgl9LDApCn0p
只要理解了时间循环机制,上面的问题答案就很容易得到了。
while循环是个同步语句,同步循环未结束就一直无法出栈,下一段同步代码alert就无法执行。就更别说setTImeout了。
setTimeout是异步语句属于宏任务。会一直在任务队列里等待,无法入栈。
因此这个代码会选入死循环,'永远无法弹出1'。

下面是event loop的经典题目:
console.log('start');

setTimeout(()=>{
    console.log('children2');
    Promise.resolve().then(()=>{
        console.log('children3')
    })},0)

new Promise(function(resolve,reject){
    console.log('children4');
    setTimeout(function(){
        console.log('children5');
        resolve('children6');
    },0)
}).then((res)=>{
    console.log('children7');
    setTimeout(()=>{
        console.log(res);
    },0)
})
答案我就不写啦,自己cope代码在浏览器上执行看看得到的结果跟你的答案一不一样吧
ps:这也是一道面试题哦。

题目4:手写一个深拷贝

解析:

本题考察的知识点:'js的数据类型、检测数据类型的方法及区别、函数的递归'下面是我封装的deepClone函数:
function deepClone(data){
    if(typeof data !== 'object' || data === null){
        return data;
    }

    var newData;
    if(data instanceof Array){
        newData = [];
        if(!data.length){
            return newData;        }
        for(var i = 0; i < data.length; i++){
            newData[i] = deepClone(data[i]);
        }
    }

    if(data instanceof Object){
        newData = {};        if(!Object.keys(data).length){
            return newData;
        }
        for(var key in data){
            newData[key] = deepClone(data[key]);
        }
    }
    return newData;
}

'js数据类型:'
基本数据类型(Number、String、Undefined、Null)
复杂数据类型/引用数据类型(Function、Array、Object)

'检测数据类型的方法及区别:'
1、typeof 检测不出Array类型。Arry识别为objec
2、instanceof 不能用于监测基本类型值
3、constructor 不能检测null和undefined
4、Object.prototype.toString.call 可检测出所有数据类型

给个文章,写的挺详细:https://blog.csdn.net/weixin_40690385/article/details/81509543

题目5:

写一个函数:遍历一个数组,如果数组中的某两项相加等于另一项输出true,否则输出false;如 a1=[1,5,6]可输出true;a2=[3,4,5,0,6]只能输出false。

本题考察的知识点:'函数的封装、简单的算法'
function fn(arr){
    if(!arr || arr.constructor !== Array){
        return;
    }
    for(var i = arr.length-1; i>=2; i--){
        for(var j = arr.length-2;j>=1; j--){
            for(var k = arr.length-3; k >=0; k--){
          	if(i != j && i!= k && j != k){
                    if(arr[k] + arr[j] === arr[i]){
                        console.log(true);
                    }else{
                        console.log(false)
                    }
		}
            }
        }
    }
}
感觉不是最佳答案,我的解题思路往往不够高阶。欢迎提出新方案