Function.prototype.bind 函数,参见ECMA规范地址

如题,这次来实现一个boundFunction函数,不挂载在Function.prototype上,而是一个单独声明的函数。挂载在原型上的bind,可以参考MDN

主要步骤,摘自ECMA规范,如图:

实现思想:当然是依葫芦画瓢,这里,我们借用ES6的...运算符与解构赋值。目的是图省事,实现bind函数,主要是了解其内部的操作流程。

首先,把需要用到的函数,都依照规范声明实现,其中FunctionIsConstructor是自己写的判断一个函数是否为构造函数,比如Proxy就不是构造函数。

而SetFunctionLength是对设置函数length属性的操作的封装,正如其名。

  1. function FunctionIsConstructor(fnc) {
  2. let isConstructor = true;
  3. try {
  4. Object instanceof fnc
  5. } catch (e) {
  6. if (e instanceof TypeError) {
  7. isConstructor = false
  8. }
  9. }
  10. return isConstructor
  11. }
  12. function BoundFunctionCreate(targetFunction, boundThis, boundArgs) {
  13. let proto = Object.getPrototypeOf(targetFunction);
  14. let boundFunction = function () {
  15. if (new.target) {
  16. // 实现构造函数功能
  17. if (FunctionIsConstructor(targetFunction)) {
  18. return new targetFunction(...boundArgs)
  19. } else {
  20. throw new TypeError(`${arguments.callee.name} is not a constructor`)
  21. }
  22. } else {
  23. // 实现函数调用功能
  24. return targetFunction.call(boundThis, [...boundArgs, ...arguments])
  25. }
  26. }
  27. delete boundFunction.name;
  28. Object.setPrototypeOf(boundFunction, proto)
  29. return boundFunction;
  30. }
  31. function isCallable(Target) {
  32. if (typeof Target === 'function') return true;
  33. return false;
  34. }
  35. function ToInteger(arg) {
  36. let number = Number(arg);
  37. if (number !== number) return +0;
  38. if (number === 0 || number === Infinity || number === -Infinity) return number;
  39. return Math.floor(Math.abs(number));
  40. }
  41. function SetFunctionName(F, name, prefix) {
  42. if (typeof name === 'symbol') {
  43. let description = name.description
  44. if (description === undefined) {
  45. name = ''
  46. } else {
  47. name = `[${description}]`
  48. }
  49. }
  50. if (prefix) {
  51. name = `${prefix} ${name}`
  52. }
  53. return Object.defineProperty(F, 'name', {
  54. value: name,
  55. writable: false,
  56. enumerable: false,
  57. configurable: true
  58. })
  59. }
  60. function SetFunctionLength(F, Target, args) {
  61. let targetHasLength = Target.hasOwnProperty('length');
  62. let L;
  63. if (targetHasLength) {
  64. let targetLen = Target.length;
  65. if (typeof targetLen !== 'number') {
  66. L = 0;
  67. } else {
  68. targetLen = ToInteger(targetLen)
  69. L = Math.max(0, targetLen - args.length)
  70. }
  71. } else {
  72. L = 0;
  73. }
  74. Object.defineProperty(F, 'length', {
  75. value: L,
  76. writable: false,
  77. enumerable: false,
  78. configurable: true
  79. })
  80. }

然后,把这些函数按照规范的流程,组装起来,完全对应。

  1. function boundFuntion(targetFunction, thisArg, ...args) {
  2. let Target = targetFunction;
  3. if (!isCallable(Target)) {
  4. throw new TypeError(`${Target.name}.bind is not a function`)
  5. }
  6. let F = BoundFunctionCreate(Target, thisArg, args);
  7. SetFunctionLength(F, Target, args)
  8. let targetName = Target.name
  9. if (typeof targetName !== 'string') targetName = '';
  10. SetFunctionName(F, targetName, 'bound')
  11. // 支持直接new调用创建的绑定函数
  12. return new.target ? new F() : F
  13. }

如此,一个手写的bind函数就出来。函数最后一行,用new.target来判断,以支持直接使用new调用创建的绑定函数,如new boundFunction(fnc)

最后,简单测试一下。

  1. var modules = {
  2. x: 42,
  3. getX: function() {
  4. console.log('this', this === modules, this.x)
  5. return this.x;
  6. }
  7. }
  8.  
  9. var unboundGetX = modules.getX;
  10. console.log('unbounnd ', unboundGetX()); // The function gets invoked at the global scope
  11. // // expected output: unbounnd undefined
  12.  
  13. var boundGetX = boundFuntion(unboundGetX, modules);
  14. console.log('bounnd ', boundGetX());
  15. // expected output: bounnd 42

