前端面試題總結(jié)(專題二)——手寫實(shí)現(xiàn)功能

最近在前端面試,面經(jīng)是自己的和周圍同學(xué)的面經(jīng)整理出來(lái)的,持續(xù)更新有用的同學(xué)加關(guān)注嗷

手寫一個(gè)js的深克?。缊F(tuán)、愛(ài)奇藝)

function deepCopy(obj){
    //判斷是否是簡(jiǎn)單數(shù)據(jù)類型,
    if(typeof obj == "object"){
        //復(fù)雜數(shù)據(jù)類型
        var result = obj.constructor == Array ? [] : {};
        for(let i in obj){
            result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
        }
    }else {
        //簡(jiǎn)單數(shù)據(jù)類型 直接 == 賦值
        var result = obj;
    }
    return result;
}

手寫組合繼承(美團(tuán)、愛(ài)奇藝、搜狗)

// 定義一個(gè)動(dòng)物類
function Animal (name) {
  // 屬性
  this.name = name || 'Animal';
  // 實(shí)例方法
  this.sleep = function(){
    console.log(this.name + '正在睡覺(jué)!');
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
  console.log(this.name + '正在吃:' + food);
};
//組合繼承
function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();

手寫一個(gè)promise(愛(ài)奇藝、搜狐)

promise是一個(gè)構(gòu)造函數(shù),下面是一個(gè)簡(jiǎn)單實(shí)例
var promise = new Promise((resolve,reject) => {
    if (操作成功) {
        resolve(value)
    } else {
        reject(error)
    }
})
promise.then(function (value) {
    // success
},function (value) {
    // failure
})

防抖和節(jié)流

scroll 事件本身會(huì)觸發(fā)頁(yè)面的重新渲染,同時(shí) scroll 事件的 handler 又會(huì)被高頻度的觸發(fā), 因此事件的 handler 內(nèi)部不應(yīng)該有復(fù)雜操作,例如 DOM 操作就不應(yīng)該放在事件處理中。

針對(duì)此類高頻度觸發(fā)事件問(wèn)題(例如頁(yè)面 scroll ,屏幕 resize,監(jiān)聽用戶輸入等),有兩種常用的解決方法,防抖和節(jié)流。

防抖(Debouncing)

防抖技術(shù)即是可以把多個(gè)順序地調(diào)用合并成一次,也就是在一定時(shí)間內(nèi),規(guī)定事件被觸發(fā)的次數(shù)。

通俗一點(diǎn)來(lái)說(shuō),先看下面這個(gè)簡(jiǎn)化的例子,這個(gè)簡(jiǎn)單的防抖的例子大概功能就是如果 500ms 內(nèi)沒(méi)有連續(xù)觸發(fā)兩次 scroll 事件,那么才會(huì)觸發(fā)我們真正想在 scroll 事件中觸發(fā)的函數(shù)。

// 簡(jiǎn)單的防抖動(dòng)函數(shù)
function debounce(func, wait, immediate) {
    // 定時(shí)器變量
    var timeout;
    return function() {
        // 每次觸發(fā) scroll handler 時(shí)先清除定時(shí)器
        clearTimeout(timeout);
        // 指定 xx ms 后觸發(fā)真正想進(jìn)行的操作 handler
        timeout = setTimeout(func, wait);
    };
};
 
// 實(shí)際想綁定在 scroll 事件上的 handler
function realFunc(){
    console.log("Success");
}
 
// 采用了防抖動(dòng)
window.addEventListener('scroll',debounce(realFunc,500));
// 沒(méi)采用防抖動(dòng)
window.addEventListener('scroll',realFunc);

完整的防抖動(dòng)函數(shù):

// 防抖動(dòng)函數(shù)
function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};
 
var myEfficientFn = debounce(function() {
    // 滾動(dòng)中的真正的操作
}, 250);
 
// 綁定監(jiān)聽
window.addEventListener('resize', myEfficientFn);

節(jié)流(Throttling)

防抖函數(shù)確實(shí)不錯(cuò),但是也存在問(wèn)題,譬如圖片的懶加載,我希望在下滑過(guò)程中圖片不斷的被加載出來(lái),而不是只有當(dāng)我停止下滑時(shí)候,圖片才被加載出來(lái)。又或者下滑時(shí)候的數(shù)據(jù)的 ajax 請(qǐng)求加載也是同理。

這個(gè)時(shí)候,我們希望即使頁(yè)面在不斷被滾動(dòng),但是滾動(dòng) handler 也可以以一定的頻率被觸發(fā)(譬如 250ms 觸發(fā)一次),這類場(chǎng)景,就要用到另一種技巧,稱為節(jié)流函數(shù)(throttling)。

節(jié)流函數(shù),只允許一個(gè)函數(shù)在 X 毫秒內(nèi)執(zhí)行一次。與防抖相比,節(jié)流函數(shù)最主要的不同在于它保證在 X 毫秒內(nèi)至少執(zhí)行一次我們希望觸發(fā)的事件 handler。
與防抖相比,節(jié)流函數(shù)多了一個(gè) mustRun 屬性,代表 mustRun 毫秒內(nèi),必然會(huì)觸發(fā)一次 handler。

