前端面试&笔试&错题指南(四)

嗯,上次写blog已是几周前的事情了,其实已经积攒了不少小问题须要记录和分享了。可是在8月底,VK我一次经历了了携程、拼多多、腾讯、网易等多轮面试轰炸,忙得不可开交,有喜有忧的同时,仍是赶快记录了不足,把一些充满迷惑性的问题继续记录和学习。javascript

JavaScript老是给人以惊喜,学习不止,进步不断,今天继续补充JS容易搞错的几道笔试/面试题,为了秋招继续努力,欢迎一块儿为秋招努力的小伙伴共勉html

系列笔记:前端

1.VK的秋招前端奇遇记(一)java

2.VK的秋招前端奇遇记(二)node

3.VK的秋招前端奇遇记(三)git

4.VK的秋招前端奇遇记(四)github

5.番外篇:前端面试&笔试算法 Algorithm面试


面试&常识题

Q1. forEach 与 map的区别? forEach不支持中断循环?

这是一道巨坑的题目算法

先看下不少博客、文章总结的一个关于循环的区别是怎么说的编程

  1. map能够作链式操做、forEach不能够
  2. map有返回值,return、 forEach没有返回值
  3. for循环不用担忧兼容性问题,能够break跳出循环,是基础循环
  4. forEach不支持continue和break,是不能退出循环自己的。

上面这些比较,自己没有什么问题,可是当第三点和第四点结合的时候,就很容易让人有个推论:

map是能够跳出循环的,能够提早中断

然而,也的确有些面试官,认为forEach不能break,map是能够的跳出的。 真的是这样的?

首先,众所周知,forEach是不能用break提早中断循环的,若是使用了,会直接报错。好比如下:

var list = [1,2,3,4];
list.forEach(item => {
    if(item === 2) {
        break;
    }
} )
复制代码

可是,若是真的想要停止? 由于会报错,这也提供给咱们一个思路,那就是用try..catch把它保住,捕获错误。 可是,我的认为真是画蛇添足,应该没人会这么用。就很少作讨论了。

那么map能够用break吗?

很显然,它也不行。因此若是有面试官问你,甚至告诉你"forEach和map的不一样,forEach不能够停止"时,你真的能够大胆回击:

  • 想要用Array.prototype.map实现break,也是彻底不可能的
  • map中文翻译为映射,所谓映射,固然是把数组中每一个值进行映射处理,有何理由中断循环,特殊处理?

Q2. 对象与继承

请问如下程序中,person1person2哪一个是Person的实例?其__proto__分别指向谁?

function Person(name) {
    this.name = name;
    return {name: name}
}
var person1 = new Person('sam');
var person2 = Person('Lily')
复制代码
Ans
person1和person2都不是Person的实例
它们的'__proto__'指向Object
复制代码

这题题目的关键就是理解new的过程,这也是常见的面试题之一了。若是遇到关键字new,那么,函数就不仅仅是一个函数了:

  • 函数执行,首先隐式创建对象this
  • 执行,绑定this相关的属性,本例中this.name = name
  • 函数最后,隐式的returnthis

固然,这里简化了这个过程,可是核心是这几步。 可是若是在隐式返回this以前,提早返回了一个对象,那么就会退出函数了。 要知道,只有这个this才是实例自己,它的__proto__才指向构造函数,若是不能把this返回出去,那么一切都是徒劳的。所以,这里不管是否new,都返回的是一个新对象{}

Q3. 垃圾回收机制方式及内存管理(爱奇艺2018提早批二面)

这里,本身当时回答的很差,就引用别人博客整理的内容啦.

回收机制方式
  1. 定义和用法:垃圾回收机制(GC:Garbage Collection),执行环境负责管理代码执行过程当中使用的内存。
  2. 原理:垃圾收集器会按期(周期性)找出那些不在继续使用的变量,而后释放其内存。可是这个过程不是实时的,由于其开销比较大,因此垃圾回收器会按照固定的时间间隔周期性的执行。
  3. 实例以下:
function fn1() {
    var obj = {name: 'hanzichi', age: 10};
}
function fn2() {
    var obj = {name:'hanzichi', age: 10};
   return obj;
}
var a = fn1();
var b = fn2();
复制代码

fn1中定义的obj为局部变量,而当调用结束后,出了fn1的环境,那么该块内存会被js引擎中的垃圾回收器自动释放;在fn2被调用的过程当中,返回的对象被全局变量b所指向,因此该块内存并不会被释放。

  1. 垃圾回收策略:标记清除(较为经常使用)和引用计数。

标记清除:

定义和用法:当变量进入环境时,将变量标记"进入环境",当变量离开环境时,标记为:"离开环境"。某一个时刻,垃圾回收器会过滤掉环境中的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。

到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或相似的策略,只不过垃圾收集的时间间隔互不相同。

引用计数:

定义和用法:引用计数是跟踪记录每一个值被引用的次数。

基本原理:就是变量的引用次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象。

内存管理
  1. 何时触发垃圾回收?

垃圾回收器周期性运行,若是分配的内存很是多,那么回收工做也会很艰巨,肯定垃圾回收时间间隔就变成了一个值得思考的问题。

IE6的垃圾回收是根据内存分配量运行的,当环境中的变量,对象,字符串达到必定数量时触发垃圾回收。垃圾回收器一直处于工做状态,严重影响浏览器性能。

