window.name = 'window'
var obj = {
name: 'obj'
}
function getName(p1, p2) {
console.log(p1, p2)
console.log(this.name)
}
getName('str1', 'str2')
getName.call(obj, 'str1', 'str2')
// 函數運行結果能夠思考一下。
複製代碼
怎麼記住call呢,其實就是getName這個方法執行了,不是window去執行,是call括號內的參數去執行。數組
能夠聯想到一個很典型的使用場景 [].prototype.shift.call(arguments),起初很不理解這種寫法,後來一想,其實就是 arguments 不是數組,它沒有shift方法能夠直接來用,就把數組的shift方法拿來用。app
1 明確是誰調用call,答案,是函數。函數
2 call接收的參數是什麼?第一個參數是要改變的this指針,也就是上面說到了,是誰去執行這個函數。若無指定,默認爲windowthis
3 call接收的第二個,第三個,等等,參數,是用來作什麼的?答,就是做爲調用call的那個函數所需的參數。spa
function myCall(context) {
// 1
if (typeof this !== 'function'){
throw new TypeError('error')
}
// 2
context = context || window
// 3
context.fn = this
// 4
const args = [...arguments].slice(1)
// 5
const result = context.fn(...args)
// 6
delete context.fn
return result
}
Function.prototype.myCall = myCall
getName.myCall(obj, 'str1', 'str2')
複製代碼
apply使用與call大致一致,只是接受參數的方法不一樣。call能夠接收多個參數。apply接收的第一個參數是this,第二個參數是 所需參數所組成的數組。prototype
window.name = 'window'
var obj = {
name: 'obj'
}
function getName(p1, p2) {
console.log(p1, p2)
console.log(this.name)
}
getName('str1', 'str2')
getName.apply(obj, ['str1', 'str2'])
複製代碼
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
var result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
複製代碼
和上述的call實現基本相似,就參數處理有些不一樣,再也不贅述。指針
爲何有call apply後還要有個bind ? 當咱們須要綁定一個點擊事件的時候,就改變回調函數的this,怎麼破?由於 call apply都是當即執行了,因此bind登場。看一下下面這個例子吧。code
var obj = {
name: 'obj'
}
document.addEventListener('click',myClick.bind(obj,'p1','p2'),false);
function myClick(p1,p2){
console.log(this.name, p1, p2)
}
複製代碼
MDN的解釋是:bind()方法會建立一個新函數,稱爲綁定函數,當調用這個綁定函數時,綁定函數會以建立它時傳入 bind()方法的第一個參數做爲 this,傳入 bind() 方法的第二個以及之後的參數加上綁定函數運行時自己的參數按照順序做爲原函數的參數來調用原函數。cdn
注意:bind方法的返回值是函數對象
bind()最簡單的用法是建立一個函數,使這個函數不論怎麼調用都有一樣的this值。
由於bind返回值是函數,那麼函數除了直接運行以外,還能夠做爲構造函數放在new 操做符以後,因此bind的實現就要把這種狀況考慮進去。
任何函數均可以做爲構造函數,放在 new 以後使用,那麼new的過程是怎麼樣的呢?大致分爲如下幾步,具體不深究。
上面所說的那個空對象就是構造函數內部的this,而且 對於 new 的狀況來講,不會被任何方式改變 this
window.name = 'window'
var obj = {
name: 'obj'
}
function Fun(p1,p2){
console.log(this)
console.log(this.__proto__ === Fun.prototype)
console.log(this.name)
this.a = p1
this.b = p2
console.log(this)
}
var c = new Fun('str1', 'str2')
console.log(c)
複製代碼
運行結果以下:
![]()
再來看一下,直接執行Fun的返回結果,代碼不作修改,直接執行 Fun('str1', 'str2')
兩次結果的不一樣,本文不展開敘述。那麼舉這個例子是想說明什麼呢? 答 : 函數充當構造函數和普通函數,運行時內部的this指向不一樣。 接下來看bind函數的模擬實現
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
// 返回函數
return function F() {
// 1 判斷是否用做構造函數
if (this instanceof F) {
return new _this(...args, ...arguments)
}
// 2 用做普通函數
return _this.apply(context, args.concat(...arguments))
}
}
// 仍是用上述舉例子
window.name = 'window'
var obj = {
name: 'obj'
}
function Fun(p1, p2){
this.a = p1
this.b = p2
console.log(this.name)
console.log(p1, p2)
}
var f1 = Fun.bind(obj, 'str1')
f1('str2')
複製代碼
運行結果以下,可見 改變了fn1函數的this指向
再來看一下 ,去掉 f1('str2') ,換成以下語句的運行結果
// f1('str2')
var b = new f1('str2')
console.log(b)
複製代碼
沒有逐行的說明,不過對於內部實現,有new的前置介紹和註釋,相信開始的call實現代碼都看懂了的話,這個bind方法也會一目瞭然