前端面試&筆試&錯題指南(四)

嗯,上次寫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的博客
相關文章
相關標籤/搜索