今天来实现JavaScript的bind函数。
首先看MDN的bind函数描述:

从上面可以看出来,var A = B.bind(this)函数其实干了这几件事情:

  1. 返回一个函数,且这个函数后面运行时的this就是bind(this)传入的this
  2. 接收参数,这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面
  3. 使用new操作bind函数返回的函数时,之前传入的this会被忽略,也就是说new的优先级高于bind

第一步

首先实现第一步:

Function.prototype.Zbind = function (othis) {var originFunc = this;return function () {originFunc.apply(othis);}
}var obj = {}function createAgumon() {this.name = "agumon";
}
var createAgumonBind = createAgumon.Zbind(obj);
createAgumonBind();
obj;// {name: "agumon"}

第二步

第二步考虑传参的问题,首先看看原生的bind函数是如何传参的:

var obj = {}
function createAgumon(gender, age) {this.name = "agumon";this.gender = gender;this.age = age;
}
var createAgumonBind = createAgumon.bind(obj, 'female');
createAgumonBind(22);

可以看出来在bind函数中能先传部分参数,运行bind返回的函数时可以再传入部分参数。
自己实现:

Function.prototype.Zbind = function (othis) {var originFunc = this;var partArgs = [].slice.call(arguments, 1);var func = function() {};var boundFunc = function () {var finalArgs = partArgs.concat([].slice.call(arguments));return originFunc.apply(othis, finalArgs);}return boundFunc;
}var obj = {}function createAgumon(gender, age) {this.name = "agumon";this.gender = gender;this.age = age;
}var createAgumonBind = createAgumon.Zbind(obj, 'female');
createAgumonBind(22);
obj;// {name: "agumon", gender: "female", age: 22}

第三步

使用new来调用bind返回的函数时,会忽略bind传入的this
new操作和普通的函数调用有哪些区别?
粗略的来讲,例如new F()这样的调用,有以下几个步骤:

  1. 新建一个对象,var o = new Object()
  2. 设置原型链,o.__proto__ = F.prototype
  3. 把F函数体内的this绑定为o,并且执行F函数的代码
  4. 判断F的返回类型:
    如果是值类型,则返回o
    如果是引用类型,则返回该引用类型对象

开始实现:

Function.prototype.Zbind = function (othis) {var originFunc = this;var partArgs = [].slice.call(arguments, 1);var func = function() {};var boundFunc = function () {var finalArgs = partArgs.concat([].slice.call(arguments));return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs);}return boundFunc;
}var obj = {}function createAgumon(gender, age) {this.name = "agumon";this.gender = gender;this.age = age;
}var createAgumonBind = createAgumon.Zbind(obj, 'female');
var newObj = new createAgumonBind(22);
obj // {}
newObj // {name: "agumon", gender: "female", age: 22}

关键的地方在于这里:this instanceof boundFunc ? this : othis,如果是new操作的话,此时this的__proto__已经指向了boundFunc,所以使用instanceof可以检测出是否在使用new操作

小细节

原型丢失
刚刚实现的Zbind方法有个小问题:

Function.prototype.Zbind = function (othis) {var originFunc = this;var partArgs = [].slice.call(arguments, 1);var func = function() {};var boundFunc = function () {var finalArgs = partArgs.concat([].slice.call(arguments));return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs);}return boundFunc;}var obj = {}function createAgumon(gender, age) {this.name = "agumon";this.gender = gender;this.age = age;}createAgumon.prototype.college = 'THU'var createAgumonBind = createAgumon.Zbind(obj, 'female');var newObj = new createAgumonBind(22);console.log(newObj.college)// undefined

可以看出来原型链丢失了,newObj.college得是'THU'才行

修改:

Function.prototype.Zbind = function (othis) {var originFunc = this;var partArgs = [].slice.call(arguments, 1);var func = function() {};var boundFunc = function () {var finalArgs = partArgs.concat([].slice.call(arguments));return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs);}func.prototype = originFunc.prototype;boundFunc.prototype = new func();return boundFunc;}var obj = {}function createAgumon(gender, age) {this.name = "agumon";this.gender = gender;this.age = age;}createAgumon.prototype.college = 'THU'var createAgumonBind = createAgumon.Zbind(obj, 'female');var newObj = new createAgumonBind(22);console.log(newObj.college)// 'THU'