同樣是利用定時(shí)器,看看下面的簡(jiǎn)單示例,大概功能就是如果在一段時(shí)間內(nèi) scroll 觸發(fā)的間隔一直短于 500ms ,那么能保證事件我們希望調(diào)用的 handler 至少在 1000ms 內(nèi)會(huì)觸發(fā)一次。

// 簡(jiǎn)單的節(jié)流函數(shù)
function throttle(func, wait, mustRun) {
    var timeout,
        startTime = new Date();
 
    return function() {
        var context = this,
            args = arguments,
            curTime = new Date();
 
        clearTimeout(timeout);
        // 如果達(dá)到了規(guī)定的觸發(fā)時(shí)間間隔,觸發(fā) handler
        if(curTime - startTime >= mustRun){
            func.apply(context,args);
            startTime = curTime;
        // 沒(méi)達(dá)到觸發(fā)間隔,重新設(shè)定定時(shí)器
        }else{
            timeout = setTimeout(func, wait);
        }
    };
};
// 實(shí)際想綁定在 scroll 事件上的 handler
function realFunc(){
    console.log("Success");
}
// 采用了節(jié)流函數(shù)
window.addEventListener('scroll',throttle(realFunc,500,1000));

手寫原生js實(shí)現(xiàn)事件代理,并要求兼容瀏覽器(騰訊)

/ ============ 簡(jiǎn)單的事件委托
function delegateEvent(interfaceEle, selector, type, fn) {
    if(interfaceEle.addEventListener){
    interfaceEle.addEventListener(type, eventfn);
    }else{
    interfaceEle.attachEvent("on"+type, eventfn);
    }
      
    function eventfn(e){
    var e = e || window.event;   
    var target = e.target || e.srcElement;
    if (matchSelector(target, selector)) {
            if(fn) {
                fn.call(target, e);
            }
        }
    }
}

function matchSelector(ele, selector) {
    // if use id
    if (selector.charAt(0) === "#") {
        return ele.id === selector.slice(1);
    }
    // if use class
    if (selector.charAt(0) === ".") {
        return (" " + ele.className + " ").indexOf(" " + selector.slice(1) + " ") != -1;
    }
    // if use tagName
    return ele.tagName.toLowerCase() === selector.toLowerCase();
}
//調(diào)用
var odiv = document.getElementById("oDiv");
delegateEvent(odiv,"a","click",function(){
    alert("1");
})

手寫Function.bind函數(shù)(騰訊、愛(ài)奇藝)

if(!Function.prototype.bind){
    Function.prototype.bind = function(oThis){
        if(typeof this !=="function"){ //如果不函數(shù)拋出異常
            throw new TyperError("")
        }
        var aArgs = Array.prototype.slice.call(arguments,1),  //此處的aArgs是除函數(shù)外的參數(shù)
            fToBind = this,//要綁定的對(duì)象
            fNOP = function(){},
            fBound = function(){
                return fToBind.apply(
                    this instanceof fNOP ? this:oThis||this,aArgs.concat(Array.prototype.slice.call(arguments)));
                    )
            };
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return  fBound;
    }
}

手寫AJAX(騰訊)

創(chuàng)建XMLHttpRequest對(duì)象

指定響應(yīng)函數(shù)

打開連接(指定請(qǐng)求)

發(fā)送請(qǐng)求

創(chuàng)建響應(yīng)函數(shù)

var xmlhttp=null;//聲明一個(gè)變量,用來(lái)實(shí)例化XMLHttpRequest對(duì)象
if (window.XMLHttpRequest)
  {
  xmlhttp=new XMLHttpRequest();// 新版本的瀏覽器可以直接創(chuàng)建XMLHttpRequest對(duì)象
  }
  
else if (window.ActiveXObject)
  {
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");// IE5或IE6沒(méi)有XMLHttpRequest對(duì)象,而是用的ActiveXObject對(duì)象
  }
  
  
if (xmlhttp!=null)
  {
  xmlhttp.onreadystatechange=state_Change;//指定響應(yīng)函數(shù)為state_Change
  xmlhttp.open("GET","/example/xdom/note.xml",true);//指定請(qǐng)求,這里要訪問(wèn)在/example/xdom路徑下的note.xml文件,true代表的使用的是異步請(qǐng)求
  xmlhttp.send(null);//發(fā)送請(qǐng)求
  } 
else
  {
  alert("Your browser does not support XMLHTTP.");
  }

//創(chuàng)建具體的響應(yīng)函數(shù)state_Change
function state_Change()
{
if (xmlhttp.readyState==4)
  {
  if (xmlhttp.status==200)
    {
    // 這里應(yīng)該是函數(shù)具體的邏輯
    }
  else
    {
    alert("Problem retrieving XML data");
    }
  }
}

手寫XMLHttpRequest

var xhr = new XMLHttpRequest();
xhr.open("GET","/api",false);
xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
        if(xhr.status == 200){
            alert(xhr.responseText);
        }
    }
}
xhr.send(null);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容