阅读 8977

2018春节后前端面试小记

写在前面

    唉,我还是前端小渣渣一个。

最近一次的面试:

是一家云服务产品公司,面试流程也比较简单,前端负责人拿了一块白板和记号笔,心想一会要手写代码了。。。。

不过这种面试方式还是不错的。

去之前以为自己准备的差不多,其实准备的远远不够充足啊。

关于自我介绍

一开始一般会让你进行一个简单的自我介绍,我就主要说了一下上家公司的工作经历,描述了一下我负责过的项目,每个项目的业务功能,主要详细介绍每个项目用到的技术。

虽然我说了很多,但是事后觉得我在表达的时候,语言的条理性、逻辑性、准确性都不是很好,表达能力的强弱也是面试官比较看中的一部分,毕竟有可能以后和他共事交流工作内容,探讨技术等,所以,自我介绍这一块,觉得也挺重要的,自我介绍代表了面试官对你的第一印象,即便你面试很多次了,也说过很多次了,这方面还是应该好好梳理下自己的语言。


接下来考察js了,面试官慢慢的拿起了他的小白板,我的心里默默的有点小紧张。。。。

考察方式也比较中规中矩,按照js里的大类挨个考察。

  • 首先是函数方面

 for (var i = 0; i < 5; i++) { 

 setTimeout(function() {  

  console.log(i);  

}, 1000); 

}复制代码

“咦?。。。,上一次的面试不是也碰到了这个?”

然后我淡定的说出了答案:

setTimeout函数会延迟执行,那么等到执行console.log()的时候,i已经变成5了,所以最后会一次性打印出5个5;”

问:“那我要输出0~4呢?”

“怎么也有这个问题?是不是要问我有几种改法。。。”

我直接说var 改成let就可以了

for (let i = 0; i < 5; i++) { 

 setTimeout(function() {  

  console.log(i); 

  }, 1000);

 }复制代码

或者加个闭包:

for (var i = 0; i < 5; i++) { 

 (function(i) {

setTimeout(function() {  

  console.log(i); 

 }, 1000); 

 })(i)

}

复制代码

或者这样:

for(var i = 1;i < 5;i++){  

  var a = function(){  

      var j = i;    

    setTimeout(function(){  

          console.log(j);  

      },1000)  
  }  
  
a();

}复制代码

这也是用了闭包的方法,其实就相当于把var化成了let效果一样,这样i的值一开始不会被默认绑定,每执行一次循环的时候都会给i赋予新值。


问:“如果去掉function里的 i呢?”

“这样其实对内存没有保持引用, i 最后还是5。”


由于类似的面试题频繁遇到,整理了一些相关的面试题:

比如这个:

for (var i = 0; i < 5; i++) { 

 setTimeout((function(i) { 

   console.log(i); 

 })(i), i * 1000);
}复制代码

延时函数的第一个参数变成了一个立即执行函数,在这里应该是一个undefined,等价于:

setTimeout( undefined, … );

立即函数会立马执行,所以是立马就输出0~4;

Promise考察:

setTimeout(function() { 

 console.log(1)}, 0);

new Promise(function executor(resolve) { 

 console.log(2);  

for( var i=0 ; i<10000 ; i++ ) { 

   i == 9999 && resolve(); 

 } 
 console.log(3);

}).then(function() {  

console.log(4);

});

console.log(5);复制代码

考察运行机制,首先Promise是异步的,Promise只有三中状态,pending、fulfilled(成功)、rejected(失败),后两者统称为resolve(已定型),一旦状态定下句就无法再改变。

第一个延时函数首先会设置一个定时,在定时结束后传递这个函数将它放到任务队列中去,因此一开始不会输出1;

Promise里的函数会按顺序执行,输出2 3 ,Promise里的then就是会异步执行,放到当前Promise任务队列的最后执行,而console.log(5)是按顺序执行的,所以先输出5,再输出4。Promise.then()里面的回调属于 microtask, 会在当前 Event Loop 的最后执行, 而 SetTimeout 内的回调属于 macrotask, 会在下一个 Event Loop 中执行

最后,才会输出1。

最后输出: 2 3 5 4 1

  • 对象考察

先来个最简单的

var obj = {
  a: "1"
};

var obj2 = obj;

obj2.a = "2";

console.log(obj.a);复制代码

“肯定是输出 2 啊”

ojb2只是对obj实例的一个引用,到最后还是修改的obj的值。


this指针

    name = "name of window";  

    function show() {   

     var name = "name in function show()";  

      alert(this.name);   
 }  

  show();复制代码

这里定义了一个全局变量 name ,这个变量是属于 window 的,在 show 函数里也声明了一个name 变量。

this 的定义是指向调用当前函数的那个对象, show() 函数是在全局被调用的,所以this 应该指向的是当前 window对象。

