2020年了,12道高頻 JavaScript 手寫面試題及答案

JavaScript筆試部分

本文分享 12 道高頻JavaScript的面試題,包含手寫以及經常使用的正則。前端

實現防抖函數 (debounce)


防抖函數原理 : 在事件被觸發n秒後在執行回調,若是在這n秒內又被觸發,則從新計時。node

那麼與節流函數的區別直接看這個動畫實現便可。 面試

手寫簡化版設計模式

//防抖函數
const debounce = (fn,delay)=>{
    let timer = null;
    return (...args)=>{
        clearTimeout(timer);
        timer = setTimeout(()=>{
        fn.apply(this,args)
        },delay);
    };
};
    
複製代碼

適用場景 :數組

  • 按鈕提交場景: 防止屢次提交按鈕,只執行最後提交的一次
  • 服務端驗證場景 : 表單驗證須要服務端配合,只執行一段連續的輸入事件的最後一次,還有搜索聯想詞功能相似

生存環境請用lodash.debounce瀏覽器

實現節流函數 (throttle)


防抖函數原理:規定在一單位時間內。只能觸發一次函數。若是這個單位時間內觸發屢次函數,只有一次生效。bash

//手寫簡化版markdown

//節流函數
const throttle = (fn,delay = 500) =>{
    let flag = true;
    return (...args) =>{
        if (!flag) return;
        flag = false;
        setTimeout(() => {
        fn.apply(this,args)
        },delay);
    };
};
複製代碼

適用場景:app

  • 拖拽場景: 固定時間內只執行一次,防止超高頻次觸發位置變更
  • 縮放場景: 監控瀏覽器resize
  • 動畫場景: 避免短期內屢次觸發動畫引發性能問題

深克隆 (deepclone)


簡單版 :dom

const newObj = JSON.parse(JSON.stringify(oldObj));
複製代碼

侷限性 : 一、他沒法實現函數、RegExp等特殊對象的克隆

二、會拋棄對象的constructor,全部的構造函數會指向Object

三、對象有循環引用,會報錯

實現Event (event bus)


event bus既是node中各個模塊的基石,又是前端組件通訊的依賴手段之一,同時涉及了訂閱-發佈設計模式,是很是重要的基礎。

簡單版:

class EventEmeitter {
    constructor(){
        this._events = this._events || new Map(); //儲存事件/回調鍵值對
        this._maxListeners = this._maxListeners || 1o;//設立監聽上限
    }
}

//觸發名爲type的事件
EventEmeitter.prototype.emit = function(type,...args){
    let hander;
    //從儲存事件鍵值對的this._events中獲取對應事件回調函數
    handler = this._events.get(type);
    if (args.length > 0) {
        hander.apply(this,args);
    }else{
        handler.call(this);
    }
    return true;
};

//監聽名爲type事件
EventEmeitter.prototype.addListener = function(type,fn) {
    //將type事件以及對應的fn函數放入this._events中儲存
    if (!this._events.get(type)) {
        this._events.set(type,fn);
    }
};
複製代碼

實現instanceOf


//模擬 instanceof
function instance_of(L,R){
    var O = R.prototype;//取 R 的顯示原型
    L = L.__proto__;//取 L 的隱式原型
    while (true) {
        if (L === null) return false;
        if (O === L)
        // 這裏重點 : 當 O 嚴格等於 L 時,返回 true
        return true;
        L = L.__proto__;
    }
}
複製代碼

模擬new


new操做符作了這些事:

  • 他建立了一個全新的對象
  • 他會被執行[[Prototype]] (也就是__proto__) 連接
  • 它使this指向新建立的對象
  • 經過new建立的每一個對象將最終被[[Prototype]]連接到這個函數的prototype對象上
  • 若是函數沒有返回對象類型Object(包含Function,Array,Date,RegExg,Error),那麼new表達式中的函數調用將返回對象引用
// objectFactory(name,'cxk','18')
function objectFactory(){
    const obj = new object();
    const Constructor = [].shift.call(arguments);
    
    obj.__proto__ = Constructor.prototype;
    
    const ret = Constructor.apply(obj,arguments);
    
    return typeof ret === "object" ? ret : obj;
}
複製代碼

實現一個call


call作了什麼 :

  • 將函數設爲對象的屬性
  • 執行&刪除這個函數
  • 指定this到函數並傳人給定參數執行函數
  • 若是不傳人蔘數,默認指向爲 window
//模擬 call bar.mycall(null);
//實現一個call方法;
Function.prototype.myCall = function(context){
    //此處沒有考慮context非object狀況
    context.fn = this;
    let args = [];
    for (let i = 1,len = arguments.length,i < len; i++){
        args.push(arguments[i]);
    }
    context.fn(...args);
    let result = context.fn(...args);
    delete context.fn;
    return result;
};
複製代碼

實現apply方法


apply原理與call很類似,很少獒數

//模擬 apply
Function.prototype.myapply = function(context,arr){
    var context = Object(context) || window;
    context.fn = this;
    
    var result;
    if (!arr){
        result = context.fn();
    }else{
        var args = [];
        for (var i = 0,len = arr.length;i < len; i++){
            args.push("arr["+ i +"]");
        }
        result = eval("context.fn("+ args + ")");
    }
    delete context.fn;
    return result;
}
複製代碼

實現bind


實現bind要作什麼

  • 返回一個函數,綁定this,傳遞預置參數
  • bind返回的函數能夠做爲構造函數使用。故做爲構造函數時應使得this失效,可是傳人的參數依然有效
// mdn的實現
if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          // this instanceof fBound === true時,說明返回的fBound被當作new的構造函數調用
          return fToBind.apply(this instanceof fBound
                 ? this
                 : oThis,
                 // 獲取調用時(fBound)的傳參.bind 返回的函數入參每每是這麼傳遞的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 維護原型關係
    if (this.prototype) {
    }
    // 下行的代碼使fBound.prototype是fNOP的實例,所以
    // 返回的fBound若做爲new的構造函數,new生成的新對象做爲this傳入fBound,新對象的__proto__就是fNOP的實例
    fBound.prototype = new fNOP();

    return fBound;
  };
}
複製代碼
詳解請移步JavaScript深刻之bind的模擬實現 #12

模擬Object.create
Object.create()方法建立一個新對象,使用現有的對象來提供新建立的對象的__proto__。
// 模擬 Object.create

function create(proto) {
  function F() {}
  F.prototype = proto;

  return new F();
}
複製代碼

模擬Object.create


Object.create() 方法建立一個新對象,使用現有的對象來提供新建立的對象的__proto__。

// 模擬 object.create

function create(proto){
    function F(){
        F.prototype = proto;
        
        return new F();
    }
}
複製代碼

解析 URL Params爲對象


let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)

/* 結果
{ user: 'anonymous',
  id: [ 123, 456 ], // 重複出現的 key 要組裝成數組,能被轉成數字的就轉成數字類型
  city: '北京', // 中文需解碼
  enabled: true, // 未指定值得 key 約定爲 true
}
*/
複製代碼

轉化爲駝峯命名

var s1 = "get-element-by-id"

//轉化爲 getElementById
複製代碼
var f = function(s){
    return s.replace(/-\w/g,function(x){
      return x.slice(1).toUpperCase(); 
    })
}
複製代碼

本文主要是一些基礎知識,但願能幫助那些基礎不太好的同行們。加油~~~~~~

相關文章
相關標籤/搜索