“惨痛”的一次面试之旅

来源:互联网 发布:算法导论第三版pdf下载 编辑:程序博客网 时间:2024/05/15 08:39

       星期一晚上刚下高铁就发现深圳乌云密布,还没等我掐指一算,雨水就哗啦啦地拍打着大地;

       雨一直下,气氛不太融洽.......手机查了一下下一个目标地的距离,发现还需要一个半小时的路程才能到达面试的地方,背上书包没多想,马不停蹄地开始转战于各个地铁口,换乘,换乘,再换乘。终于到达了目的地附近,肚子仿佛知道了这个“喜讯”,大叫一声,才发现坐了4个小时高铁的我还没吃饭;这个时候不知道哪根神经短路了,对自己说,不能吃太饱,不然等下会想睡觉,然后自己就稀里糊涂地去买了一瓶菊花茶,提提神。

       废话不多说,由于自己对于一些基本知识只是浮于表面,再加上自己还是缺少一种创新精神和思想;所以在面试中节节败退;

      直接上面试题:

1.css中的position分为哪几种,都有什么特点?

absolute

生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。

元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。

fixed

生成绝对定位的元素,相对于浏览器窗口进行定位。

元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。

relative

生成相对定位的元素,相对于其正常位置进行定位。

因此,"left:20" 会向元素的 LEFT 位置添加 20 像素。

static默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明)。inherit规定应该从父元素继承 position 属性的值。

这个是W3C上面的介绍;

如果你不给这个元素定义positon的话,其就默认为static;

然后根据这个条件我们再看一下"absolute说的相对于 static 定位以外的第一个父元素进行定位"这句话,因为这里我答错了,我回答的是absolute是被relative所限制,因为在平时在别人代码的过程中,我发现一般都是relative和absolute来进行互相组合的,比如bootstrap中的modal弹出框;所以我天真的认为只有relative可以限制absolute,其他的都不能;

然后就是fixed和absolute都是脱离了文档流的,其他的没有脱离文档流,既然脱离了文档流那么它的位置改变是不会引起重排;但是如果absolute和float同时存在,则float失去效果


(但是需要我们注意的是给元素的float属性赋值后,也能脱离文档流,进行左右浮动,紧贴着父元素(默认为body文本区域)的左右边框,直到碰到其他的浮动元素;

而此浮动元素在文档流空出的位置,由后续的(非浮动)元素填充上去:块级元素直接填充上去,若跟浮动元素的范围发生重叠,浮动元素覆盖块级元素。内联元素:有空隙就插入。)

2017/7/25 更新;

对于上面说的浮动元素我们遇到最多的问题就是如何清除浮动;(例如塌陷)

目前最常用的方法就是利用伪选择器配合clear来清除浮动;

http://www.cnblogs.com/dolphinX/p/3508869.html

关于BFC的入门可以看一下这个:(通俗一点来讲,可以把 BFC 理解为一个封闭的大箱子,箱子内部的元素无论如何翻江倒海,都不会影响到外部。)

https://zhuanlan.zhihu.com/p/25321647

http://www.cnblogs.com/CafeMing/p/6252286.html

2.如何实现一个带有还有黑色上边框的三角形?

 实现一个三角形很简单,利用border,如何让这个三角形带有黑色上边框呢? 就是类似于我们平时聊天那种气泡式对话框那个角。

我们就再创建一个黑色的三角形在我们的三角形的上方1px处,然后让其被覆盖住就行;

 下面是我自己写的一个很粗略的样式,后续改进;

  <style>#Triangle{width:0px;height:0px;border:10px solid transparent;border-bottom-color:black;position:absolute;top:10px;}#Triangle:after{content:"";width:0px;height:0px;border:10px solid transparent;border-bottom-color:#e4e4e4;position:absolute;top:-9px;left:-10px;}  </style>    <div id="Triangle">    </div>


3.如何原生实现检测一个变量是不是数组?

这个就是需要我们去实现一个isArray这个方法:


方法1:

var arr = [ ]; 

arr instanceof Array // true 

