手撕 JavaScript 程式碼

new 的模擬實現

new 運算子建立一個使用者定義的物件型別的例項或具有建構函式的內建物件型別之一

過程

首先建立一個新的空物件

設定原型,將物件的原型(

__proto__

)設定為函式的 prototype 物件

讓函式的 this 指向這個物件,執行建構函式的程式碼(為這個新物件新增屬性)

判斷函式的返回值型別,如果是值型別,返回建立的物件。如果是引用型別,就返回這個引用型別的物件

實現:

function objectFactory(){ let Constructor = Array。prototype。shift。call(arguments) if(typeof Constructor !== ‘function’) { console。err(‘type err’) return; } let obj = Object。create(Constructor。prototype) let ret = Constructor。apply(obj, arguments) let flag = ret && (typeof ret === “object” || typeof ret === “function”) return flag ? ret : obj}

Array。prototype。filter

引數:一個函式引數。這個函式接受一個預設引數,就是當前元素。這個作為引數的函式返回值為一個布林值型別,決定元素是否保留。

filter 方法返回值為一個新的陣列,這個數組裡麵包含引數裡面所有被保留的項。

示例

let nums = [1,2,3]let odd = nums。filter(item=>item%2) //[1,3]

實現

Array。prototype。filter = function(callback, thisArg){ // 處理陣列異常情況 if(this === undefined || this === null) { throw new TypeError(‘this is null or not undefined’) } if(typeof callback !== ‘function’){ throw new TypeError(callback + ‘ is not a function’) } const res = [] // 讓 O 成為回撥函式的物件傳遞(強制轉換物件) let O = Object(this) // >>>0 保證len為number, 且為正數 let len = O。length >>> 0 let resLen = 0 for(let i=0; i

關於

>>>0

,請看 js中 something >>> 0是什麼意思?

Array。prototype。map

引數:接受兩個引數,一個是回撥函式,一個是回撥函式的 this(可選)

其中,回撥函式被預設傳入三個值,依次為當前元素、當前索引、整個陣列。

建立一個數組,其結果是該陣列中的每個元素都呼叫一個提供的函式後返回的結果

對原來的陣列沒有影響。

實現

Array。prototype。map = function(callback, thisArg){ if(this === null || this === undefined){ throw new TypeError(“Can not read property ‘map’ of null or undefined”) } if(Object。prototype。toString。call(callback) !== ‘[object Function]’){ throw new TypeError(callback + ‘ is not a function’) } let res = [] let O = Object(this) let len = O。length >>> 0 for(let i=0; i

Array。prototype。forEach

Array。prototype。map

差不多,但沒有返回值。

Array。prototype。forEach = function(callback, thisArg){ if(this === null || this === undefined){ throw new TypeError(“Can not read property ‘map’ of null or undefined”) } if(Object。prototype。toString。call(callback) !== ‘[object Function]’){ throw new TypeError(callback + ‘ is not a function’) } let O = Object(this) let len = O。length >>> 0 for(let i=0; i

Array。prototype。reduce

引數:一個為回撥函式,一個為初始值。

回撥函式中三個預設引數,依次為積累值、當前值、整個陣列

不傳預設值會自動以第一個元素為初始值,然後從第二個元素開始一次累計

示例

let nums = [1,2,3]let newNums = nums。reduce(function(pre, cur, nums){ return pre+cur},0) //6

實現

Array。prototype。reduce = function(callback, initialValue){ if(this === null || this === undefined){ throw new TypeError(“Can not read property ‘map’ of null or undefined”) } if(Object。prototype。toString。call(callback) !== ‘[object Function]’){ throw new TypeError(callback + ‘ is not a function’) } let O = Object(this) let len = O。length >>> 0 let k = 0 let accumulator = initialValue // 如果第二個引數為 undefined 的情況下,陣列的第一個有效值作為累加器的初始值 if(accumulator === undefined){ while(k= len){ throw new TypeError(‘Reduce of empty array with no initial value’) } accumulator = O[k++] } while(k < len){ if(k in O){ accumulator = callback。call(undefined, accumulator, O[k], O) } } return accumulator}

call 的模擬實現

call() 方法在使用一個指定的 this 值和若干個指定的引數值的前提下呼叫某個函式或方法。

Function。prototype。call = function(context = window, 。。。args){ if(typeof this !== ‘function’){ throw new TypeError(‘Type Error’) } context。_fn = this let result = context。_fn(。。。args) delete context。_fn return result}

apply 的模擬實現

第一個引數為繫結的this,預設為window,第二個引數是陣列或類陣列

Function。prototype。apply = function(context = window, args){ if(typeof this !== ‘function’){ throw new TypeError(‘Type Error’) } context。_fn = this let result = context。_fn(。。。args) delete context。_fn return result}

bind 的模擬實現

bind() 方法會建立一個新函式。當這個新函式被呼叫時,bind() 的第一個引數將作為它執行時的 this,之後的一序列引數將會在傳遞的實參前傳入作為它的引數。

【注意】:一個繫結函式也能使用new運算子建立物件:這種行為就像把原函式當成構造器。提供的 this 值被忽略,同時呼叫時的引數被提供給模擬函式。

Function。prototype。bind = function(context = window, 。。。args){ if(typeof this !== “function”){ throw new Error(“Type Error”) } // 儲存 this 的值 var self = this return function F(){ //考慮new的情況 // 當作為建構函式時,this 指向例項,此時結果為 true,將繫結函式的 this 指向該例項,可以讓例項獲得來自繫結函式的值 if(this instanceof F){ return new self(。。。args, 。。。arguments) } return self。apply(context, [。。。args, 。。。arguments]) }}