荔枝FM的前端筆試題

Wongzw發表於2019-03-27

前言:正所謂金三銀四,我也在三月份面了一次荔枝FM,初試是做一套比較簡單的筆試題,發出來跟大家簡單探討一下,面試時的一些問題主要是通過這些筆試題進行擴充,例如flex佈局、改變正則條件,promise事件機制等等,會在下一篇文章中提到,這篇文章僅為初面的筆試題

1、變數提升和函式提升

function outter () {
    return inner;
    function inner () {}
    var inner;
    inner = 9;
}
//問題是下面程式碼執行輸出值是什麼:
typeof outter();
複製程式碼

答案:這道題不單只是考變數提升,因為這裡還有函式提升,那就是還要考函式提升和變數提升究竟誰優先順序更高了。

根據 《你不知道的JavaScript(上卷)》 裡的說法,js引擎在解釋程式碼之前會先進行編譯,編譯階段會將所有的變數宣告和函式宣告與相關作用域關聯起來,並且將這些宣告提升到作用域頂部,然後到程式碼執行階段才會進行變數賦值。

雖然第一行程式碼就是return inner, 但是根據上面的結論,var inner的變數宣告和function inner() {}的函式宣告會第一時間被提升到頂部。

那麼剩下的問題就是究竟函式優先還是變數優先,答案自然也在書中找得到,那就是函式優先,函式優先提升後,由於inner已經被宣告為函式變數,後面var inner編譯器進行LHS,也就是左查詢,發現同一作用域中已經有該值會忽略該變數宣告,因此這題的答案是字串'function';

 

2、用html和css實現一個寬度為螢幕45%的正方形,可使用最新標準

<html>
    <div class="box"></div>
</html>
<style>
    .box {
        width: 45vw;
        height: 45vw;
    }
</style>
複製程式碼

我這是使用最新標準也最簡單的實現方式了,大家可以在評論寫一寫自己的實現方式。

 

3、寫出6個div元素的堆疊順序,最上面的在第一個位置,例如: .one .two .three .four .five .six(z-index)

html:

<div class="one">
	<div class="two"></div>
    <div class="three"></div>
</div>
<div class="four">
    <div class="five"></div>
    <div class="six"></div>
</div>
複製程式碼

css:

.one {
    position: relative;
    z-index: 2;
    .two {
        z-index: 6;
    }
    .three {
        position: absolute;
        z-index: 5;
    }
}
.four {
    position: absolute;
    z-index: 1;
    .five {}
    .six {
        position: absolute;
        top: 0;
        left: 0;
        z-index: -1;
    }
}
複製程式碼

PS:其實這道題程式碼寫的不大好,因為沒有div寬度和高度,甚至.one和.four也不重疊,就算自己把程式碼跑起來也是無法看出是否重疊的,因此需要自己去加點東西,不過主要知道考點是什麼就好了。

解析如下:從w3c的文件可以知道,z-index屬性設定一個定位元素沿 z 軸的位置,z 軸定義為垂直延伸到顯示區的軸。如果為正數,則離使用者更近,為負數則表示離使用者更遠。

  1. 沒有定位的元素z-index是不生效的
  2. 定位元素擁有更高堆疊順序的元素總是會處於堆疊順序較低的定位元素的前面
  3. 子元素必然在父元素的前面,不管是否是定位元素
  4. 同級定位元素的z-index相同時遵循“後來居上”,後面的定位元素堆疊順序更前
  5. z-index小於0的定位元素處於所有元素的後面,僅在層疊上下文的背景顏色和邊框前面

通過上面幾點可以得出答案是 .three .two .one .five .six .four,但是這只是元素層疊順序的冰山一角,更深入的瞭解可以讀一下張旭鑫大神的文章。

還有疑問的可以直接通過程式碼看效果:元素z-index的堆疊順序

擴充閱讀:深入理解CSS中的層疊上下文和層疊順序

 

4、寫出能匹配以下兩個要求的正規表示式

(1)字串長度為8-12

(2)字串只包含英文和數字

這道題算是送分題,只要對正則有所瞭解就能答出,而且答案不止一個,大家可以寫寫看自己的答案

/(\d|[A-z]{8,12})/
複製程式碼

 

5、四個請求介面分別是/a、/b、/c、/d,算出最快請求回來的那個介面的耗時時長(可用setTimeout模擬非同步請求)

