模拟实现Javascript中的bind函数

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

从MDN对于bind的描述来看:

  • 返回值是一个函数,而不是执行结果
  • this值会指向第一个参数
  • 其余参数会作为新函数的参数

看个例子:

function test(name, age) {
    console.log(this.name);
    this.name = name;
    this.age = age;
    console.log(this.name, this.age);
}

var obj = {
    name: 'Willem'
};

var bindFn = test.bind(obj, 'Wei');
bindFn(18);
// Willem
// Wei, 18

从上面的代码就可以看出来,bind函数执行之后,bindFn的this值指向了obj,并且在bind的时候传入的参数和在执行bindFn时传入的参数都成功的传入了test函数。

那代码就呼之欲出了啊。

Function.prototype.wbind = function() {
    var context = [].shift.call(arguments);
    var args = [].slice.call(arguments);
    var self = this;

    return function() {
        var innerArgs = [].slice.call(arguments);
        
        self.apply(context, args.concat(innerArgs));
    }
}

相关:模拟实现Javascript中的call和apply

既然bind返回的是一个函数,那我有一个大胆的想法,如果我把这个返回的函数作为一个构造函数会怎样呢?改造一下上面的那个例子:

function test(name, age) {
    console.log(this.name);
    this.name = name;
    this.age = age;
    console.log(this.name, this.age);
}

test.prototype.sayHi = function() {
    console.log('Hi, ' + this.name);
}

var obj = {
    name: 'Willem'
};

var bindFn = test.bind(obj, 'Wei');
var instance = new bindFn(18);
// undefined
// Wei,18
instance.sayHi(); // Hi, Wei
console.log(obj.name); // Willem

咦,obj对象里面明明是有name属性的啊,为啥第一次输出的是undfined呢?明明传入了name属性为"Wei",为啥obj.name还是"Willem"呢?

其实是因为this并没有指向obj了,而是指向了instance。总结一下,将返回的函数作为普通函数使用时,函数的this指向bind执行时传入的第一个参数;将返回的函数作为构造函数使用时,函数的this指向实例,并且该实例将会继承原函数原型上的属性和方法。

这时候,我们再来改一改wbind函数

Function.prototype.wbind = function() {
    var context = [].shift.call(arguments);
    var args = [].slice.call(arguments);
    var self = this;

    var fBound = function() {
        var innerArgs = [].slice.call(arguments);
        // 做构造函数时,this指向实例
        self.apply(this instanceof fBound ? this : context, args.concat(innerArgs));
    }
    
    // 实例需要继承原函数原型上的方法和属性
    // 使用fNOP中转一次是因为直接将this.prototype赋值到fNOP.prototype时
    // 当修改fNOP的prototype时,this.prototype也会被修改
    var fNOP = function() {}
    if (this.prototype) {
        fNOP.prototype = this.prototype;
    }
    
    // fBound.prototype = { __proto__: { this.prototype } }
    // 相当于是中间多了一个__proto__,因为原型链的缘故,所以多一层__proto__没有什么影响
    fBound.prototype = new fNOP(); 
    
    return fBound;
}

相关:
模拟实现js中的new操作符
简单说说原型和原型链
使用ts模拟实现js中的一些函数和属性

以上,就是bind的相关内容。

1 篇内容引用
491 声望
36 粉丝
0 条评论
推荐阅读
前端开发中图片的优化
1. 常用的图片格式PNG优点:背景可以透明、画质很高、不失真缺点:体积很大使用场景:比较小或者需要部分透明的图片,例如logo、图标等JPG/JPEG优点:压缩比高的情况下可以保留比较高的画质,相对PNG俩说体积会小...

WillemWei阅读 875

从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木140阅读 11.8k评论 10

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木60阅读 5.9k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.1k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木39阅读 7k评论 6

【关于Javascript】--- 正则表达式篇
基础知识一、元字符 {代码...} 二、量词 {代码...} 三、集合 字符类 {代码...} 四、分支 {代码...} 五、边界 开始结束 {代码...} 六、修饰符 {代码...} 七、贪婪模式和非贪婪模式js默认贪婪模式即最大可能的匹配...

Jerry35阅读 2.9k

从零搭建 Node.js 企业级 Web 服务器(二):校验
校验就是对输入条件的约束,避免无效的输入引起异常。Web 系统的用户输入主要为编辑与提交各类表单,一方面校验要做在编辑表单字段与提交的时候,另一方面接收表单的接口也要做足校验行为,通过前后端共同控制输...

乌柏木32阅读 6k评论 9

491 声望
36 粉丝
宣传栏