JavaScript专题之模拟实现bind

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

概述

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

本文介绍如何模拟实现 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 的指向

1、call 简单实现

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

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 是为了让返回的函数有返回值的功能。函数

测试一下:post

var person = {
    name: 'jayChou'
};

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

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

成功啦。测试

2、传递参数

刚才的函数是没有传递参数,固然不行,因此咱们想办法把函数的参数也传递进去。ui

bind 函数有个特色,就是在绑定的时候能够传参,返回的函数还能够继续传参。this

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 函数基本功能的实现。

可是!

3、做为构造函数时?

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
相关文章
相关标签/搜索