58.call、apply以及bind函式內部實現是怎麼樣的
阿新 • • 發佈:2020-08-11
call, apply, bind的內部實現原理 https://www.cnblogs.com/renzhiwei2017/p/10364760.html
call, apply, bind都是改變函式執行的上下文,說的直白點就是改變了函式this的指向。不同的是:call和apply改變了函式的this,並且執行了該函式,而bind是改變了函式的this,並返回一個函式,但不執行該函式。
看下面的例子1:
var doThu = function(a, b) {
console.log(this)
console.log(this.name)
console.log([a, b])
}
var stu = {
name: 'xiaoming',
doThu: doThu,
}
stu.doThu(1, 2) // stu物件 xiaoming [1, 2]
doThu.call(stu, 1, 2) // stu物件 xiaoming [1, 2]
由此可見,在stu上新增一個屬性doThu,再執行這個函式,就將doThu的this指向了stu。而call的作用就與此相當,只不過call為stu添加了doThu方法後,執行了doThu,然後再將doThu這個方法從stu中刪除。
下面來看call函式的內部實現原理:
Function.prototype.call = function(thisArg, args) {
// this指向呼叫call的物件
if (typeof this !== 'function') { // 呼叫call的若不是函式則報錯
throw new TypeError('Error')
}
thisArg = thisArg || window
thisArg.fn = this // 將呼叫call函式的物件新增到thisArg的屬性中
const result = thisArg.fn(...[...arguments].slice(1)) // 執行該屬性
delete thisArg.fn // 刪除該屬性
return result
}
apply的實現原理和call一樣,只不過是傳入的引數不同而已。下面只給出程式碼,不做解釋:
Function.prototype.apply = function(thisArg, args) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
thisArg = thisArg || window
thisArg.fn = this
let result
if(args) {
result = thisArg.fn(...args)
} else {
result = thisArg.fn()
}
delete thisArg.fn
return result
}
bind的實現原理比call和apply要複雜一些,bind中需要考慮一些複雜的邊界條件。bind後的函式會返回一個函式,而這個函式也可能被用來例項化:
Function.prototype.bind = function(thisArg) {
if(typeof this !== 'function'){
throw new TypeError(this + 'must be a function');
}
// 儲存函式本身
const _this = this;
// 去除thisArg的其他引數 轉成陣列
const args = [...arguments].slice(1)
// 返回一個函式
const bound = function() {
// 可能返回了一個建構函式,我們可以 new F(),所以需要判斷
if (this instanceof bound) {
return new _this(...args, ...arguments)
}
// apply修改this指向,把兩個函式的引數合併傳給thisArg函式,並執行thisArg函式,返回執行結果
return _this.apply(thisArg, args.concat(...arguments))
}
return bound
}