再看下一个:

 var myObj = {   

     name: " my Object",

        show: function() {    

        var name = "my Object in function";  
 
         alert(this.name);  
      },  
  };   
 myObj.show();复制代码

这次,调用 myObj.show() 的对象是 myObj, 所以就会输出  my Object

  • 数组Array

先来个排序吧:

var arr = [ 2, 4, 50, 20, 3 ];复制代码

冒泡排序:

var arr = [ 2, 4, 50, 20, 3 ];

for(var i = 0 ; i < arr.length-1; i ++){

    for(var j = 0; j < arr.length-i; j++ ){

        var ls;

        if(arr[j] > arr[j+1]){

            ls = arr[j];

            arr[j] = arr[j+1]

            arr[j+1] = ls

        }
    }
}

console.log(arr);复制代码

其实我要开始想到的是用sort() ,但是sort排出来的不是很稳定,他默认排序是根据字符串Unicode码点。

还问我sort有几个参数,第二个参数是什么?

我当时没想起来,后续整理出来吧!

可选参数是一个比较函数, compareFunction ,用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。

  • 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
  • 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变;
  • 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。
  • compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。

要比较数字而非字符串,比较函数可以简单的以 a 减 b,如下的函数将会将数组升序排列

function compareNumbers(a, b) {
  return a - b;
}复制代码

所以上边那个排序就可以简单的写成:

var arr = [ 2, 4, 50, 20, 3 ];arr.sort(function (a, b) {

    return a - b;
});

console.log(arr);复制代码

或者更简洁一点:

var arr = [ 2, 4, 50, 20, 3 ];

arr.sort((a, b) => a -b );

console.log(arr);复制代码

又给我改了一下,说怎么用sort 来排序一个对象?

var obj = [
  { name: 'a', value: 21 },
  { name: 'b', value: 37 },
  { name: 'c', value: 45 },
  { name: 'd', value: -12 },
  { name: 'e' }
];复制代码

应该是这样:

obj.sort(function (a, b) {

    return (a.value - b.value);

});复制代码

顺便贴上MDN的详解


零零碎碎的问了不少,其他的记不太清了,都是一些知识性的东西,不罗列了。

然后问了一些react、 redux相关的技术原理,生命周期、单向数据流、虚拟DOM等等。。。

这些我回答的还好,因为工作天天用这个,也比较熟悉,js基础类的一些题回答的不太好,我也知道我js基础不扎实,再者,面试题看少了,虽然这些基础的知识点在工作中用的不多,有的记不清楚的就google了,也不会影响开发进度,但是,即便react 用的再熟练,重中之重的js基础!才是最重要的,框架都是js写的。

反思一下,之前工作中碰到了一些不会的或者生疏不常用的技术,google出来答案后直接就用上了,也没有仔细的去研究其中的原理,知其然不知其所以然!

事后也没有总结出来,下次再碰到这个技术点,可能又要google了。。。


不说废话了!

整理一些网上看到的不错的面试题:(转载)

  1. 关于变量声明提升和函数声明提升

alert(a);

a();

var a=3;

function a(){

    alert(10)
}   

alert(a);

a=6;

a();
复制代码

果不其然,我自己看一遍的时候就分析错了。

关键的一点就是:var 声明的变量 和function生命的函数都会提前,只是提前到作用域开始的位置,但不会进行赋值操作;

1.函数声明优先于变量声明,所以,刚开始,a就是function a(){alert(10)} ,就会看到这个函数。
2.a(),执行函数,就是出现alert(10)
3.执行了var a=3; 所以alert(a)就是显示3
4.由于a不是一个函数了,所以往下在执行到a()的时候, 报错。 

再来看:

alert(a);

a();

var a=3;

var a=function(){

    alert(10)
}   
alert(a);

a=6;

a();
复制代码

区别是,函数这次用var声明了,根据原理,他只是声明被提前,但a 没有被赋值为一个函数,

所以,一开始alert(a) 是undefined;

a() 报错;

类似的题目:

 var a=0;

function aa(){

    alert(a);

    var a=3;
}

 aa();复制代码

在aa函数里面,有var a=3,那么在aa作用域里面,就是把a这个变量声明提前,但是不会赋值,所以是undefined

var a=0;

function aa(a){ 
 
    alert(a); 
 
    var a=3; 

};

aa(5);

alert(a);

//5,0   复制代码

在函数体内,参数a的优先级高于变量a ;

var a=0;

function aa(a){

    alert(a);

    a=3;

    alert(a);

}
aa();

alert(a);
复制代码

首先没有传参数进去,aa() undefined

a=3,实际上修改的是形参a的值,并不是全局变量a,往下alert(a)也是形参a;

最后的alert(a); 是全局的a;


写在最后

不说了!。。。

我要去刷面试题,继续啃我的JavaScript 语言精粹了。