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

NO IMAGE

JavaScript筆試部分

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

實現防抖函數 (debounce)


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

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

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

手寫簡化版

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

適用場景 :

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

生存環境請用lodash.debounce

實現節流函數 (throttle)


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

//手寫簡化版

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

適用場景:

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

深克隆 (deepclone)


簡單版 :

const newObj = JSON.parse(JSON.stringify(oldObj));

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

2、會拋棄對象的constructor,所有的構造函數會指向Object

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

實現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(); 
})
}

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

相關文章

[譯]Android原生開發的現狀,截止到2019年12月

Flutter會不會被蘋果限制其發展?

2019與子弈的前端之路(乾貨滿滿)|年度徵文

如何全面出色的回答面試官防抖與節流提問?