instanceof 是用来判断这个对象是不是由某个指定的构造器函数所创建出来的;


方法2:

var arr = [ ]; 
arr.constructor == Array // true  

constructor 是用来寻找这个对象的构造器函数


方法3:

var arr=[ ];

Object.prototype.toString.call(arr) === '[object Array]';

我们需要用对象原型上的toString这个方法来获取对象的类型,因为其他数组类型都有自己的toString这个方法。



4.实现一个throttle节流函数

var throttle = function(callback,delay){var last = 0  return function(){var curr = new Date().getTime();if (curr - last > delay){callback.apply(this, arguments)last = curr }}}document.onmousemove=throttle(function(){console.log("lzj");},100)
这就是一个节流函数,保证我的mousemove事件只在100ms中触发一次;

new Date().getTime(); 返回的是指定的日期和时间距 1970 年 1 月 1 日午夜(GMT 时间)之间的毫秒数。

callback为我们需要节流的函数,我们限制这个函数在delay这段时间内只运行一次;

由于last最开始的时候就给了0,所以这里函数刚执行的时候肯定会运行我们需要调用的callback,然后把这次时间记录在last中(last是利用闭包的特性保存下来),再进入这个函数的时候又会得到一个新的时间,我们再跟上次的时间比较一下,如果大于这个时间,我们则执行,否则不执行;



下面是uderscore.js中的源码:

 _.throttle = function(func, wait, options) {    var context, args, result;    var timeout = null;    var previous = 0;    if (!options) options = {};    var later = function() {      previous = options.leading === false ? 0 : _.now();      timeout = null;      result = func.apply(context, args);      if (!timeout) context = args = null;    };    return function() {      var now = _.now();      if (!previous && options.leading === false) previous = now;      var remaining = wait - (now - previous);      context = this;      args = arguments;      if (remaining <= 0 || remaining > wait) {        if (timeout) {          clearTimeout(timeout);          timeout = null;        }        previous = now;        result = func.apply(context, args);        if (!timeout) context = args = null;      } else if (!timeout && options.trailing !== false) {        timeout = setTimeout(later, remaining);      }      return result;    };  };


然后我们也需要了解一下函数消抖:

var debounce = function(callback, delay){var lastreturn function(){var ctx = this, args = argumentsclearTimeout(last)last = setTimeout(function(){callback.apply(ctx, args)}, delay)       }    }document.onmousemove=debounce(function(){console.log("lzj");},100)

这个函数保证我们的mousemove移动100ms以后没有其再次移动鼠标后触发;

其实这种函数思想我们可以用在keydown keyup这种用户输入信息的校验上,等其输入完毕后再进行校验;

程序的思想在于我们利用setTimeout来延时执行,如果这段时间内没有再次触发这个函数则执行,否则就清除掉上一次的定时器,再开启一个定时器去执行;


下面是underscore.js中的源码:

 _.debounce = function(func, wait, immediate) {    var timeout, args, context, timestamp, result;    var later = function() {      var last = _.now() - timestamp;      if (last < wait && last >= 0) {        timeout = setTimeout(later, wait - last);      } else {        timeout = null;        if (!immediate) {          result = func.apply(context, args);          if (!timeout) context = args = null;        }      }    };    return function() {      context = this;      args = arguments;      timestamp = _.now();      var callNow = immediate && !timeout;      if (!timeout) timeout = setTimeout(later, wait);      if (callNow) {        result = func.apply(context, args);        context = args = null;      }      return result;    };  };


5.一道综合的面试题

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.getName();   getName();   Foo().getName();    getName();    new Foo.getName();    new Foo().getName();    new new Foo().getName();  

在网上一查果然是道很经典的面试题,╮(╯▽╰)╭竟然没做过。

OK,我就重新做一次吧~

Foo.getName( );

这个答案是2,因为函数也是一个对象,所以也能为其添加属性;

getName( );

这个答案是4,因为函数声明在变量声明的前面,var getName=function( ){ console.log( 4) }也会覆盖掉 函数定义的getName( );

Foo( ).getName( );

