1. new
的實現原理
- 創建一個空對象
- 這個新對象被執行 [[原型]] 連接
- 構造函數中的this指向這個空對象,執行構造函數方法,屬性和方法被添加到this引用的對象中
- 如果構造函數中沒有返回其它對象,那麼返回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;
});
}
}
});
}