JavaScript专题之模拟实现bind

本文共 1100 字,读完只需 4 分钟

概述

前一篇文章我们尝试模拟实现了 call 和 apply 方法,其实 bind 函数也可以用来改变 this 的指向。bind 和 call和 apply 两者的区别在于,bind 会返回一个被改变了 this 指向的函数。

本文介绍如何模拟实现 bind 函数:

首先观察 bind 函数有什么特点:

var person = {
    name: 'jayChou'
}

function say(age, sex) {
    console.log(this.name, age, sex);
}

var foo = say.bind(person, '男', 39);

foo();  // jayChou 男 39
复制代码
  1. 返回一个函数
  2. 函数参数以逗号的形式传入
  3. 改变了 this 的指向
一、call 简单实现

既然 bind 内部也要用改变 this 指向,我们可以用现成的 call 函数来实现这一功能。

Function.prototype.newBind = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var self = this;
    return function () {
        return self.call(context)
      }
}
复制代码

说明一下:
if 判断是为了校验,只能让函数来调用此方法,并抛出错误。 第二个 return 是为了让返回的函数有返回值的功能。

测试一下:

var person = {
    name: 'jayChou'
};

var say = function() {
    console.log(this.name);
}

var foo = say.newBind(person);
foo();  // jayChou
复制代码

成功啦。

二、传递参数

刚才的函数是没有传递参数,当然不行,所以我们想办法把函数的参数也传递进去。

bind 函数有个特点,就是在绑定的时候可以传参,返回的函数还可以继续传参。

var person = {
    name: 'jayChou'
};

var say = function(p1, p2) {
    console.log(this.name, p1, p2);
}

var foo = say.bind(person, 18);
foo(20);  // jayChou 18 20
复制代码

还可以写成:

say.bind(person, 18)(20); // jayChou 18 20
复制代码

好,进入正题,考虑传参的事。在前面的文章,我们讲过 arguments 类数组对象的一些特性,不能直接调用数组的方法,但是可以用原型方法间接来调用,我们采用 apply 的方式来传递参数。

Function.prototype.newBind = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);  // 间接调用数组方法,获取第一次传的参数
    
    return function () {
        var innerArgs = Array.prototype.slice.call(arguments);
        return self.apply(context, args.concat(innerArgs))
      }
}


var person = {
    name: 'jayChou'
};

var say = function(p1, p2) {
    console.log(this.name, p1, p2);
}

var foo = say.newBind(person, 18);  // 第一次传参
foo(20);  // 第二次传参
复制代码

最后输出:

jayChou 18 20

结果正确,以上就完成了 bind 函数基本功能的实现。

但是!

三、作为构造函数时?

bind 函数其实还有一个非常重要的特点:

bind返回的函数如果作为构造函数,搭配new关键字出现的话,我们的绑定this就需要“被忽略”。

意思就是指:当使用 nuw 关键字把 bind 返回的函数作为构造函数,之前改变了指向的 this 就失效了。返回的函数的 this 就关联到了构造函数的实例对象上。

针对这个特点,需要再做一些修改:

Function.prototype.newBind = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);  // 间接调用数组方法,获取第一次传的参数
    
    let tempFn = function {};  // 利用一个空函数作为中转
    
    tempFn.prototype = this.prototype;  // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
    
    var resultFn = function () {
        var innerArgs = Array.prototype.slice.call(arguments);
        if (this instanceof tempFn) {  // 如果 返回函数被当做构造函数后,生成的对象是 tempFn 的实例,此时应该将 this 的指向指向创建的实例。
            return self.apply(this, args.concat(innerArgs));
        } else {
            return self.apply(context, args.concat(innerArgs))
        }
      }
      
    resultFn = new tempFn();
    return resultFn;
}
复制代码
总结

本文尝试模拟实现了 bind 函数,bind 函数与 call,apply 函数的区别在于,bind 函数返回一个指定了 this 的函数,函数并未执行。其次,当返回的函数作为构造函数时,之前绑定的 this 会失效。

欢迎关注我的个人公众号“谢南波”,专注分享原创文章。

掘金专栏 JavaScript 系列文章
  1. JavaScript之变量及作用域
  2. JavaScript之声明提升
  3. JavaScript之执行上下文
  4. JavaScript之变量对象
  5. JavaScript之原型与原型链
  6. JavaScript之作用域链
  7. JavaScript之闭包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值传递
  11. JavaScript之例题中彻底理解this
  12. JavaScript专题之模拟实现call和apply
  13. JavaScript专题之模拟实现bind
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值