这个答案是1,这里就需要注意一下了,Foo这个函数返回的是this,我们说过this是谁,需要看什么时候调用,谁调用它,我们可以看出,这个this是window,而且Foo中又重新对于getName( )进行了赋值,且为全局变量,所以为1。

getName() ;

这个答案也是1,因为刚才执行了Foo( ),所以对全局变量getName进行了新的赋值;

new Foo.getName( ); 

这个答案是2,因为Foo.getName( )优先级高于 new ,所以先执行Foo.getName( ); 得出了是2,再new一个2,所以还是2;

new Foo( ).getName( );

这个地方答案是3,因为new Foo( )的优先级高于 . (我们的点操作符) 这时候的Foo()中的this,因为有new的存在,所以这个this不是window,而是Foo,所以我们这里的new Foo( )其实是new了一个Foo对象的实例,因此,我们需要调用getName( )的时候需要去它原型上找,因此为3;

new new Foo( ).getName( );

这个答案是3,这里先执行的是new Foo( )得到一个Foo的实例,然后调用getName( ),最后对于这个结果再进行new,其实就是对于3再new 一下,结果还是3;


( 对于最后两个答案可以参见一下这个博客,出题人就是他~~http://www.cnblogs.com/xxcanghai/p/5189353.html#!comments)


6 理解 JavaScript 的 async/await  (我还要继续看一下)


/*下面是其他面试的补充*/

7.在 javascript 中获取格式为 yyyy-mm-dd 的日期字符串 

var date = new Date(); var mon = date.getMonth() + 1;var day = date.getDate();var nowDay = date.getFullYear() + "-" + (mon<10?"0"+mon:mon) + "-" +(day<10?"0"+day:day);
注:如果想获得自己定义的一个时间,则改成var date=new Date("2016-5-12") ;

        获取小时:getHours( 0---23);   获取分钟:getMinutes(0--59);  获取秒钟:getSeconds(0--59);


8.求一个字符串中出现次数最多的字符次数,及这个字符;

/*求字符串中出现次数最多的字符*/function fn(string){var obj={};var temp=[];var len=string.length;var max=0;for(var i=0;i<len;i++){if(obj[string[i]]){obj[string[i]]++;}else{obj[string[i]]=1;}}for( key in obj){if(obj[key]>max){max=obj[key];}}for(var key in obj){if(max==obj[key]){temp.push(key);}}return temp; }var myWord="aabbddeee";console.log(fn(myWord));


其实这里用到了数组去重的一个思想,只不过在去重的逻辑里面稍加修改,关键逻辑是:如果我这个字符不存在,则给这个字符对应的值赋为1,如果已经存在了,则累加,这样就很容易算出我们每一个字符对应的一个出现字数;然后再去遍历求出最大次数; 当然我们还需要考虑到最大次数存在多个字符的情况下,不过这里用对象就一起解决了;


9.HTML页面加载和解析的流程

(网上找的)

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

1.用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件; 
2. 浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件; 
3. 浏览器又发出CSS文件的请求,服务器返回这个CSS文件; 
4. 浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了; 
5. 浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码; 
6. 服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码; 
7. 浏览器发现了一个包含一行javascript代码的<script>标签,赶快运行它; 
8. Javascript脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”)。突然少了这么一个元素,浏览器不得不重新渲染这部分代码; 
9. 终于等到了</html>的到来,浏览器泪流满面…… 
10. 等等,还没完,用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径; 
11. 浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

注意

从上面看出来js是加载后立即执行,所以它会阻塞后面的渲染;

            解决办法:a.尽量把js外部链接的脚本放在页面的最后加载,也就是</body>的前面,将多个js进行合并成一个;

                                b.利用<script>标签的defer属性去标示本元素所含的脚本不会修改DOM,因此代码会安全的延迟执行;H5中新加了一个async属性,也是用于异步加载脚本,它和defer的相同点是采用并行下载,不会阻塞页面渲染;区别在于async是加载完后自动执行,defer是需要等待页面完成后再执行;

                                c.动态创建脚本,我们可以通过doucument.createElement("script")来进行动态创建<script>标签;


