javascript中new,call,apply,bind等方法是咱們常常要使用到,在僞數組轉數組、函數傳參、繼承等場景中,都離不開他們。這裏就不具體討論他們的使用方法,咱們將研究他們的實現方式,重寫屬於咱們本身的方法,讓你們從新認識他們的實現過程,使咱們在開發中知其然,知其因此然!javascript
咱們用new實例化一個構造函數,生成一個實例對象,而new到底作了什麼呢,主要分爲如下五步:java
function MyNew() { let Constructor = Array.prototype.shift.call(arguments); // 1:取出構造函數 let obj = {} // 2:執行會建立一個新對象 obj.__proto__ = Constructor.prototype // 3:該對象的原型等於構造函數prototype var result = Constructor.apply(obj, arguments) // 4: 執行函數中的代碼 return typeof result === 'object' ? result : obj // 5: 返回的值必須爲對象 }
function Man(name, age) { this.name = name this.age = age } var tom = new Man('tom', 20) var mike = MyNew(Man, 'mike', 30) console.log(tom instanceof Man, mike instanceof Man) // true true
call方法的實現主要有如下三步,好比 fn.call(obj, a, b)
:數組
Function.prototype.myCall = function (context) { context = context ? Object(context) : window context.fn = this // 重置上下文 let args = [...arguments].slice(1) // 截取參數a,b let r = context.fn(...args) // 執行函數 delete context.fn // 刪除屬性,避免污染 return r // 返回結果 }
// 瀏覽器環境下 var a = 1, b = 2; var obj ={a: 10, b: 20} function test(key1, key2){ console.log(this[key1] + this[key2]) } test('a', 'b') // 3 test.myCall(obj, 'a', 'b') // 30
apply方法和call方法大同小異,惟一差異就是,apply傳入的參數是數組格式。瀏覽器
// apply 原理 Function.prototype.myApply = function (context) { context = context ? Object(context) : window context.fn = this let args = [...arguments][1] if (!args) { return context.fn() } let r = context.fn(...args) delete context.fn; return r }
// 瀏覽器環境下 var a = 1, b = 2; var obj ={a: 10, b: 20} function test(key1, key2){ console.log(this[key1] + this[key2]) } test('a', 'b') // 3 test.myCall(obj, ['a', 'b']) // 30 注意這裏是傳入數組 ['a', 'b']
bind方法和call、apply方法的差異是,他們都改變了上下文,可是bind沒有當即執行函數。app
// bind 原理 Function.prototype.Mybind = function (context) { let _me = this return function () { return _me.apply(context) } }
var a = 1, b = 2; var obj ={a: 10, b: 20} function test(key1, key2){ console.log(this[key1] + this[key2]) } var fn = test.bind(obj) fn('a', 'b') // 30
好了,介紹完了,若是以爲對你有幫助,點個贊哈,嘿嘿!函數