幾道前端面試題小記

風滿樓發表於2019-02-16

最近面試了不少家,苦於前端經驗薄弱,被各種血虐。做了不少家面試題,把各種不會的回來再做一遍,作為經驗總結吧。

1.如何最優效能去重一個陣列?

方法有好多,比如新建一個陣列,原陣列的內容依次往裡放,若該陣列元素已存在,則跳過;又或者先排序,依次比較前後兩個元素是否相等,若相等,則去掉刪除後一個元素。面試官有提到使用 filter 的方法,但是當場沒想到,發現這個方法其實屬於相當不錯的,這種函式式的思維在某些地方相當實用。

var arr = [3,5,2,6,2,3,5,8,6]

function distinct(arr) {
    return arr.filter(function (elem,index,arr){
        return arr.indexOf(elem,index+1) === -1;
    });
}

console.log(distinct(arr));

思路擴充套件
比如說存在一個陣列,其中元素為物件,根據物件某個屬性進行排序。例如將以下data陣列按age正序排列,常規的辦法可能是通過比較age大小,操作物件來進行排序,這樣程式碼會比較複雜。而更優的方法則是通過 sort 方法。

var data = [
{name:`xiaoming`,age:18},
{name:`xiaohua`,age:20},
{name:`xiaoli`,age:25},
{name:`xiaozheng`,age:16}];

查閱 MDN 關於 sort 方法,此方法明顯程式碼量更少,含義更加清晰。

function asc_order(data){
    return data.sort(function (a,b){
        return a.age- b.age;
    })
}

2.變數宣告和函式宣告提升

function fn(a){
    console.log(a);
    var a=2;
    function a(){}
    console.log(a);
}
fn(1);

以上程式碼輸出內容?
此前看書時有了解到變數宣告會提升到作用域頂部,但忘記了變數賦值保持在原處,同時變數宣告和函式宣告的先後關係不確定,所以此題對我來說比較難,只能瞎蒙答案。

function  fn(a){
    var a;
    function a(){}
    console.log(a);
    a = 2;
    console.log(a);
}

fn(1);
  1. 所有全域性變數都是window或Global的屬性

  2. 函式宣告會被提到範圍作用域的頂端

  3. 變數宣告被提到範圍作用域的頂端

  4. 變數宣告比函式宣告優先順序高,變數宣告優於函式宣告,如果兩者同時存在,後被提升的函式宣告會覆蓋被提升的變數宣告

  5. 變數賦值不會被提升,到執行行程式碼才開始賦值

參考部落格地址,根據以上五點共識,可將程式碼翻譯如上所示。

深入思考
為什麼JavaScript相比較其它語言會存在宣告提升?變數宣告時編譯器做了什麼?變數賦值時編譯器又做什麼了?

3.作用域和 this

var a = 10;
function test(){
    a = 100;
    console.log(a);
    console.log(this.a);
    var a;
    console.log(a);
}
test();
var a = 100;function test(){
    console.log(a);
    var a = 10;
    console.log(a);
}
test();
var a = 100;function test(){
    console.log(a);
    a = 10;
    console.log(a);
}

test();
console.log(a);

在非嚴格環境下,以上三個程式碼分別輸出什麼?碰到這種題目我也是頭暈眼花,稍有不慎就掉坑了。當然實際業務中不會出現這樣的程式碼,但還是相當有必要以這樣的程式碼來檢查對 JavaScript 理解的程度。
this 的用法參照阮一峰老師的部落格,主要分為三種情況,但總的原則是指向呼叫函式的那個物件。

  • 全域性環境:呼叫函式的物件實際為 window ,所以函式內的 this 指向 window ;

  • 建構函式:通過構造函造函式生成了一個新物件,this 指向這個新物件。

  • 物件的方法:函式作為物件的某個方法呼叫, this 就指向這個上級物件。

故第一道題中屬於全域性環境, this 指向 window ,所以輸出結果為:100,10,100;
第二道題輸出結果為:undefined,10;第三道題輸出結果為:100,10,10;

4.setTimeout 深入分析其機制

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

此題輸出內容是什麼?

setTimeoutWindow 物件方法,用來註冊在指定的事件之後單次或重複呼叫的函式。

setTimeout的作用是將程式碼推遲到指定時間執行,如果指定時間為0,即setTimeout(f,0),那麼會立刻執行嗎?

答案是不會。因為上一段說過,必須要等到當前指令碼的同步任務和“任務佇列”中已有的事件,全部處理完以後,才會執行setTimeout指定的任務。也就是說,setTimeout的真正作用是,在“訊息佇列”的現有訊息的後面再新增一個訊息,規定在指定時間執行某段程式碼。setTimeout新增的事件,會在下一次EventLoop執行。

setTimeout(f,0)將第二個引數設為0,作用是讓f在現有的任務(指令碼的同步任務和“訊息佇列”指定的任務)一結束就立刻執行。也就是說,setTimeout(f,0)的作用是,儘可能早地執行指定的任務。而並不是會立刻就執行這個任務。

所以最終的結果是當前的函式執行結束之後,再去執行 setTimeout 定義的內容。

5.class屬性覆蓋問題

<style>
    .B {color:  red}
    .A {color: blue}

</style>

<body>
<p class="A B">XXXXXXXXXX</p>
</body>

最後實際的問題是什麼顏色?
存在多個類名時,類名的位置不會對屬性的渲染產生影響。只有在style中定義的位置才會有影響,同一條屬性,後面定義的會覆蓋前面定義的屬性。

6.實現類似 jquery 的 one 方法

即對一個元素繫結一個事件,操作一次後繫結事件失效。

HTML部分:

<body>
<p id="target">XXXXXXXXXX</p>
</body>

JS部分:

window.onload = function(){
    var target = document.getElementById(`target`);
    function fn(e){
        alert(`hello`);
        target.removeEventListener(`click`,fn);
    };
    target.addEventListener(`click`,fn);
}

此程式碼雖可行,但沒有進行封裝,不便於使用。

正統封裝後的JS程式碼,使用 arguments.callee 表示當前 function ,同時需對 event 上的屬性有所瞭解。

function oneTime(node,type,callback){
    node.addEventListener(type,function (e){
        e.target.removeEventListener(e.type,arguments.callee);
        return callback(e);
    });
}
function handle(e){
    alert(`hello!`);
}
oneTime(p,`click`,handle);

學習前端一個月,上一週面試了大概10多家,收穫的 offer 卻是寥寥。
為了效率,前端各方面的內容都有涉獵,深度卻相當不足,面試時暴露各種問題。
還是需深入思考,欲速則不達啊!

大概是要加入大魚或者小悟,以後工作好好努力吧!

相關文章