IE7中,垃圾回收器会根据内存分配量与程序占用内存的比例进行动态调整,开始回收工做。

  1. 合理的GC方案:(1)、遍历全部可访问的对象; (2)、回收已不可访问的对象。
  2. GC缺陷:(1)、中止响应其余操做;
  3. GC优化策略:(1)、分代回收(Generation GC);(2)、增量GC

说实话,这一块,本身没有很好的整理,可是目前准备秋招和毕设,没有更多的经历,只能待有空再深刻学习研究了。

编程题

Q1. 表格排序

请将如下表格,按年龄进行排序,使用原生JS,不容许使用任何第三方工具。

<table>
        <thead>
            <tr>
                <th>name</th>
                <th>age</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>张三</td>
                <td>17</td>
            </tr>
            <tr>
                <td>李四</td>
                <td>43</td>
            </tr>
            <tr>
                <td>王五</td>
                <td>22</td>
            </tr>
            <tr>
                <td>小刘</td>
                <td>9</td>
            </tr>
            <tr>
                <td>黄三</td>
                <td>20</td>
            </tr>
        </tbody>
    </table>
复制代码
Ans:

这个其实很easy了,只是我在写的时候,仍是调试了好几回,这里主要两点:

  • 经过document.getElementsBytagNames选择的对象,在dom的映射机制下,是双双绑定的;
  • 使用dom.appendChild()方法,要保证参数是node节点。
  • Array.sort()方法,注意,只能用在Array上。
var sortByAge = function () {
            var tbody = document.getElementsByTagName('tbody')[0];
            var items = tbody.getElementsByTagName('tr');
            let arrayI = Array(...items); //将类数组转化为数组,使用sort方法
            arrayI.sort((a,b)=> {
                let ageA = a.getElementsByTagName('td')[1].innerText;
                let ageB = b.getElementsByTagName('td')[1].innerText;
                return ageA - ageB;
            })
           for(let i = 0; i<items.length; i++) {
               tbody.appendChild(arrayI[i]); //依次插入,这里arrayI的每个元素都是原来的dom映射过来的实例。因此并非“创造”出了复制品,而是从新排序了
           }
        }
sortByAge();
复制代码

Q2.时间'0101'转换为正常时间值(杭州有赞2018秋招二面在线编程)

一天24小时,咱们将其折为每30分钟为一段,这样一天共有48段。咱们用1表示这段时间有效,0表示无效,好比10... 表示开始时间为00:00 持续了半个小时,技术时间为00:30111001.. 则表示00:00~01:30 02:30~03:00两个时间段。

要求写一个函数,对时间码进行转换:

输入:110100000000000000000000000000000000000000000000

输出: ["00:30~01:30", "01:00~02:00"]

Ans

这个是我视频面试时的一道编程题,因为时间紧,面对面试官有点小压力,因此就用了比较笨的方法实现了,后来也没有从新思考和优化,有好的思路和简单的方法的小伙伴欢迎交流。

如下思路:

  • 遍历输入字符串
  • 当为1,判断是否为第一次,若是是第一次,根据index,记录startTime;若是不是第一次,进行time累加
  • 当为0,判断以前是否为1,若是以前是1,则根据time值进行累加,计算endTime,并将时间段push到数组
  • 判断是否为第一次、判断以前是否为1,主要经过一个flag进行记录。

根据上面思路,代码以下:

function timeTransfer(str) {
            var flag = false,  //flag,判断是否第一次为1
                time = 0,   //时间记录值
                n = str.length,
                startTime, //开始时间
                startHour, //开始的小时
                startMin,  //开始的分钟
                endTime,  //结束时间
                endHour,  //结束的小时
                endMin,  //结束的分钟
                retTime = [];  //最终返回的数组
            for(let i = 0 ; i < n; i++) {
                if(str[i] === '1') {
                    if(flag === false) {  //判断是否为第一个1,若是是,根据i设定开始时间
                        startHour = parseInt(i/2);
                        startMin = i % 2 ? 0:30;
                        startTime = `${startHour > 9? startHour: '0' + startHour}:${startMin > 9 ? startMin : '0' + startMin}`;
                        flag = true;   //置位 flag
                    }
                    time += 0.5;  //时间累加
                    if(i === n-1) {  //若是已经遍历到最后,那么计算结束时间(当str的末尾为1时,须要处理)
                        endHour = startHour + parseInt(time);
                        endMin = startMin + (time % 2 ? 0 : 30);
                        endTime = `${endHour > 9 ? endHour : '0' + endHour}:${endMin > 9 ? endMin : '0' + endMin}`;
                        retTime.push(`${startTime}~${endTime}`);
                    }
                } else if (str[i] === '0') {
                    if(flag === false) { //若是为flase,表明前面不是1,继续下次循环
                        continue;
                    } else {   //不然,计算结束时间
                        flag = false;
                        endHour = startHour + parseInt(time);
                        endMin = startMin + (time % 2 ? 0 : 30);
                        endTime = `${endHour > 9 ? endHour : '0' + endHour}:${endMin > 9 ? endMin : '0' + endMin}`;
                        retTime.push(`${startTime}~${endTime}`);
                    }
                }
            }
            return retTime;
        }
复制代码

以上程序,是跑通了的,虽然有点笨,可是勉强知足要求。

参考资料

  1. csdn wdlhao的博客
相关文章
相关标签/搜索