【JavaScript高级】手写apply()、call()、bind()

2023-10-27

手写之前

我们有一个函数foo。
已知:

  • foo的隐式原型是绑定在Function的显式原型上的(Function是一个构造函数)
function foo(){}
console.log(foo.__proto__===Function.prototype);//true
  • apply、call、bind方法是来自Function的原型对象
console.log(Function.prototype.apply);//ƒ apply() { [native code] }
  • 也就是说:在Function的原型对象上添加的属性或方法, 可以被所有的函数获取

apply

获取thisArg, 我们要确保是一个对象类型,要进行边界判断

Function.prototype.MyApply = function(thisArg){
   thisArg=(thisArg===undefined||thisArg===null)?window:Object(thisArg);
};

将this绑定到传入的参数thisArg上

我们想实现apply显式绑定,就只能用:默认绑定、隐式绑定、new绑定来实现。(总不能自己实现自己)。

默认绑定肯定不是我们要的,因为显式绑定并不希望帮到window上。
隐式绑定可以。
new绑定要用到构造函数,略显复杂。

所以我们用隐式绑定

Function.prototype.MyApply = function(thisArg){
    thisArg=(thisArg===undefined||thisArg===null)?window:Object(thisArg);

    //这步
    //当读取thisArg.fn时返回this,相当于thisArg.fn=this
    //这里创建一个属性fn,它的值为this
    Object.defineProperty(thisArg,"fn",{
        configurable:true,//true,否则没法delete
        value:this,
    });

    //隐式绑定:让this指向thisArg
    thisArg.fn();

    //完成后删除thisArg中的fn属性
    delete thisArg.fn;
};

将剩余的其他参数传入thisArg

apply传入的其他参数是一个数组。

function foo(name, age) {
  console.log(this, name, age); // {name: 'kaisa'} 'kaisa' 18
}

Function.prototype.myapply = function (thisArg, arrArgs) {
  
  thisArg =thisArg === undefined || thisArg === null ? window : Object(thisArg);

  Object.defineProperty(thisArg, "fn", {
    configurable: true,
    value: this,
  });

  // 通过展开运算符, 将其他参数传入thisArg
  thisArg.fn(...arrArgs);

  delete thisArg.fn;
};

foo.myapply({ name: "kaisa" }, ["kaisa", 18]);

call

call传入的其他参数是参数列表

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

Function.prototype.MyCall = function (thisArg,...arrArgs) {
    thisArg = (thisArg === undefined || thisArg === null) ? window : Object(thisArg);

    Object.defineProperty(thisArg, "fn", {
        configurable: true,
        value: this,
    });
    
    thisArg.fn(...arrArgs);

    delete thisArg.fn;
};

foo.MyCall({name:"abc"},"abc", 18);

封装函数实现apply和call

显然,apply和call的函数有重复的代码。因此,我们可以把重复代码封装成一个函数。

function repFn(thisArg,otherArgs,fn){
    thisArg= (thisArg===null||thisArg===undefined)?window:Object(thisArg);

    Object.defineProperty(thisArg,"fn",{
        configurable: true,
        value: fn,
    })

    thisArg.fn(...otherArgs);

    delete thisArg.fn;
}

//apply
Function.prototype.MyApply=function(thisArg,otherArgs){
    repFn(thisArg,otherArgs,this);
}

//call
Function.prototype.MyCall=function(thisArg,...otherArgs){
    repFn(thisArg,otherArgs,this);
}

测试:

//测试
function foo1(name,age){
    console.log(this,name,age);
}

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

foo1.MyApply({name:"apply"},["apply",19]) //{name: 'apply', fn: ƒ} 'apply' 19
foo2.MyCall({name:"call"},"call",20)//{name: 'call', fn: ƒ} 'call' 20

bind

bind函数会返回一个新的函数,返回的新函数中可以传参数。

function foo(name,age,height){
    console.log(this,name,age,height);
}

Function.prototype.MyBind=function(thisArg,...otherArgs){
    thisArg= thisArg===null||thisArg===undefined?window:Object(thisArg);

    Object.defineProperty(thisArg,"fn",{
        configurable:true,
        value:this
    })

    return (...newArgs) =>{
        var allArgs=[...otherArgs,...newArgs];
        thisArg.fn(...allArgs);
    };
};

var newFoo=foo.MyBind({name:"bind"},"bind");
newFoo(18,1.88);//{name: 'bind', fn: ƒ} 'bind' 18 1.88

参考

coderwhy的课
手写apply-call-bind
手写 实现call、apply和bind方法 超详细!!!
使用JS简单实现一下apply、call和bind方法
Function

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【JavaScript高级】手写apply()、call()、bind() 的相关文章

