call,apply,bind函数内部实现是怎样的?
this的特性对于我们前端来说是很重要的,本文承接个人总结的JS基础中的this部分
tips:代码是JS的模拟实现,只是为了掌握this,真实的call apply bind并不是这么简单的用JS实现的而且相信他们也会处理更多的边界问题. 其中bind实现参考了这篇文章
Function.prototype.myCall = function (thisArg, ...args) {
const fn = Symbol('fn')
thisArg = thisArg ?? window
thisArg= Object(thisArg)
thisArg[fn]=this
const result = thisArg[fn](...args)
delete thisArg[fn]
return result
}
function foo(num1, num2){
return num1 + num2
}
console.log(foo.myCall(null, 1, 2))
Function.prototype.myApply = function (thisArg, args) {
const fn = Symbol('fn')
thisArg = thisArg ?? window
thisArg = Object(thisArg)
thisArg[fn] = this
const result = thisArg[fn](...args)
delete thisArg[fn]
return result
}
function foo(num1, num2, num3){
return num1 + num2 + num3
}
console.log(foo.myApply('abc', [4, 5, 1]))
// 如果你很了解apply 那么我们可以利用他们来实现一个完整的myBind
Function.prototype.myBind = function (thisArg, ...arrayArg) {
const fn = this
if (typeof fn !== 'function') {
throw new Error('Function.prototype.bind - what is trying to be bound is not callable')
}
thisArg = thisArg ?? window
thisArg = Object(thisArg)
const fBound = function (...args) {
arrayArg = arrayArg.concat(args)
if (new.target !== undefined) {
return new fn(...arrayArg)
} else {
return fn.apply(thisArg, arrayArg)
}
}
return fBound;
}
// 不利用apply关键字实现myBind
Function.prototype.myBind = function (thisArg, ...arrayArg) {
const fn = this
const fnName = Symbol('fnName')
if (typeof fn !== 'function') {
throw new Error('Function.prototype.bind - what is trying to be bound is not callable')
}
thisArg = thisArg ?? window
thisArg = Object(thisArg)
// 定义一个空函数 来中转
const fNOP = function () {}
const fBound = function (...args) {
arrayArg = [...arrayArg, ...args]
// 如果new.target 不等于undefined 说明函数被new调用了
if(new.target !== undefined){
// 所以当前this会执行new的实例,我们要把fn函数隐式绑定到new实例上
this[fnName] = fn
const result = this[fnName](...arrayArg)
delete this[fnName]
return result
} else {
// 如果没被new调用,那么this就隐式绑定到传进来的thisArg
thisArg[fnName] = fn
const result = thisArg[fnName](...arrayArg)
delete thisArg[fnName]
return result
}
}
// 将调用bind绑定函数的原型赋值给fNOP中转函数的原型
fNOP.prototype = fn.prototype
// 将fNOP的实例赋值给将要返回fBound函数的原型实现原型式继承,并且利用中转函数使得fn.prototype和fBound.prototype不会共享同一个对象
fBound.prototype = new fNOP()
return fBound
}
var value = 2
var foo = {
value: 1,
}
function bar(name, age) {
this.habit = "shopping"
console.log(this.value)
console.log(name)
console.log(age)
}
bar.prototype.friend = "kevin"
var bindFoo = bar.myBind(foo, "daisy")
// console.log('bindFoo',bindFoo('19'));
// console.log('bindFoo',bindFoo('19'));
var obj = new bindFoo("18")
// undefined
// daisy
// 18
console.log(obj.habit)
console.log(obj.friend)
// shopping
// kevin