前端面試題及答案整理(一)

各公司秋招很快就開始了,最近在準備面試的東西,乾脆將發現的各種面試題整理一下共享出來,大部分面試題是沒有標準答案的,我給出的答案也是僅供參考,若是有更好的解答歡迎在評論區留言。javascript

Part1 手寫代碼

現場手寫代碼是如今面試中很常見的一類面試題,考察基礎的數據結構與算法能力。html

1 數組去重的實現

  • 基本數組去重
Array.prototype.unique = function(){
    var result = [];
    this.forEach(function(v){
        if(result.indexOf(v) < 0){
            result.push(v);
        }
    });
    return result;
}
  • 利用hash表去重,這是一種空間換時間的方法
Array.prototype.unique = function(){
    var result = [],hash = {};
    this.forEach(function(v){
        if(!hash[v]){
            hash[v] = true;
            result.push(v);
        }
    });
    return result;
}

上面的方法存在一個bug,對於數組[1,2,'1','2',3],去重結果爲[1,2,3],緣由在於對象對屬性索引時會進行強制類型轉換,arr[‘1’]和arr[1]獲得的都是arr[1]的值,所以需作一些改變:java

Array.prototype.unique = function(){
    var result = [],hash = {};
    this.forEach(function(v){
        var type = typeof(v);  //獲取元素類型
        hash[v] || (hash[v] = new Array());
        if(hash[v].indexOf(type) < 0){
            hash[v].push(type);  //存儲類型
            result.push(v);
        }
    });
    return result;
}
  • 先排序後去重
Array.prototype.unique = function(){
    var result = [this[0]];
    this.sort();
    this.forEach(function(v){
        v != result[result.length - 1] && result.push(v); //僅與result最後一個元素比較
    });
}
  • 利用ES6 Set去重
//ES6環境下
    Array.prototype.unique = function(){
         return [...new Set(this)];
    }

2 快速排序的實現

方法一(儘量不用js數組方法):node

function quickSort(arr){
    qSort(arr,0,arr.length - 1);
}
function qSort(arr,low,high){
    if(low < high){
        var partKey = partition(arr,low,high);
        qSort(arr,low, partKey - 1);
        qSort(arr,partKey + 1,high);
    }
}
function partition(arr,low,high){
    var key = arr[low];  //使用第一個元素做爲分類依據
    while(low < high){
        while(low < high && arr[high] >= arr[key])
            high--;
        arr[low] = arr[high];
        while(low < high && arr[low] <= arr[key])
            low++;
        arr[high] = arr[low];
    }
    arr[low] = key;
    return low;
}

方法二(使用js數組方法):面試

function quickSort(arr){
   if(arr.length <= 1) return arr;
   var index = Math.floor(arr.length/2);
   var key = arr.splice(index,1)[0];
   var left = [],right = [];
   arr.forEach(function(v){
       v <= key ? left.push(v) : right.push(v);
   });
   return quickSort(left).concat([key],quickSort(right));
}

另外要知道,快速排序的平均時間複雜度O(nlogn),最壞狀況是有序的狀況,時間複雜度爲n的平方,另外快速排序是不穩定的。算法

Part2 JavaScript相關

1 JavaScript基礎數據類型

JavaScript數據類型包括原始類型和引用類型,原始類型有五個:數組

Number(數值) String(字符串) Boolean(布爾) Null(空) Undefined(未定義)

引用類型有一個:網絡

Object(對象)

經過typeof(x)能夠返回一個變量x的數據類型「number」、「string」、「boolean」、「undefined」、"object",這裏要注意一點:typeof運算符對於null類型返回的是object數據結構

《JavaScript高級程序設計》:
這其實是JavaScript最初實現中的一個錯誤,後來被ECMAScript沿用了。如今null被認爲是對象的佔位符,從而解釋了這一矛盾。可是從技術上來講,它仍然是原始值。閉包

2 談一談JavaScript做用域鏈

當執行一段JavaScript代碼(全局代碼或函數)時,JavaScript引擎會建立爲其建立一個做用域又稱爲執行上下文(Execution Context),在頁面加載後會首先建立一個全局的做用域,而後每執行一個函數,會創建一個對應的做用域,從而造成了一條做用域鏈。每一個做用域都有一條對應的做用域鏈,鏈頭是全局做用域,鏈尾是當前函數做用域。

做用域鏈的做用是用於解析標識符,當函數被建立時(不是執行),會將this、arguments、命名參數和該函數中的全部局部變量添加到該當前做用域中,當JavaScript須要查找變量X的時候(這個過程稱爲變量解析),它首先會從做用域鏈中的鏈尾也就是當前做用域進行查找是否有X屬性,若是沒有找到就順着做用域鏈繼續查找,直到查找到鏈頭,也就是全局做用域鏈,仍未找到該變量的話,就認爲這段代碼的做用域鏈上不存在x變量,並拋出一個引用錯誤(ReferenceError)的異常。

3 如何理解JavaScript原型鏈

JavaScript中的每一個對象都有一個prototype屬性,咱們稱之爲原型,而原型的值也是一個對象,所以它也有本身的原型,這樣就串聯起來了一條原型鏈,原型鏈的鏈頭是object,它的prototype比較特殊,值爲null。

