依据ECMA规范,手写一个bind函数
Function.prototype.bind 函数,参见ECMA规范地址
如题,这次来实现一个boundFunction函数,不挂载在Function.prototype上,而是一个单独声明的函数。挂载在原型上的bind,可以参考MDN
主要步骤,摘自ECMA规范,如图:
实现思想:当然是依葫芦画瓢,这里,我们借用ES6的...运算符与解构赋值。目的是图省事,实现bind函数,主要是了解其内部的操作流程。
首先,把需要用到的函数,都依照规范声明实现,其中FunctionIsConstructor是自己写的判断一个函数是否为构造函数,比如Proxy就不是构造函数。
而SetFunctionLength是对设置函数length属性的操作的封装,正如其名。
- function FunctionIsConstructor(fnc) {
- let isConstructor = true;
- try {
- Object instanceof fnc
- } catch (e) {
- if (e instanceof TypeError) {
- isConstructor = false
- }
- }
- return isConstructor
- }
- function BoundFunctionCreate(targetFunction, boundThis, boundArgs) {
- let proto = Object.getPrototypeOf(targetFunction);
- let boundFunction = function () {
- if (new.target) {
- // 实现构造函数功能
- if (FunctionIsConstructor(targetFunction)) {
- return new targetFunction(...boundArgs)
- } else {
- throw new TypeError(`${arguments.callee.name} is not a constructor`)
- }
- } else {
- // 实现函数调用功能
- return targetFunction.call(boundThis, [...boundArgs, ...arguments])
- }
- }
- delete boundFunction.name;
- Object.setPrototypeOf(boundFunction, proto)
- return boundFunction;
- }
- function isCallable(Target) {
- if (typeof Target === 'function') return true;
- return false;
- }
- function ToInteger(arg) {
- let number = Number(arg);
- if (number !== number) return +0;
- if (number === 0 || number === Infinity || number === -Infinity) return number;
- return Math.floor(Math.abs(number));
- }
- function SetFunctionName(F, name, prefix) {
- if (typeof name === 'symbol') {
- let description = name.description
- if (description === undefined) {
- name = ''
- } else {
- name = `[${description}]`
- }
- }
- if (prefix) {
- name = `${prefix} ${name}`
- }
- return Object.defineProperty(F, 'name', {
- value: name,
- writable: false,
- enumerable: false,
- configurable: true
- })
- }
- function SetFunctionLength(F, Target, args) {
- let targetHasLength = Target.hasOwnProperty('length');
- let L;
- if (targetHasLength) {
- let targetLen = Target.length;
- if (typeof targetLen !== 'number') {
- L = 0;
- } else {
- targetLen = ToInteger(targetLen)
- L = Math.max(0, targetLen - args.length)
- }
- } else {
- L = 0;
- }
- Object.defineProperty(F, 'length', {
- value: L,
- writable: false,
- enumerable: false,
- configurable: true
- })
- }
然后,把这些函数按照规范的流程,组装起来,完全对应。
- function boundFuntion(targetFunction, thisArg, ...args) {
- let Target = targetFunction;
- if (!isCallable(Target)) {
- throw new TypeError(`${Target.name}.bind is not a function`)
- }
- let F = BoundFunctionCreate(Target, thisArg, args);
- SetFunctionLength(F, Target, args)
- let targetName = Target.name
- if (typeof targetName !== 'string') targetName = '';
- SetFunctionName(F, targetName, 'bound')
- // 支持直接new调用创建的绑定函数
- return new.target ? new F() : F
- }
如此,一个手写的bind函数就出来。函数最后一行,用new.target来判断,以支持直接使用new调用创建的绑定函数,如new boundFunction(fnc)
最后,简单测试一下。
- var modules = {
- x: 42,
- getX: function() {
- console.log('this', this === modules, this.x)
- return this.x;
- }
- }
- var unboundGetX = modules.getX;
- console.log('unbounnd ', unboundGetX()); // The function gets invoked at the global scope
- // // expected output: unbounnd undefined
- var boundGetX = boundFuntion(unboundGetX, modules);
- console.log('bounnd ', boundGetX());
- // expected output: bounnd 42
总结
手写bind函数,主要是利用闭包功能,将传入的this固定在新函数里,并对原型链进行处理,以免丢失继承关系。
其他的错误处理,比如判断是否构造函数,是否用new调用当前函数,也是值得去了解的。
依据ECMA规范,手写一个bind函数的更多相关文章
- 手写Function.bind函数
if(!Function.prototype.bind){ Function.prototype.bind = function(oThis){ if(typeof this !=="fun ...
- 手写一个bind
1 Function.prototype.bind1 = function(){ 2 // 将类数组转化成数组 3 let arr = Array.prototype.slice.call(argum ...
- 手写简化版printf函数
2019.02.01更新:经同学提醒,myprintf函数应有返回值为输出的字符数. 期末的大作业,手写一个myprintf函数,支持如下一些操作. 也就是 % -(负号控制左右对齐) 数(控制字段 ...
- 只会用就out了,手写一个符合规范的Promise
Promise是什么 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Prom ...
- 剖析手写Vue,你也可以手写一个MVVM框架
剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...
- 『练手』手写一个独立Json算法 JsonHelper
背景: > 一直使用 Newtonsoft.Json.dll 也算挺稳定的. > 但这个框架也挺闹心的: > 1.影响编译失败:https://www.cnblogs.com/zih ...
- 手写事件代理函数 (Delegated function)
‘手写 ’ 这个词 ,面试是不是听过无数遍呢 ! 今天我们来手写一个这样的事件委托函数 => function( parent, selector, type , handle) {} 你需 ...
- 看年薪50W的架构师如何手写一个SpringMVC框架
前言 做 Java Web 开发的你,一定听说过SpringMVC的大名,作为现在运用最广泛的Java框架,它到目前为止依然保持着强大的活力和广泛的用户群. 本文介绍如何用eclipse一步一步搭建S ...
- 用过消息队列?Kafka?能否手写一个消息队列?懵
是否有同样的经历?面试官问你做过啥项目,我一顿胡侃,项目利用到了消息队列,kafka,rocketMQ等等. 好的,那请开始你的表演,面试官递过一支笔:给我手写一个消息队列!!WHAT? 为了大家遇到 ...
随机推荐
- php页面传值的方法(转)
原文链接:https://www.cnblogs.com/suvllian/p/5582540.html PHP页面间传值的几种方法 方法一:require_once //Page a: < ...
- P1991 无线通讯网 最小生成树
题目描述 国防部计划用无线网络连接若干个边防哨所.2 种不同的通讯技术用来搭建无线网络: 每个边防哨所都要配备无线电收发器:有一些哨所还可以增配卫星电话. 任意两个配备了一条卫星电话线路的哨所(两边都 ...
- sql语句表连接删除
DELETE 表1,表2FROM 表1 LEFT JOIN 表2 ON 表1.id=表2.id WHERE 表1.id=需要删除的ID
- Wxpython入门
Wxpython入门 api文档以及中文教程: https://pan.baidu.com/s/1TDTgHg9Mwc74ODQy68YnlQ 提取码:354n 入门示例 frame=wx.Frame ...
- Pytorch学习(一)基础语法篇
how to use pytorch 1.Tensor we can create a tensor just like creating a matrix the default type of a ...
- 【java】-- java并发包总结
1.同步容器类 1.1.Vector与ArrayList异同 1.Arraylist和Vector都是采用数组方式存储数据,都允许直接序号索引元素,所以查找速度快,但是插入数据等操作涉及到数组元素移动 ...
- CodeForces - 1013C C - Photo of The Sky 贪心
题目链接: https://vjudge.net/problem/1735276/origin 题目大意与思路: 题目的基本意思就是求一个矩形的最小面积. 这个可以用最大最小值, 将他们分为X和Y组. ...
- 编程菜鸟的日记-初学尝试编程-C++ Primer Plus 第6章编程练习3
#include <iostream> using namespace std; void showmenu(void) { cout<<"Please enter ...
- HTML5_新标签
HTML5 是定义 HTML 标准的最新版本. 是一个新版本的 HTML 语言,具有新的元素,属性,行为, 是一个技术及,允许更多样化和强大的网站和应用程序 优势: 跨平台: 通吃 MAC PC Li ...
- 08_ for 练习 _ sumOf7
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...