JavaScript之bind模拟实现

本文详细解析了JavaScript中bind方法的工作原理,包括其如何改变函数的this上下文,如何预设参数,以及如何在作为构造函数时正确处理this。通过逐步模拟实现bind方法,文章深入探讨了bind的内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaScript之bind模拟实现

一句话介绍bind:
bind() 方法会创建一个新函数,当这个新函数被调用时,bind()的第一个参数将作为他运行时的this,之后的一序列参数将会在传递的实参前传入作为他的参数
 
由此可以看出两个特点:
  1. 返回一个函数
  2. 可以传入参数
 
返回函数的模拟实现
第一个特点,我们举个例子(返回一个函数)
var obj = {
    value : 1
}

function foo(name,age){
    console.log(name)
    console.log(age)
    console.log(this.value)
}

var foo1 = foo.bind(obj,'hy')
foo1(18)// hy 18 1

模拟实现第一步

Function.prototype.bind2 = function(context){
    var that = this
    return function(){
        return that.apply(context)
    }
}

之所以 returnthat.apply(context) ,是考虑到绑定函数可能有返回值。

var obj = {
    value : 1
}
function foo(){
    return this.value
}

var foo1 = foo.bind(obj)
console.log(foo1()). // 1

传参的模拟实现

我们举个例子(可以传参)
var obj = {
    value : 1
}
function foo(name,age){
    console.log(this.value)
    console.log(name)
    console.log(age)
}
var Foobind = foo.bind(obj,'hy')
Foobind(18)

函数需要传name和age两个参数,竟然还可以在bind的时候,只传一个name,在执行返回的函数的时候,在传另一个参数age

模拟实现第二步
Function.prototype.bind2 = function(context){
    var that = this
    // 获取bind2函数从第二个参数到最后一个参数
    var args = Array.prototype.slice.call(arguments,1)
    return function(){
        // 这个时候的 arguments 是指bind返回的函数传入的参数
        var bindArgs = Array.prototype.slice.call(arguments)
        return that.apply(context,args.concat(bindArgs))
    }
}

构造函数效果的模拟实现

最难的部分到了,因为bind还有一个特点,就是:
  1. 一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的this值被忽略,同时调用时的参数被提供给模拟函数
也就是说当bind返回的函数作为构造函数的时候,bind时指定的this值会失效,但传入的值依然有效
var value = 2
    var obj = {
    value : 1
}

function foo(name,age){
    this.name = 'ycl'
    console.log(this.value)// undefined
    console.log(name)// hy
    console.log(age)// 18
}
foo.prototype.flag = '1-'

var bindFoo = foo.bind(obj,'hy')
var bindFoo1 = new bindFoo(18)

console.log(bindFoo1.name)// ycl
console.log(bindFoo1.flag)// 1-

注意 :尽管在全局和obj 都声明了value值,但最后返回了undefined,说明绑定this失效了。

所以我们可以通过修改返回的函数的原型来实现
模拟实现第三步
Function.prototype.bind2=function(context){
    var that = this
    var args = Array.prototype.slice.call(arguments,1)
    var fBound = function(){
        var bindArgs = Array.prototype.slice.call(arguments)
        // 当作为构造函数时,this 指向实例,此时结果为true,将绑定函数的this指向该实例,可以让实例获得来自绑定函数的值
        // 当作为普通函数时,this 指向window,此时结果为false,将绑定函数的this指向context
        return that.apply(this instanceof fBound ? this : context , args.concat(bindArgs))
    }
    // 修改返回函数的 prototype 为绑定函数的prototype,实例就可以继承绑定函数的原型的值 (此例子的绑定函数为foo)
    fBound.prototype = this.prototype
    return fBound
}

构造函数效果的优化实现

但是在这个写法中,我们直接将 fBound . prototype = this . prototype ,我们直接修改 fBound . prototype的时候,也会直接修改绑定函数的prototype。这个时候我们通过一个空函数进行中转。
模拟实现第四步
Function.prototype.bind2 = function(context){
    var that = this
    var args = Array.prototype.slice.call(arguments , 1)
    var fBound = function(){
        var bindArgs = Array.prototype.slice.call(arguments)
        var fNOP = function(){}
        // 当作为构造函数时,this 指向实例,此时结果为true,将绑定函数的this指向该实例,可以让实例获得来自绑定函数的值
        // 当作为普通函数时,this 指向window,此时结果为false,将绑定函数的this指向context
        return that.apply(this instanceof fNOP ? this : context , args.concat(bindArgs))
    }
    // 修改返回函数的 prototype 为绑定函数的prototype,实例就可以继承绑定函数的原型的值 (此例子的绑定函数为foo)
    fNOP.prototype = this.prototype
    fBound.prototype = new fNOP()
    return fBound
}

如果调用bind的不是函数咋办?

if(typeof this !== 'function'){
    throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}

最后完成版本

Function.prototype.bind2 = function(context){
    if(typeof this !== 'function'){
        throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }
    var that = this
    var args = Array.prototype.slice.call(arguments , 1)
    var fBound = function(){
        var bindArgs = Array.prototype.slice.call(arguments)
        var fNOP = function(){}
        // 当作为构造函数时,this 指向实例,此时结果为true,将绑定函数的this指向该实例,可以让实例获得来自绑定函数的值
        // 当作为普通函数时,this 指向window,此时结果为false,将绑定函数的this指向context
        return that.apply(this instanceof fNOP ? this : context , args.concat(bindArgs))
    }
    // 修改返回函数的 prototype 为绑定函数的prototype,实例就可以继承绑定函数的原型的值 (此例子的绑定函数为foo)
    fNOP.prototype = this.prototype
    fBound.prototype = new fNOP()
    return fBound
}

 

转载于:https://my.oschina.net/u/4052053/blog/3020402

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值