面試常談之手寫系列

面試常談之手寫系列

new

function myNew(){
    //建立一個空對象
    let obj = new Object();
    //獲取構造函數
    let Constructor = [].shift.call(arguments);
    //連接到原型
    obj.__proto__ = Constructor.prototype;
    //綁定this值
    let result = Constructor.apply(obj,arguments);//使用apply,將構造函數中的this指向新對象,這樣新對象就能夠訪問構造函數中的屬性和方法
    //返回新對象
    return typeof result === "object" ? result : obj;//若是返回值是一個對象就返回該對象,不然返回構造函數的一個實例對象
  }

能夠歸納爲如下四步:
1.建立一個空對象
2.連接到原型
3.綁定this值
4.返回新對象面試

call

這裏提供兩種寫法segmentfault

Function.prototype.myCall = function(obj, ...arg){
    //咱們要讓傳入的obj成爲, 函數調用時的this值.
    obj._fn_ = this;  //在obj上添加_fn_屬性,值是this(要調用此方法的那個函數對象)。
    obj._fn_(...arg);       //在obj上調用函數,那函數的this值就是obj.
    delete obj._fn_; // 再刪除obj的_fn_屬性,去除影響.
    //_fn_ 只是個屬性名 你能夠隨意起名,可是要注意可能會覆蓋obj上原本就有的屬性
}
Function.prototype.myCall = function(obj){
    if(obj === null || obj === undefined){
        obj = window;
    } else {
        obj = Object(obj);
    }
    let arg = [];
    for(let i = 1 ; i<arguments.length ; i++){
        arg.push( 'arguments[' + i + ']' ) ;
        // 這裏要push 這行字符串  而不是直接push 值
        // 由於直接push值會致使一些問題
        // 例如: push一個數組 [1,2,3]
        // 在下面👇 eval調用時,進行字符串拼接,JS爲了將數組轉換爲字符串 ,
        // 會去調用數組的toString()方法,變爲 '1,2,3' 就不是一個數組了,至關因而3個參數.
        // 而push這行字符串,eval方法,運行代碼會自動去arguments裏獲取值
    }
    obj._fn_ = this;
    eval( 'obj._fn_(' + arg + ')' ) // 字符串拼接,JS會調用arg數組的toString()方法,這樣就傳入了全部參數
    delete obj._fn_;
}

apply

apply的寫法跟call接近數組

Function.prototype.myApply = function(obj,arr){
    if(obj === null || obj === undefined){
        obj = window;
    } else {
        obj = Object(obj);
    }
    let args = [];
    let val ;
    for(let i = 0 ; i<arr.length ; i++){
        args.push( 'arr[' + i + ']' ) ;
    }
    obj._fn_ = this;
    val = eval( 'obj._fn_(' + args + ')' ) 
    delete obj._fn_;
    return val
}

bind

一樣提供兩種寫法app

Function.prototype.myBind = function(obj,...arg1){
    return (...arg2) => { 
        let args = arg1.concat(arg2);
        let val ;
        obj._fn_ = this;
        val = obj._fn_( ...args ); 
        delete obj._fn_;
        return val
    }
}
Function.prototype.myBind = function(obj){
    let _this = this;
    let argArr = [];
    let arg1 = [];
    for(let i = 1 ; i<arguments.length ; i++){ // 從1開始 
        arg1.push( arguments[i] ); // 這裏用arg1數組收集下參數
        // 獲取arguments是從1開始, 但arg1要從 0(i-1)開始
        // 如果用Array.prototype.slice.call(argument)就方便多了
        argArr.push( 'arg1[' + (i - 1)  + ']' ) ; // 若是用arguments在返回的函數裏運行 會獲取不到這個函數裏的參數了
    }
    return function(){
        let val ;
        for(let i = 0 ; i<arguments.length ; i++){ // 從0開始
            argArr.push( 'arguments[' + i + ']' ) ;
        }
        obj._fn_ = _this;
        val = eval( 'obj._fn_(' + argArr + ')' ) ;
        delete obj._fn_;
        return val
    };
}

淺克隆

淺克隆比較簡單,直接遍歷老對象逐個賦值便可。函數

function clone(target) {
    let cloneTarget = {};
    for (const key in target) {
        cloneTarget[key] = target[key];
    }
    return cloneTarget;
};

深克隆

深克隆須要考慮數據是基本類型仍是引用類型
clipboard.png
另外,引用類型中數組和對象要單獨處理,所以須要二次判斷
最後還要考慮對象的嵌套引用,防止堆棧溢出post

function clone(target, map = new Map()) {
    // 引用類型
    if (typeof target === 'object') {
        // 判斷是否爲數組
        let cloneTarget = Array.isArray(target) ? [] : {};
        // 用map來判斷該對象是否已經克隆過,方式嵌套引用
        if (map.get(target)) {
            return map.get(target);
        }
        map.set(target, cloneTarget);
        for (const key in target) {
            cloneTarget[key] = clone(target[key], map);
        }
        return cloneTarget;
    } 
    // 基本類型
    else {
        return target;
    }
};

本文參考:
https://www.jianshu.com/p/9ce...
https://www.jianshu.com/p/3b6...
http://www.javashuo.com/article/p-dwtfvyno-k.htmlthis

相關文章
相關標籤/搜索