为什么要使用func.prototype = originFunc.prototype;boundFunc.prototype = new func();,而不是直接用boundFunc.prototype = originFunc.prototype;是因为这样写的话,修改boundFunc.prototype会影响到原函数的prototype。

that'all

参考资料:
mdn-bind
javascript中,new操作符的工作原理是什么?

【build your own xxx】实现你自己的bind函数相关推荐

  1. ERROR: Could not build wheels for XXX, which is required to install pyproject.toml-based projects

    报错内容: ERROR: Could not build wheels for pynacl, which is required to install pyproject.toml-based pr ...

  2. 无法将“XXX”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。 对这个问题的解决方法

    我在windows里用powershell跑ps1格式的文件,会报如下的错误. 无法将"XXX"项识别为 cmdlet.函数.脚本文件或可运行程序的名称. 首先我用powershe ...

  3. nothing to build for project 'XXX'

    有时候使用Eclipse CDT或者CCS 写程序时出现 nothing to build for project  的错误,这是因为新建的项目类型和实际的源代码类型不一致造成的,比如你新建了C Pr ...

  4. 无法将“xxx”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。

    在学习国产某芯片时,跟着b站某视频搭建编译环境,如题报错,找了一天错误,试了网上能找到的所有方法,皆无果,后面发现是某视频的字幕有问题,字幕中命令行为.\install.psl xxxx,实际因为.\ ...

  5. 全网详细解决:无法将 “xxx” 项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次

    文章目录 1. 复现问题 2. 分析问题 3. 解决问题 1. 复现问题 今天,使用如下命令在Windows PowerShell中执行时: telnet 127.0.0.1 80 却报出如下错误: ...

  6. 【解决】无法将“XXX”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次

    进入PowerShell 模式 Get-ExecutionPolicy -List 查看当前所有作用域 上图显示就最后一个作用域有权限,其他作用域都没有权限,那么我们就需要去给它设置权限 设置权限 S ...

  7. 无法将“xxx”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

    Win 键 + Q ,在搜索框内输入 Powershell . 点击以管理员身份运行. 输入代码 set-executionpolicy remotesigned 按回车键执行命令. 输入 A,按回车 ...

  8. [转]Nant daily build实践

    本文转自:http://www.cnblogs.com/moonvan/archive/2006/11/07/552585.html 折腾了一个周,基于Nant的VS.NET项目每日构建终于成功了,在 ...

  9. iOS build faad

    1.新建目录xxx,下载faad(http://downloads.sourceforge.net/faac/faad2-2.7.tar.gz)到xxx,解压,重命名为faad. 2.编写build. ...

最新文章

  1. 【资源分享】数字图像处理MATLAB版冈萨雷斯+中文高清版+随书源码链接
  2. 安装SAP Business One对软硬件有哪些要求
  3. initializeCachedDB function in JavaScript - how is the call delegated to
  4. html相对定位向上偏移,使用CSS的相对定位和偏移量
  5. asp.net接受表单验证格式后再提交数据_看滴普科技大前端如何玩转el-form-renderer 表单渲染器1.14.0
  6. openfeign seata事务不回滚_Spring,你为何中止我的事务?
  7. 猎豹网校C++ Primer学习笔记
  8. NRF52840 USB串口例程
  9. vmware虚拟机WinXp sp3的系统cpu占用100%的解决方案
  10. ECCV2022 | 网易AI 基于单幅图片的实时高分辨率人脸重演算法
  11. 微信小程序之小程序审核
  12. 成大事,赚大钱,都要有股永不服输的精神
  13. 怎么关闭Windows7显示器校准?
  14. 术语FXO和FXS的含义是什么?
  15. Android应用之——微信微博第三方sdk登录分享使用过程中的一些常见问题
  16. 深入理解时间和空间复杂度
  17. 四足步行机器人的结构设计及仿真
  18. 发明计算机的人的名人名言,60句关于发明的名言
  19. 分库分表之MyCat详解
  20. MTI雷达原理的学习

热门文章

  1. Struts2结果页面配置(Result)
  2. wireshark 抓包分析 TCPIP协议的握手
  3. 09.显式的实现接口的方法
  4. CSS3背景图片百分比及应用
  5. Python md5 sha1 的使用
  6. Python模块之optparse
  7. Linux ISATAP配置
  8. Android基础教程(六)之------- 参数的传递(Bundle)
  9. 【Java】身份证的验证
  10. ggplot2 | 图例(Ⅰ):图例函数、主题函数中的图例参数