总结

手写bind函数,主要是利用闭包功能,将传入的this固定在新函数里,并对原型链进行处理,以免丢失继承关系。

其他的错误处理,比如判断是否构造函数,是否用new调用当前函数,也是值得去了解的。

依据ECMA规范,手写一个bind函数的更多相关文章

  1. 手写Function.bind函数

    if(!Function.prototype.bind){ Function.prototype.bind = function(oThis){ if(typeof this !=="fun ...

  2. 手写一个bind

    1 Function.prototype.bind1 = function(){ 2 // 将类数组转化成数组 3 let arr = Array.prototype.slice.call(argum ...

  3. 手写简化版printf函数

    2019.02.01更新:经同学提醒,myprintf函数应有返回值为输出的字符数. 期末的大作业,手写一个myprintf函数,支持如下一些操作. 也就是  % -(负号控制左右对齐) 数(控制字段 ...

  4. 只会用就out了,手写一个符合规范的Promise

    Promise是什么 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Prom ...

  5. 剖析手写Vue,你也可以手写一个MVVM框架

    剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...

  6. 『练手』手写一个独立Json算法 JsonHelper

    背景: > 一直使用 Newtonsoft.Json.dll 也算挺稳定的. > 但这个框架也挺闹心的: > 1.影响编译失败:https://www.cnblogs.com/zih ...

  7. 手写事件代理函数 (Delegated function)

    ‘手写 ’ 这个词 ,面试是不是听过无数遍呢 ! 今天我们来手写一个这样的事件委托函数 => function( parent, selector, type ,  handle)  {} 你需 ...

  8. 看年薪50W的架构师如何手写一个SpringMVC框架

    前言 做 Java Web 开发的你,一定听说过SpringMVC的大名,作为现在运用最广泛的Java框架,它到目前为止依然保持着强大的活力和广泛的用户群. 本文介绍如何用eclipse一步一步搭建S ...

  9. 用过消息队列?Kafka?能否手写一个消息队列?懵

    是否有同样的经历?面试官问你做过啥项目,我一顿胡侃,项目利用到了消息队列,kafka,rocketMQ等等. 好的,那请开始你的表演,面试官递过一支笔:给我手写一个消息队列!!WHAT? 为了大家遇到 ...

随机推荐

  1. php页面传值的方法(转)

    原文链接:https://www.cnblogs.com/suvllian/p/5582540.html   PHP页面间传值的几种方法 方法一:require_once //Page a: < ...

  2. P1991 无线通讯网 最小生成树

    题目描述 国防部计划用无线网络连接若干个边防哨所.2 种不同的通讯技术用来搭建无线网络: 每个边防哨所都要配备无线电收发器:有一些哨所还可以增配卫星电话. 任意两个配备了一条卫星电话线路的哨所(两边都 ...

  3. sql语句表连接删除

    DELETE 表1,表2FROM 表1 LEFT JOIN 表2 ON 表1.id=表2.id WHERE 表1.id=需要删除的ID

  4. Wxpython入门

    Wxpython入门 api文档以及中文教程: https://pan.baidu.com/s/1TDTgHg9Mwc74ODQy68YnlQ 提取码:354n 入门示例 frame=wx.Frame ...

  5. Pytorch学习(一)基础语法篇

    how to use pytorch 1.Tensor we can create a tensor just like creating a matrix the default type of a ...

  6. 【java】-- java并发包总结

    1.同步容器类 1.1.Vector与ArrayList异同 1.Arraylist和Vector都是采用数组方式存储数据,都允许直接序号索引元素,所以查找速度快,但是插入数据等操作涉及到数组元素移动 ...

  7. CodeForces - 1013C C - Photo of The Sky 贪心

    题目链接: https://vjudge.net/problem/1735276/origin 题目大意与思路: 题目的基本意思就是求一个矩形的最小面积. 这个可以用最大最小值, 将他们分为X和Y组. ...

  8. 编程菜鸟的日记-初学尝试编程-C++ Primer Plus 第6章编程练习3

    #include <iostream> using namespace std; void showmenu(void) { cout<<"Please enter ...

  9. HTML5_新标签

    HTML5 是定义 HTML 标准的最新版本. 是一个新版本的 HTML 语言,具有新的元素,属性,行为, 是一个技术及,允许更多样化和强大的网站和应用程序 优势: 跨平台: 通吃 MAC PC Li ...

  10. 08_ for 练习 _ sumOf7

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...