前端面試手寫篇

手寫篇

1. 手寫 instenceof

原生的instanceof

console.log([] instanceof Array) // true

console.log('' instanceof Array) // false

手寫myInstanceof

function myInstanceof(left,right){
    
    let proto = left.__proto__
    
    let prototype = right.prototype
    
    while(true){
        
        if(proto === null)return false
        
        if(proto === prototype)return true
        
        proto = proto.__proto__
        
    }
}

console.log(myInstanceof([],Array))// true

console.log(myInstanceof('',Array))// false

實現原理:

通過不斷的沿着原型鏈查找,如果找到頂端了即:proto === null,那麼就說明沒有找到,返回false,說明 left 不是 right 構造函數的實例

如果找到隱式原型 proto等於構造函數的原型prototype,那麼說明 leftright 構造函數的實例,返回true

其它情況就是不斷的改變proto,以便可以不斷的往上查找

2. 手寫 flat

原生示例:

const arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

const arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

手寫flatDeep:

function flatDeep( arr, dep=1 ){
    let ret = []
    
    for(let i=0;i<arr.length;i++){
        
        if(Array.isArray(arr[i])){
            
            dep>0 ? (ret = ret.concat(flatter(arr[i],dep-1))):(ret.push(arr[i]))
            
        }else{
            
            ret.push(arr[i]) 
        }
    }
    
    return ret
}

實現原理:

第一個參數是數組,第二個是降維層級,

用for循環遍歷這個數組,檢測每一項

如果這項是不是數組則直接添加到ret結果數組裡面

否則根據降維層級判斷,默認是降一維層級,當遞歸降維不滿足ret>0,說明已經達到dep降維層數了,其它情況即ret.push(arr[i])

3. 手寫 call

Function.prototype.myCall = function(context){

    context =(context === null || context === undefined) ? window : context
    
    context.fn = this// 其實就等價於 obj.fn = function say(){} 當指向 context.fn 時,say裏面的this 指向obj [關鍵]
    //obj 此時變成 var obj = {name:'innerName',fn:function say(){console.log(this.name)}}

    let args = [...arguments].slice(1) //截取第二個開始的所有參數
    let result= context.fn(...args)//把執行的結果賦予result變量

    delete context.fn //刪除執行上下文上的屬性 (還原)由var obj = {name:'innerName',fn:function say(){console.log(this.name)}}刪除fn
    return result
}
var name = 'outerName'
var obj = {
    name:'innerName'
}
function say(){
    console.log(this.name)
}
say()//outerName 等價於 window.say this指向window
say.myCall(obj)//innerName

實現原理:

函數的原型方法call 第一個參數是傳入的執行上下文,後面傳入的都是參數,以逗號隔開

當傳入的是null或undefined是執行上下文是指向window,否使為傳入的對象,然後再傳入的對象身上添加fn屬性並把函數實例say函數賦值給fn,此時變成

var obj = {name:'innerName',fn:function say(){console.log(this.name)}}此時context就是obj對象啦,所有你執行context.fn(...args)

其實就是obj.fn(...args)fn 其值是 function say(){ console.log(this.name) },所以這個this就變成obj對象了

然後就是結果賦值,對象還原

返回結果

4. 手寫 apply

Function.prototype.myApply = function(context){
    
    context =(context === null || context === undefined) ? window : context
    
    let result
    
    context.fn = this
    
    result = arguments[1] ? context.fn(...arguments[1]) : context.fn()
    
    delete context.fn
    
    return result
}

myCall實現原理大致相同,不同的是由於callapply的傳參方式不一樣,

我們需要額外的對第二個參數做判斷,apply受參形式是數組,且再第二個參數位置,

一:如果第二個參數存在,執行的時候就把第二個參數(數組形式)用擴展運算符打散後傳入執行

二:如果第二個參數不存在,執行執行

其它就於call的實現一樣

5. 手寫 bind

Function.prototype.myBind = function(context){
    
    context =(context === null || context === undefined) ? window : context
    
    let o = Object.create(context)
    
    o.fn = this
    
    let args = [...arguments].slice(1)
    
    let fn= function(){
        
        o.fn(...args)
    }
    
    return fn
}

bind 的手寫實現,與其它兩個區別是返回一個函數,並沒返回函數執行的結果,並且受參形式不受限制

實現原理:

通過 Object.create方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__,通過 中介對象o來實現,來達到不影響傳入的對象

6. 手寫 new

new 一個函數的時候,會生成一個實例,該實例的隱式原型__proto__ ===該函數的prototype原型對象

在構造函數中this指向當前實例

最後再將實例對象返回

function myNew(func){
    
    //第一步 將函數的 prototype 指向 o 對象的__proto__
    let o = Object.create(func.prototype)
    
    //第二步 通過call改變 this的指向,使之指向 o
    let ret = func.call(o)
    
    //第三步 如果構造函數裏面有返回對象,則返回這個對象,沒有則返回 o 對象
    return typeof ret === 'object' ? ret : o

}

檢測:

function M(){}

let m = myNew(M); // 等價於 new M 這裏只是模擬
console.log(m instanceof M); // instanceof 檢測實例
console.log(m instanceof Object);
console.log(m.__proto__.constructor === M);

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!