随机推荐

  • STM32自带RTC时钟

    上面是我的微信和QQ群 欢迎新朋友的加入 没有使用32M的那个时钟 正确的应该是用那个的 现在的配置只是让RTC跑起来 生成工程 自动给我声明了两个结构体 打开结构体 看到的是一些跟时间相关的东西 找到四个函数 读取和设置时间日期 进入大循
  • XSS-labs-level4详解

    访问题目url 直接经典代码起手 查看界面回显发现表单中的尖括号都消失了 我们直接去看源代码
  • 单片机电子时钟的设计(期末课程设计)

    题目 单片机电子时钟的设计 设计一个时钟 可以正常显示时分秒 也可以通过按键改变分钟和小时 且有整点提醒功能 1 能够正常计时并且能够显示小时 分钟 秒 10分 1 正常显示秒 2分 2 正常显示分 2分 3 正常显示时 2分 4 秒进位至
  • ULAM公链第七十六期工作总结

    ULAM公链第七十六期工作总结 那些看上去光鲜的人背后一定经历过万千烦恼 没有谁的成功都是一蹴而就的 你受的委屈 摔的伤痕 背的冷眼 别人都有过 他们身上有光 是因为扛下了黑暗 生活给了一个人多少磨难 日后必会还给他多少幸运 为梦想颠簸的人
  • 设计模式之四 --- 建造(Builder)模式

    1 基本概念 建造 Builder 模式是一种对象构建的设计模式 它可以将复杂对象的建造过程抽象出来 抽象类别 使这个抽象过程的不同实现方法可以构造出不同表现 属性 的对象 2 简单分析 我们先来看一下该设计模式的UML结构图 上图是Str
  • align-content、justify-content、align-items三个属性的作用和效果

    一 align content属性 作用 设置同一列子元素在Y轴的对齐方式 属性值 描述 flex start 排列在当前列的最上方 flex end 排列在当前列的最下方 center 排列在当前列的中间位置 space between
  • C++学习(三十二)初始化列表

    从概念上来讲 构造函数的执行可以分成两个阶段 初始化阶段和计算阶段 初始化阶段先于计算阶段 初始化阶段 所有类类型 class type 的成员都会在初始化阶段初始化 即使该成员没有出现在构造函数的初始化列表中 计算阶段 一般用于执行构造函
  • 使用open3d将obj格式转为pcd格式并保存(模型转点云)

    import open3d as o3d mesh o3d io read triangle mesh bunny obj pcd mesh sample points uniformly number of points 10000 o3
  • H5 video 播放器demo

    H5 video 播放器demo 前言 最近在做一个wap端的项目 需要视频播放功能 大家也知道wap对flash支持很差 所以优先考虑使用h5播放器video 在这里我介绍用video实现视频播放方法 之后在介绍几个插件和第三方视频实现方
  • 如何在 Rust 中运行 Lua 程序

    在Rust中 你可以使用rust lua这个库来运行Lua程序 下面是一个简单的例子 首先 将 rust lua 添加到你的 Cargo toml 文件中 dependencies rust lua 0 36 然后 在你的Rust代码中 你
  • VS C++ 程序运行错误: 0xc0...07b错误等

    问题1的出现 找不到动态库 解决方式 这个问题就是你需要找到这个动态库 并且放到exe目录同级目录 问题2的出现 今天在使用动态库的时候发现了一点问题 就是我修改了我原来的库文件 当我在另外的项目中使用的时候 我只进行了lib库和头文件的替
  • Linux中find命令基本使用方法

    Linux中find命令是系统中查找文件的命令 可以帮助用户快速找出自己所需要的文件 通过文件名查找 find name 文件名 find etc name passwd 查找 etc目录下的passwd 通过文件嵌套层数查找 find m
  • python画图数据的平均值怎么算的_绘图平均值和标准偏差

    Demo of errorbar function with different ways of specifying error bars Errors can be specified as a constant value as sh
  • Matlab利用模拟退火算法求解旅行商(TSP)问题

    简介 先引入一个例题 旅行商问题 TSP 假设有一个旅行商人要拜访n个城市 已知这n个城市的坐标 他必须选择所要走的路径 路径的限制是每个城市只能拜访一次 而且最后要回到原来出发的城市 路径的选择目标是要求得的路径路程为所有路径之中的最小值
  • git 查看/修改用户名、密码

    用户名和邮箱地址的作用 用户名和邮箱地址是本地git客户端的一个变量 不随git库而改变 有朋友说这里没有git修改密码的 特意在这里补充一下 git config global credential helper store 输入这个命令
  • dup2函数:复制文件描述符示例

    dup函数用于复制文件描述符 这样使得两个描述符指向同一个文件 这就类似于linux中的硬链接 此时内核会在内部维护一个计数为2 如果关闭其中一个不能真正的关闭文件 当计数为0时即两个文件描述符都被关闭 这个文件才真正被关闭 dup2函数作
  • LeetCode打卡——62.不同路径

    LeetCode打卡 62 不同路径 题目描述 一个机器人位于一个 m x n 网格的左上角 起始点在下图中标记为 Start 机器人每次只能向下或者向右移动一步 机器人试图达到网格的右下角 在下图中标记为 Finish 问总共有多少条不同
  • 微信支付:JSAPI或APP拉起支付,return_msg=签名错误

    1 发送给微信的参数xml 下图中的商户key就是微信后台设置的appsecret 2 复制到 微信支付接口签名校验工具 本文适用如下图是检测通过的 如果跟我一样 检测通过了 但下单接口仍返回 签名错误 唯一的问题就是 签名方法传的key用
  • 那些你不知道的表结构设计思路--开源软件诞生9

    ERP表结构的设计 第9篇 用日志记录 开源软件 的诞生 赤龙 ERP 开源地址 点亮星标 感谢支持 与开发者交流 kzca2000 码云 https gitee com redragon redragon erp GitHub https
  • 【JavaScript高级】手写apply()、call()、bind()

    文章目录 手写之前 apply call 封装函数实现apply和call bind 参考 手写之前 我们有一个函数foo 已知 foo的隐式原型是绑定在Function的显式原型上的 Function是一个构造函数 function fo