实现bind函数

本文详细解析了如何手写JavaScript的Function.prototype.bind方法,包括基本的bind实现、构造函数使用、原型链继承优化等,通过具体示例展示了bind在不同场景下的应用。

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

Function.prototype.bind : 原生bind函数,改变函数运行时的作用域。支持参数:第一个为作用域,后面的为传递给运行函数的参数(可为空)。

手写bind函数从最基本的函数开始:

1. 支持带参数的函数

var foo = {
    age: 17
}
function getName(name) {
    console.log(name)
    return this.age
}
console.log(getName('aa')) //aa, undefined, 此时this指向window
Function.prototype.bind2 = function(context) {
    var func = this // 这里的this指向getName函数
    var bindArgs = Array.prototype.slice.call(arguments, 1) // 获取context后面传递的参数
    return function() {
        var args = Array.prototype.slice.call(arguments) // 获取实际调用时候的参数
        return func.apply(context, bindArgs.concat(args))
    }
}
var newFunc1 = getName.bind2(foo)
console.log(newFunc1('aa')) //aa, 17, 此时this指向window
var newFunc2 = getName.bind2(foo, 'aa')
console.log(newFunc2()) //aa, 17, 此时this指向window

2.假如上面getName函数在改变作用域后生成的函数,需要用作为构造函数,然后用new操作符来生成对象,先看原生的实现效果

var foo2 = {
    value: 'test'
}
function GetName(name) {
    console.log(this.value)
    this.name = name
}
var Func3 = GetName.bind(foo2)
var newObj = new Func3('aa')
console.log( newObj.name, newObj instanceof GetName) //  'aa', true

3.再看用bind2来实现

var Func4 = GetName.bind2(foo2)
var newObj2 = new Func4('aa') // 此时new操作符生成的对象的this指针指向的是newObj2这个对象
console.log(newObj2) // {} 空对象,并没有name等属性,反而出现下面foo2的属性得到扩展
/**
 * 原因:在new操作符的时候,bind2返回的函数体里面是func.apply(context, bindArgs.concat(args))这句
 * 而context是foo2,所以是扩展的是foo2,并没有扩展newObj2
 */
console.log(foo2) // {name: 'aa',value:'test',newValue:'test'}

4. 对bind2进行优化

Function.prototype.bind3 = function(context) {
    var func = this
    var bindArgs = Array.prototype.slice.call(arguments, 1)
    var bound = function() {
        var args = Array.prototype.slice.call(arguments)
        return func.apply(this instanceof bound ? this : context, bindArgs.concat(args))
    }
    return bound
}
var Func5 = GetName.bind3(foo2)
var newObj3 = new Func5('aa')
console.log(newObj3) // bound{name:'aa'}

5.如果GetName的prototype原型对象上有属性?

GetName.prototype.say = function() {
    return this.name
}
var Func6 = GetName.bind3(foo2)
var newObj4 = new Func6('aa')
console.log(newObj4, newObj4.say) // bound{name:'aa'}, undefined 可以看出say方法并没有得到继承

6.再次对bind3进行优化

Function.prototype.bind4 = function(context) {
    var func = this
    var bindArgs = Array.prototype.slice.call(arguments, 1)
    var bound = function() {
        var args = Array.prototype.slice.call(arguments)
        return func.apply(this instanceof bound ? this : context, bindArgs.concat(args))
    }
    bound.prototype = Object.create(this.prototype)
    return bound
}
var Func7 = GetName.bind4(foo2)
var newObj5 = new Func7('aa')
console.log(newObj5, newObj5.say) // bound{name:'aa'}, function(){} 

7.总结bind方法

;(function(global, factory){
    return factory.call(global)
})(this, function(){
    var selfBind = {}
    selfBind.bind = function(funcName, context) {
        var func = funcName
        var bindArgs = Array.prototype.slice.call(arguments, 2)
        var bound = function() {
            var args = Array.prototype.slice.call(arguments)
            return func.apply(this instanceof bound ? this : context, bindArgs.concat(args))
        }
        bound.prototype = Object.create(func.prototype)
        return bound
    }
    this.selfBind = selfBind
})

// 使用

        var person = {
            name: 'rose',
            age: 18
        }
        function Person(name, age) {
            this.name = name
            this.age = age
        }
        Person.prototype.say = function() {
            return this.name
        }
        var bound = selfBind.bind(Person, person)
        var obj = new bound('kobe', 22)
        console.log(obj)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值