function a () {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('/a');
        }, 1000);
    });
 }
function b () {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('/b');
        }, 2000);
    });
}
function c () {
   	return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('/c');
        }, 500);
    });
}
function d () {
 	return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('/d');
        }, 3000);
	});
}
// Promise.race競賽方法在第一個請求resolve後就立馬進入then,可以算出最快介面的時間
const startTime = new Date / 1;
Promise.race([a(), b(), c(), d()]).then(o => {
    const endTime = new Date / 1;
    console.log(`介面${o}最快完成請求,請求時長為${endTime - startTime}`);
});
複製程式碼

 

6、實現一個模態框dialog

要求:建構函式不能直接執行,必須使用new,每個例項不互相影響引數列表如下

引數名 型別 預設值
title String XXXX(不記得了)
body String ''
cancelText String 取消
okText String 確認

具有show和close方法,可以通過on繫結相關事件,off解綁相關事件。

需要達成效果如下:

let dialog = new Dialog();
dialog.on('show', () => {
   console.log('Dialog Show'); 
});
dialog.on('close', () => {
   console.log('Dialog Close'); 
});

dialog.show(); // 輸出Dialog Show;
setTimeout(() => {
    dialog.close(); // 輸出Dialog Close;
}, 3000);
複製程式碼

答案:這道題看起來也是少了很多細節, 不過這是一種常用的彈窗元件,因此我們可以自己給它補上應有的功能。

class Dialog {
  // show方法的回撥函式陣列
  showCallbacks = [];

  // close方法的回撥函式陣列
  closeCallbacks = [];

  // 建構函式初始化引數
  constructor(options) {
    this.options = Object.assign(
      {
        title: "title",
        body: "是否確認是一個dialog",
        cancelText: "取消",
        okText: "確認"
      },
      options
    );
    document.body.appendChild(this.renderTemplate());
    this.initEvent();
  }

  // 渲染彈窗模板方法
  renderTemplate() {
    const { title, body, cancelText, okText } = this.options;
    this.container = document.createElement("div");
    this.container.classList = "dialog-container";
    this.container.innerHTML = `<div class="dialog-title">${title}</div>
	<div class="dialog-body">${body}</div>
	<div class="dialog-footer">
	<div class="dialog-confirm">${okText}</div>
	<div class="dialog-cancel">${cancelText}</div>
    </div>`;
    return this.container;
  }
    
  // 初始化事件
  initEvent() {
    this.container
      .querySelector(".dialog-confirm")
      .addEventListener("click", () => {
        this.close();
      });
    this.container
      .querySelector(".dialog-cancel")
      .addEventListener("click", () => {
        this.close();
      });
  }

  // 事件繫結
  on(type, callback) {
    if (!callback || typeof callback !== "function")
      throw new Error("callback必須是函式");
    this[`${type}Callbacks`].push(callback);
  }

  // 事件解綁
  off(type, callback) {
    if (callback && typeof callback === "function") {
      // 將函式toString後在回撥陣列中查詢對比。
      callback = callback.toString();
      this[`${type}Callbacks`].forEach((v, i) => {
        // 同樣的回撥函式將一次性全部解綁
        if (v.toString === callback) {
          // 刪除事件回撥陣列中的值
          this[`${type}Callbacks`].splice(i, ++i);
        }
      });
      return;
    }

    // 如果不傳callback,清空整個回撥函式陣列
    this[`${type}Callbacks`] = [];
  }

  // show方法,執行show回撥函式陣列
  show() {
    this.container.style.display = "block";
    this.showCallbacks.forEach(callback => {
      callback();
    });
  }

  // close方法,執行close回撥函式陣列
  close() {
    this.container.style.display = "none";
    this.closeCallbacks.forEach(callback => {
      callback();
    });
  }
}

複製程式碼

示例程式碼:實現一個Dialog彈窗

最後這道題其實有兩道題,選擇其中之一作答,不過時間有限,我沒做另一道題所以沒記住問題是什麼,順便吐槽一下那個答題頁面打程式碼時真的很不順手,嚴重影響答題效率。

 

上面的答案可能並不一定對或者並不是最佳答案,畢竟個人答案或有缺漏,希望有更好答案的朋友可以在評論寫一下,謝謝大家的閱讀。

相關文章