apply
的方法和 call
方法的實現相似,只不過是若是有參數,以數組形式進行傳遞,直接上代碼:javascript
Function.prototype.apply2 = function(context) { var context = context || window context.fn = this // this 也就是調用apply的函數 var result // 判斷是否有第二個參數 if(arguments[1]) { result = context.fn(...arguments[1]) } else { result = context.fn() } delete context.fn() return result } var foo = { value: 1 } function bar(name, age) { console.log(name) console.log(age) console.log(this.value); } bar.apply2(foo, ['black', '18']) // black 18 1
bind() 方法會建立一個新的函數。當這個新函數被調用時,bind() 的第一個參數會做爲它運行時的this,以後的參數將會在傳遞的實參前傳入做爲它的參數。
也就是說bind能夠返回一個函數,而且能夠傳入參數。java
首先根據第一點,須要返回一個函數:數組
Function.prototype.bind2 = function(context) { var self = this return function() { self.apply(context) } } var foo = { value: 1 }; function bar() { console.log(this.value); } var bindFoo = bar.bind2(foo); bindFoo() // 1
接下來還有第二點,能夠傳入參數,但是傳參數還會有兩種狀況,是在bind的時候傳參數呢,仍是在調用返回的函數的時候傳參數呢?app
var foo = { value: 1 }; function bar(name, age) { console.log(name); console.log(age); console.log(this.value); } var bindFoo = bar.bind(foo, 'black'); bindFoo('18') // black // 18 // 1
經過上面的例子就能夠明顯發現。兩種方式均可以傳參。那模擬實現:函數
Function.prototype.bind2 = function(context) { var self = this var args = [...arguments].slice(1) console.log([...arguments]) return function() { // 這個時候的arguments指調用的時候傳入的參數 self.apply(context, args.concat([...arguments])) } } var foo = { value: 1 }; function bar(name, age) { console.log(name); console.log(age); console.log(this.value); } var bindFoo = bar.bind2(foo, 'black'); bindFoo('18')
這兩點完成後,還有一個特色,就是一個綁定函數也能使用new操做符建立對象,提供的this值被忽略,同時調用時的參數被提供給模擬函數。this
也就是說 bind 返回的函數做爲構造函數的時候,bind 時指定的 this 值會失效,可是傳入的參數依然生效,舉個例子來講明:prototype
var value = 2; var foo = { value: 1 } function bar(name, age) { console.log(name) console.log(age) console.log(this.value) } bar.prototype.sex = 'boy' var bindFoo = bar.bind(foo, 'black') var obj = new bindFoo('18') // black // 18 // undefined console.log(obj.sex) // boy
儘管在全局和 foo 中都聲明瞭 value 值,最後依然返回 undefined,那是由於當調用obj的時候,這個時候的this指向已經指向到了 objcode
因此咱們要去修改一下返回函數的原型來去實現:對象
Function.prototype.bind2 = function(context) { var self = this; var args = [...arguments].slice(1) var fun = function() { // 看成爲構造函數的時候,this 指向實例, self指向綁定函數,由fun.prototype = this.prototype, 使 fun.prototype 爲 綁定函數的 prototype,此時結果爲true,因此this指向實例 // 做爲普通函數時,this 指向window, self 指向綁定函數,此時結果爲false,此時this指向綁定的context self.apply(this instanceof self ? this : context,args.concat(...arguments)) } // 修改返回函數的 prototype 爲綁定函數的 prototype,實例就能夠繼承函數的原型中的值 // 使用Object.create避免修改函數的prototype fun.prototype = Object.create(this.prototype) return fun }
最後加個對調用bind是不是個函數的判斷:繼承
Function.prototype.bind2 = function(context) { if(typeof this !== 'function') { throw new TypeError('Error') } var self = this; var args = [...arguments].slice(1) var fun = function() { // 看成爲構造函數的時候,this 指向實例, self指向綁定函數,由fun.prototype = this.prototype, 使 fun.prototype 爲 綁定函數的 prototype,此時結果爲true,因此this指向實例 // 做爲普通函數時,this 指向window, self 指向綁定函數,此時結果爲false,此時this指向綁定的context self.apply(this instanceof self ? this : context,args.concat(...arguments)) } // 修改返回函數的 prototype 爲綁定函數的 prototype,實例就能夠繼承函數的原型中的值 // 使用Object.create避免修改函數的prototype fun.prototype = Object.create(this.prototype) return fun }