刚刚自己实践了js脚本异步加载,发现有几点我们在使用过程中需要注意的是:

  用defer或者是async脚本是外部链接,否则不生效;

比如: 我想去获取DOM节点

<script type="text/javascript" defer>    var div=document.getElementById("Triangle")    console.log(div);</script>
这样是不行的,需要把代码放到一个外部js中,这样引用才可以

  <script type="text/javascript" src="event.js" defer>  </script>

还有就是async是文件下载完就执行,defer是页面渲染完才执行,所以async是不稳定的,顺序上没有完全依赖;我们最好使用这两个属性的时候,文件代码没有对节点的访问;


而且css的加载顺序是从右到左匹配,例如 .nav li 是先找到所有的li标签,然后去寻找拥有nav这个class的标签;所以我们尽量去减少CSS选择的DOM深度;(JQ的匹配顺序是从左到右)



10.求一个数组中长度最长的升序的开始索引和长度

function searchIncreaSequence(arr){if(!arr){return;}if(arr.length<3){return;}var tempMaxLen=1, //临时保存最大升序长度    tempMaxIndex=0, //临时保存最大升序的开始索引    arrLen=arr.length;var obj=new Object();//返回对象,保存最后的最大升序长度和开始索引obj.maxLen=1;obj.maxIndex=0;for(var i=0;i<arrLen-1;i++){if(arr[i]<arr[i+1])//对数组是否是升序进行判断{tempMaxLen++;if(tempMaxLen>obj.maxLen) //比较出最大的升序长度{obj.maxLen=tempMaxLen;obj.maxIndex=tempMaxIndex;//保存开始索引}}else{ //如果数组不是升序,则重置最大长度和开始索引tempMaxLen=1;tempMaxIndex=i+1;}}return obj;}var sls=[1,2,3,1,2,3,4,1,2,3];var result=searchIncreaSequence(sls);console.log("最大长度:"+result.maxLen+" "+"开始索引:"+result.maxIndex);



11.关于setTimeout的面试题


for(var i=0;i<5;i++){setTimeout(function(){console.log(i);},i*1000);}


刚开始输出一个5,然后每隔一秒钟输出一个5,一共5个5;


for(var i=0;i<5;i++){(function(i){  setTimeout(function(){    console.log(i);  },i*1000)})(i)}

刚开始输出一个0,然后每隔一秒输出1,2,3,4;


for(var i=0;i<5;i++){(function( ){  setTimeout(function(){    console.log(i);  },i*1000)})(i)}

刚开始输出一个5,然后每隔一秒钟输出一个5,一共5个5;


for(var i=0;i<5;i++){setTimeout((function(){console.log(i);})(i),i*1000);}

立即输出0,1,2,3,4


setTimeout(function () {    console.log(1)}, 0);new Promise(function executor(resolve) {    console.log(2); for(var i=0;i<1000;i++) {    i==0000&&resolve(); }    console.log(3);}).then(function () {    console.log(4);});    console.log(5);


输出2,3,5,4,1

Promise的任务会在当前事件循环末尾中执行,而setTimeout中的任务是在下一次事件循环执行


12.实现一个计数器函数,每调用一次,其返回值加1;

function count(){  var num=1;    count=function(){  num++;  return num;  }  return num;}console.log(count());console.log(count());console.log(count());


OK~还要就是一些自己做的项目上是否有想过性能优化方面的问题等等,总之这一次面试经历让自己很“惨痛”,和面试官的交流就能看出来是什么样才是一个“合格”的程序员,而且有些基础知识自己也看到过,但是浮于表面,只是见过,没有真正理解,有些问题自己也想过,但是没有继续深入下去研究等等......然后最后还想说的就是数学不好的也不能当好一个程序员~~╭(╯^╰)╮


      回到家已经是晚上10点钟,长达5个小时的路程加面试让自己很疲惫又很兴奋,肚子似乎也能体会到我的感受,又吆喝了我一声,于是乎自己买了一碗泡面和一包辣条开始思考自己没答上来的问题...............




原创粉丝点击