一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
本题难度:⭐ ⭐ ⭐
答:
Function.prototype.myBind = function (context) {
const fn = this
const args = [...arguments].slice(1)
return function newFn () {
if (this instanceof newFn) {
return new fn(...args, ...arguments)
}
return fn.call(context, ...args, ...arguments)
}
}
或者
Function.prototype.myBind = function (context) {
const fn = this
const args = [...arguments].slice(1)
return function newFn () {
if (this instanceof newFn) {
return new fn(...args, ...arguments)
}
return fn.apply(context, [...args, ...arguments])
}
}
这两者的区别仅仅只是改变内部函数 this 指向时,一个用了 call、一个用了 apply,参数传递方式不同,其他都一模一样。
实现 this 指向改变和返回函数
Function.prototype.myBind = function (context) {
const fn = this // this 为调用方法 比如:fn.bind,this 就是 fn
return function () { // 返回一个函数,这样才能在外部调用
return fn.call(context) // 显式改变 this 指向 context
}
}
测试一下:
var lastName = 'xxx'
function fn() {
console.log(this.lastName)
}
const obj = {
lastName: 'lin'
}
fn.myBind(obj)() // 'lin'
考虑参数
因为 bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以我们需要像下面这样处理参数:
Function.prototype.myBind = function (context) {
const fn = this
const args = [...arguments].slice(1) // 取 myBind 函数内的参数
return function () {
return fn.call(context, ...args, ...arguments) // 把 myBind 函数内的参数和要返回的函数的参数拼接起来
}
}
测试一下:
function sum(x, y, z) {
console.log(x + y + z)
}
sum.myBind(null, 1, 2, 3)() // 6
sum.myBind(null, 1, 2)(3) // 6
sum.myBind(null, 1)(2, 3) // 6
兼容 new 调用的情况
对于函数来说有两种方式调用,一种是直接调用,一种是通过 new 的方式。
既然 bind 返回一个函数,那么这个返回的函数就可以被 new 调用,如下面代码所示:
function Person (name) {
this.name = name
}
const fn = Person.myBind()
const p = new fn('lin')
console.log('p :>> ', p)
console.log('p.name :>> ', p.name)
最终输出结果:
我们知道,构造函数中的 this 指向这个构造函数的实例,很显然,这里的输出不对,p.name 应该输出 'lin',继续改造 myBind 函数:
Function.prototype.myBind = function (context) {
const fn = this
const args = [...arguments].slice(1)
return function newFn () {
if (this instanceof newFn) { // 如果用了 new 的调用方式,this instanceof F 为 true
return new fn(...args, ...arguments) // 这里不需要传 this
}
return fn.call(context, ...args, ...arguments)
}
}
对于 new 的情况来说,this 总是指向构造函数的实例,不会被任何方式改变 this,所以对于这种情况我们需要忽略传入的 this。
function Person (name) {
this.name = name
}
const fn = Person.myBind()
const p = new fn('lin')
console.log('p :>> ', p)
console.log('p.name :>> ', p.name)
再次执行刚才出问题的代码,这次正常了。
我们知道,如果构造函数内部返回了一个对象,实例指向这个对象,测试一下这种情况:
function Person (name) {
this.name = name
return {
name: 'xxx'
}
}
const fn = Person.myBind()
const p = new fn('lin')
console.log('p :>> ', p)
console.log('p.name :>> ', p.name)
测试结果:
至此, myBind 函数就写完了,如果还有细节可以完善,欢迎评论区补充。
结尾
阿林水平有限,文中如果有错误或表达不当的地方,非常欢迎在评论区指出,感谢~
如果我的文章对你有帮助,你的👍就是对我的最大支持^_^
你也可以关注《前端每日一问》这个专栏,防止失联哦~
我是阿林,输出洞见技术,再会!
上一篇:
下一篇: