手寫JS面試題 --- call apply bind 實現
阿新 • • 發佈:2021-06-21
手寫JS面試題 --- call apply bind 實現
題目描述:手寫 call apply bind 實現
實現程式碼如下:
Function.prototype.myCall = function (context, ...args) { if (!context || context === null) { context = window; } // 建立唯一的 key 值 作為我們構造的 context 內部方法名 let fn = Symbol(); context[fn] = this; // this 指向呼叫 call 的函式 // 執行函式並返回結果 相當於把自身作為傳入的 context 的方法進行呼叫了! return context[fn](...args); }; // apply 原理一致 只是第二個傳入的引數是一個數組! Function.prototype.myApply = function (context, args) { if (!context || context === null) { context = window; } // 建立唯一的 key 值 作為我們構造的 context 內部方法名 let fn = Symbol(); context[fn] = this; // 執行函式並返回結果! return context[fn](...args); }; // bind 實現要複雜一點 因為他考慮的情景比較多 還要涉及到引數合併(類似函式柯里化) Function.prototype.myBind = function (context, ...args) { if (!context || context === null) { context = window; } // 建立唯一的 key 值 作為我們構造的 context 內部方法名! let fn = Symbol(); context[fn] = this; let _this = this; // bind 情況要複雜一點 const result = function (...innerArgs) { // 第一種情況 :若是將 bind 繫結之後的函式當作建構函式,通過 new 操作符使用,則不繫結傳入的 this,而是將 this 指向例項化出來的物件 // 此時由於new操作符作用 this指向result例項物件 而result又繼承自傳入的_this 根據原型鏈知識可得出以下結論 // this.__proto__ === result.prototype // this instanceof result => true // this.__proto__.__proto__ = result.prototype.__proto__ === _this.prototype; // this instanceof _this => true if (this instanceof _this === true) { // 此時 this 指向 result 的例項 這時候不需要改變 this 指向 this[fn] = _this; this[fn](...[...args, ...innerArgs]); // 這裡使用 es6 的方法讓 bind 支援引數合併 delete this[fn]; } else { // 如果只是作為普通的函式使用 那就很簡單了 直接改變 this 指向為傳入的 context context[fn](...[...args, ...innerArgs]); delete context[fn]; } }; // 如果繫結的是建構函式 那麼需要繼承建構函式原型的屬性和方法 // 實現繼承的方式:使用 Object.create result.prototype = Object.create(this.prototype); return result; } // 用法如下: function Person(name, age) { console.log(name); //'我是引數傳進來的name' console.log(age); //'我是引數傳進來的age' console.log(this); //建構函式this指向例項物件 } // 建構函式原型的方法 Person.prototype.say = function() { console.log(123); } let obj = { objName: '我是 obj 傳進來的 name', objAge: '我是 objAge 傳進來的 age' } // 普通函式 function normalFun(name, age) { console.log(name); //'我是引數傳進來的name' console.log(age); //'我是引數傳進來的age' console.log(this); //普通函式this指向繫結bind的第一個引數 也就是例子中的obj console.log(this.objName); //'我是obj傳進來的name' console.log(this.objAge); //'我是obj傳進來的age' } // 先測試作為建構函式的呼叫 // let bindFun = Person.myBind(obj, '我是引數傳進來的name') // let a = new bindFun('我是引數傳進來的 age') // a.say(); // 123 console.log('------------------------') // 在測試作為普通函式的呼叫! let bindFun = normalFun.myBind(obj, '我是引數傳進來的name') bindFun('我是引數傳進來的 age')