js 原理題

1. new 的實現原理

  1. 創建一個空對象
  2. 這個新對象被執行 [[原型]] 連接
  3. 構造函數中的this指向這個空對象,執行構造函數方法,屬性和方法被添加到this引用的對象中
  4. 如果構造函數中沒有返回其它對象,那麼返回this,即創建的這個的新對象,否則,返回構造函數中返回的對象。
function _new () {
    let target = {}; // 創建的新對象
    // 第一個參數是構造函數
    let [constructor, ...args] = [...arguments];
    // 執行[[原型]]連接;target 是 constructor 的實例
    target.__proto__ = constructor.prototype;
    // 構造函數中的this指向空對象,執行構造函數,將屬性和方法添加到創建的空對象上
    let result = constructor.apply(target, args);
    if (result && (typeof result == 'object' || typeof result == 'function')) {
        // 如果構造函數執行的結果返回的是一個對象,那麼返回這個對象
        return result;
    }
    // 如果構造函數返回的不是一個對象,返回創建的新對象
    return target;
}

2. call / apply 的實現原理是什麼?

call 和 apply 的功能相同,都是改變 this 的指向,並立即執行函數。區別在於傳參方式不同。

  • func.call(thisArg, arg1, arg2, ...):第一個參數是 this 指向的對象,其它參數依次傳入。

  • func.apply(thisArg, [argsArray]):第一個參數是 this 指向的對象,第二個參數是數組或類數組。

一起思考一下,如何模擬實現 call ?

首先,我們知道,函數都可以調用 call,說明 call 是函數原型上的方法,所有的實例都可以調用。即: Function.prototype.call

  • 在 call 方法中獲取調用call()函數
  • 如果第一個參數沒有傳入,那麼默認指向 window / global(非嚴格模式)
  • 傳入 call 的第一個參數是 this 指向的對象,根據隱式綁定的規則,我們知道 obj.foo()foo() 中的 this 指向 obj;因此我們可以這樣調用函數 thisArgs.func(...args)
  • 返回執行結果
Function.prototype.call = function() {
    let [thisArg, ...args] = [...arguments];
    if (!thisArg) {
        //context爲null或者是undefined
        thisArg = typeof window === 'undefined' ? global : window;
    }
    //this的指向的是當前函數 func (func.call)
    thisArg.func = this;
    //執行函數
    let result = thisArg.func(...args);
    delete thisArg.func; //thisArg上並沒有 func 屬性,因此需要移除
    return result;
}

bind 的實現思路和 call 一致,僅參數處理略有差別。如下:

Function.prototype.apply = function(thisArg, rest) {
    let result; //函數返回結果
    if (!thisArg) {
        //context爲null或者是undefined
        thisArg = typeof window === 'undefined' ? global : window;
    }
    //this的指向的是當前函數 func (func.call)
    thisArg.func = this;
    if(!rest) {
        //第二個參數爲 null / undefined 
        result = thisArg.func();
    }else {
        result = thisArg.func(...rest);
    }
    delete thisArg.func; //thisArg上並沒有 func 屬性,因此需要移除
    return result;
}

3. 柯里化函數實現

在開始之前,我們首先需要搞清楚函數柯里化的概念。

函數柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受餘下的參數而且返回結果的新函數的技術。

const curry = (fn, ...args) =>
    args.length < fn.length
        //參數長度不足時,重新柯里化該函數,等待接受新參數
        ? (...arguments) => curry(fn, ...args, ...arguments)
        //參數長度滿足時,執行函數
        : fn(...args);
function sumFn(a, b, c) {
    return a + b + c;
}
var sum = curry(sumFn);
console.log(sum(2)(3)(5));//10
console.log(sum(2, 3, 5));//10
console.log(sum(2)(3, 5));//10
console.log(sum(2, 3)(5));//10

函數柯里化的主要作用:

  • 參數複用
  • 提前返回 – 返回接受餘下的參數且返回結果的新函數
  • 延遲執行 – 返回新函數,等待執行

4. 實現 Promise.all 方法

在實現 Promise.all 方法之前,我們首先要知道 Promise.all 的功能和特點。

Promise.all 功能

Promise.all(iterable) 返回一個新的 Promise 實例。此實例在 iterable 參數內所有的 promise 都 fulfilled 或者參數中不包含 promise 時,狀態變成 fulfilled;如果參數中 promise 有一個失敗rejected,此實例回調失敗,失敗原因的是第一個失敗 promise 的返回結果。

let p = Promise.all([p1, p2, p3]);

p的狀態由 p1,p2,p3決定,分成以下;兩種情況:
(1)只有p1、p2、p3的狀態都變成 fulfilled,p的狀態纔會變成 fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。
(2)只要p1、p2、p3之中有一個被 rejected,p的狀態就變成 rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

Promise.all 實現

Promise.all = function (promises) {
    //promises 是可迭代對象,省略參數合法性檢查
    return new Promise((resolve, reject) => {
        //Array.from 將可迭代對象轉換成數組
        promises = Array.from(promises);
        if (promises.length === 0) {
            resolve([]);
        } else {
            let result = [];
            let index = 0;
            for (let i = 0;  i < promises.length; i++ ) {
                //考慮到 i 可能是 thenable 對象也可能是普通值
                Promise.resolve(promises[i]).then(data => {
                    result[i] = data;
                    if (++index === promises.length) {
                        //所有的 promises 狀態都是 fulfilled,promise.all返回的實例才變成 fulfilled 態
                        resolve(result);
                    }
                }, err => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章