02-bind的深度解析和实现

#bind特征#

由第一章总结了bind函数有以下几点特征:

  • bind返回的是一个改变了this指向的函数
  • bind传入的也是参数列表,但是可以多次传入
  • 当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)
  • 原函数的原型链上的属性,new出来的结果也可以找到

#代码示例# 

// 测试一下
var value = 2;
 
var obj = {
    value: 1
}
 
function bar(name, age) {
    console.log(this.value);
    return {
        value: this.value,
        name: name,
        age: age
    }
}

const bindFun = bar.bind(null, 'zhangguan');
 
// bind 返回的函数 作为普通函数调用
// 输入
let a = bindFun('29'); 
console.log(a)
 
// 输出
{
    value: 2, 
    name: 'zhangguan', 
    age: '29'
}
 
// bind 返回的函数 作为构造函数调用,绑定的 this 值obj会失效,this指向实例对象a
// 输入
let a = new bindFun('29'); 
console.log(a)
 
// 输出
{
    value: undefined, 
    name: 'zhangguan', 
    age: '29'
}

#bind实现步骤# 

1、定义一个bind函数,并return 返回一个函数

// bind 返回的函数 作为普通函数调用
Function.prototype.bind = function (context, ...args){
    // bind返回的函数  
    return function (...innerArgs) {
        // bind 返回的函数 作为普通函数被执行
    }
}

2、bind传入的也是参数列表,但是可以多次传入 

// bind 返回的函数 作为普通函数调用
Function.prototype.bind = function (context, ...args){
    // 如果第一个参数传入的是undefined和null,context为window对象
    let ctx = context || window;
    let fn = Symbol();
    ctx[fn] = this;
    
    // bind返回的函数  
    return function (...innerArgs) {
        // 对外层参数和后续追加参数进行拼接,外层参数在前
        return ctx[fn](...[...args,...innerArgs]);
    }
}

3、 此时基本上快满足bind的实现,但是还有最后一点特征:当 bind 返回的函数,使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效,目前第二步代码执行后,通过以下两个示例调用发现this的指向并没有失效,value值不等于undefined

const bindFun = bar.bind(null, 'zhangguan');

// 函数调用
let a = bindFun('29'); 

// 输出
{
    value: 2, 
    name: 'zhangguan', 
    age: '29'
}

// 使用new作为构造函数
let a = new bindFun('29'); 

// 输出
{
    value: 2, 
    name: 'zhangguan', 
    age: '29'
}

如何判断是new作为构造函数的场景,可以通过如下代码:

// bind 返回的函数 作为普通函数调用
Function.prototype.bind = function (context, ...args){
    // 如果第一个参数传入的是undefined和null,context为window对象
    let cxt = context || window;
    let fn = Symbol();
    cxt[fn] = this; // this:Bar

    // bind返回的函数  
    var fbound = function (...innerArgs) {
        if(this instanceof fbound) { // this:a (new出来的实例对象)
            // 为实例对象a添加Person方法
            this.fn = cxt[fn];
            // 实例对象a执行Person方法
            return this.fn(...[...args,...innerArgs]);
        } else {
            // 对外层参数和后续追加参数进行拼接,外层参数在前
            return cxt[fn](...[...args,...innerArgs]);
        }
    }; 
    return fbound;
}

4、 原函数的原型链上的属性,new出来的结果也可以找到,最终代码实现如下:

// bind 返回的函数 作为普通函数调用
Function.prototype.bind = function (context, ...args){
    // 如果第一个参数传入的是undefined和null,context为window对象
    let cxt = context || window;
    let fn = Symbol();
    cxt[fn] = this; // this:Bar

    // Object.create()原理,创建一个空白函数来做中间件
    function Fn() {};

    // bind返回的函数  
    var fbound = function (...innerArgs) {
        if(this instanceof fbound) { // this:a (new出来的实例对象)
            // 为实例对象a添加Person方法
            this.fn = cxt[fn];
            // 实例对象a执行Person方法
            return this.fn(...[...args,...innerArgs]);
        } else {
            // 对外层参数和后续追加参数进行拼接,外层参数在前
            return cxt[fn](...[...args,...innerArgs]);
        }
    }; 

    Fn.prototype = this.prototype;
    // 通过原型链找到原函数中的属性
    fBound.prototype = new Fn();

    return fbound;
}
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端-张冠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值