原型鏈的做用是用於對象繼承,函數A的原型屬性(prototype property)是一個對象,當這個函數被用做構造函數來建立實例時,該函數的原型屬性將被做爲原型賦值給全部對象實例,好比咱們新建一個數組,數組的方法便從數組的原型上繼承而來。

當訪問對象的一個屬性時, 首先查找對象自己, 找到則返回; 若未找到, 則繼續查找其原型對象的屬性(若是還找不到實際上還會沿着原型鏈向上查找, 直至到根). 只要沒有被覆蓋的話, 對象原型的屬性就能在全部的實例中找到,若整個原型鏈未找到則返回undefined;

4 JavaScript變量聲明提早

《JavaScript權威指南》中是這樣解釋的:JavaScript變量在聲明以前已經可用,JavaScript的這個特性被非正式的稱爲聲明提早(hoisting),即JavaScript函數中聲明的全部變量(但不涉及賦值)都被「提早」至函數的頂部。

從一個例子來看:

var scope = "global";
function myFunc(){
    console.log(scope); 
    var scope = "local";
}

控制檯打印出來的不是「global」而是「undefined」,這是由於在myFunc這個函數的做用域中,局部變量scope聲明被提早至函數頂部,而此時,scope僅聲明,未賦值,所以輸出undefined。實際上,上面的代碼和下面的效果是同樣的:

var scope = "global";
function myFunc(){
    var scope;
    console.log(scope);
    scope = "local";
}

5 如何理解和應用JavaScript閉包

關於閉包具體的定義文獻中給的概念很抽象,我認爲閉包是一種使函數可以都去其它函數的局部變量的語法機制。

舉個例子:

function outFunc(){
    var name = "Vicfeel";
    function inFunc(){
        console.log(name);
    }
    return inFunc;
}
inFunc(); //控制檯顯示"Vicfeel"

這這個例子咱們能夠看出,在函數inFunc中依然能夠訪問outFunc的局部變量name。

閉包應用舉例,模擬類的私有屬性,利用閉包的性質,局部變量只有在sayAge方法中才能夠訪問,而name在外部也訪問,從而實現了類的私有屬性。

function User(){
        this.name = "Vicfeel";  //共有屬性
        var age = 23;    //私有屬性
        this.sayAge:function(){
            console.log("my age is " + age); 
        }
    }
    var user = new User();
    console.log(user.name); //"Vicfeel"
    console.log(user.age);  //"undefined"
    user.sayAge();   //"my age is 23"

要了解詳細的閉包,推薦一下 阮一峯的網絡日誌-學習Javascript閉包(Closure)

6 new構建對象的本質

function User(){
        this.name = "Vicfeel";
        this.age = 23;
    }
    
    var user = new User();

經過new操做符,實際上在構造函數User中完成了以下操做:

  • 建立一個新的對象,這個對象的類型是object;
  • 設置這個新的對象的內部、可訪問性和prototype屬性爲構造函數(指prototype.construtor所指向的構造函數)中設置的;
  • 執行構造函數;
  • 返回新建立的對象。
function User(){
        //this = {};  
        //this.constructor = User;
        this.name = "Vicfeel";
        this.age = 23;
        //return this;
    }
    
    var user = new User();

若是構造函數默認返回的新建立的this對象,若是手動return 一個變量的話,若是該變量是原始類型則無效,若是是對象,則返回該對象。

7 JavaScript代理

當咱們須要對不少元素添加事件的時候,能夠經過將事件添加到它們的父節點而將事件委託給父節點來觸發處理函數。

好比咱們須要向一個ul中動態添加不少個li,須要遍歷li逐個添加點擊事件

<ul id='list'></ul>
var count = 100;
    var ulList = document.getElementById("list");
    //動態構建節點
    for(var i = count;i--;){
        var liDom = document.createElement('li');
        ulList.appendChild(liDom);
    }
    //綁定點擊事件
    var liNode = ulList.getElementByTagName("li");
    for(var i=0, l = liNodes.length; i < l; i++){
        liNode[i].onClick = function(){
            //li點擊事件
        }
    }

衆所周知,DOM操做是十分消耗性能的。因此重複的事件綁定簡直是性能殺手。而事件代理的核心思想,就是經過儘可能少的綁定,去監聽儘可能多的事件。如何作呢?答案是利用事件冒泡機制,對其父節點ul進行事件綁定(Event Bubble),而後經過event.target來判斷是哪一個節點觸發的事件,從而減小不少EventHandler的綁定。

var count = 100;
    var ulList = document.getElementById("list");
    //動態構建節點
    for(var i = count;i--;){
        var liDom = document.createElement('li');
        ulList.appendChild(liDom);
    }
    //綁定點擊事件
    var liNode = ulList.getElementByTagName("li");
    liNode.onClick = function(e){
        if(e.target && e.target.nodeName.toUpperCase == "LI") {
            // li點擊事件
        }
    }

發現新內容會持續更新...

博文做者:vicfeel
博文出處:http://www.cnblogs.com/vicfeel 本文版權歸做者和博客園共有,歡迎轉載,但須保留此段聲明,並給出原文連接,謝謝合做! 若是閱讀了本文章,以爲有幫助,您能夠爲個人博文點擊「推薦一下」!

相關文章
相關標籤/搜索