JavaScript 手写 call、apply、bind 和 new

news2025/3/31 12:53:47

1. 手写 call 方法

核心思路:改变函数的 this 指向并立即执行,通过将函数临时挂载到目标对象上调用。

Function.prototype.myCall = function (context, ...args) {
    // 如果 context 为 null 或 undefined,则默认为 window
    context = context || window;
    // 将当前函数作为 context 的一个属性
    const fnKey = Symbol(); // 使用 Symbol 避免属性名冲突
    context[fnKey] = this;
    // 调用函数
    const result = context[fnKey](...args);
    // 删除临时添加的属性
    delete context[fnKey];
    return result;
};

// 使用示例
function greet(name, age) {
    console.log(`Hello, ${name}! You are ${age} years old.`);
    console.log(`This is ${this.title}`);
}

const context = { title: 'My Context' };
greet.myCall(context, 'Alice', 25);

 将函数作为上下文对象的一个属性调用,调用后删除该属性,接收参数列表。
 

2. 手写 apply 方法

核心思路:与 call 类似,但参数以数组形式传递。

Function.prototype.myApply = function (context, argsArray) {
    // 如果 context 为 null 或 undefined,则默认为 window
    context = context || window;

    // 将当前函数作为 context 的一个属性
    const fnKey = Symbol();
    context[fnKey] = this;

    // 调用函数,apply 接收数组参数
    const result = context[fnKey](...argsArray);

    // 删除临时添加的属性
    delete context[fnKey];

    return result;
};

// 使用示例
function greet(name, age) {
    console.log(`Hello, ${name}! You are ${age} years old.`);
    console.log(`This is ${this.title}`);
}

const context = { title: 'My Context' };
greet.myApply(context, ['Bob', 30]);

将函数作为上下文对象的一个属性调用,调用后删除该属性,接收参数数组。

3. 手写 bind 方法

核心思路:返回一个新函数,该函数固定 this 指向和部分参数,调用时合并剩余参数。

Function.prototype.myBind = function (context, ...bindArgs) {
    const originalFunc = this;

    return function (...callArgs) {
        // 合并绑定时和调用时的参数
        const allArgs = bindArgs.concat(callArgs);

        // 使用 apply 方法调用原始函数
        return originalFunc.apply(context, allArgs);
    };
};

// 使用示例
function greet(name, age) {
    console.log(`Hello, ${name}! You are ${age} years old.`);
    console.log(`This is ${this.title}`);
}

const context = { title: 'My Context' };
const boundGreet = greet.myBind(context, 'Charlie');
boundGreet(35); 
// Hello, Charlie! You are 35 years old. This is My Context

返回一个新函数,合并绑定时的参数和调用时的参数,使用 apply 调用原始函数。

边界情况:构造函数调用:若返回的函数被用作构造函数(通过 new 调用),原生 bind 绑定的 this 会失效,指向新实例。但当前实现未处理此情况,需额外判断(见下方优化代码)。

Function.prototype.myBind = function (context, ...bindArgs) {
    const originalFunc = this;
    const boundFunc = function (...callArgs) {
        // 判断是否通过 new 调用
        const isNewCall = this instanceof boundFunc;
        const thisArg = isNewCall ? this : context;
        return originalFunc.apply(thisArg, bindArgs.concat(callArgs));
    };
    // 维护原型关系(确保 new 操作符正确工作)
    boundFunc.prototype = Object.create(originalFunc.prototype);
    return boundFunc;
};

4. 手写 new 操作符

核心思路:模拟构造函数实例化过程:创建对象、绑定原型、初始化属性、处理返回值。

function myNew(constructor, ...args) {
    // 创建一个新对象,并将其原型指向构造函数的 prototype
    const obj = Object.create(constructor.prototype);

    // 调用构造函数,将 this 绑定到新创建的对象
    const result = constructor.apply(obj, args);

    // 如果构造函数返回了一个对象,则返回该对象
    // 否则返回新创建的对象
    return result instanceof Object ? result : obj;
}

// 使用示例
function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.greet = function () {
    console.log(`Hi, I'm ${this.name}, ${this.age} years old.`);
};

const john = myNew(Person, 'John', 28);
john.greet(); 
// Hi, I'm John, 28 years old.

创建一个新对象,并将其原型指向构造函数的 prototype,调用构造函数,绑定 this 到新对象,处理构造函数的返回值(如果返回对象则使用该对象)

5. 总结对比

通过手写实现这些核心方法,可以更深入理解 JavaScript 的函数执行机制、this 绑定规则和原型继承原理。实际开发中,建议直接使用原生方法(性能更优),但掌握原理对调试和进阶学习至关重要!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2323403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络基础:量子通信技术在网络中的应用前景

计算机网络基础:量子通信技术在网络中的应用前景 一、前言二、量子通信技术基础2.1 量子通信的基本概念2.2 量子通信的主要原理2.2.1 量子密钥分发(QKD)原理2.2.2 量子隐形传态原理三、量子通信技术的特点3.1 绝对安全性3.2 超高通信速率潜力3.3 抗干扰能力强四、量子通信技…

Postman 下载文件指南:如何请求 Excel/PDF 文件?

在 Postman 中进行 Excel/PDF 文件的请求下载和导出,以下是简明的步骤,帮助你轻松完成任务。首先,我们将从新建接口开始,逐步引导你完成整个过程。 Postman 请求下载/导出 excel/pdf 文件教程

Stereolabs ZED Box Mini:机器人与自动化领域的人工智能视觉新选择

在人工智能视觉技术快速发展的今天,其应用场景正在持续拓宽,从智能安防到工业自动化,从机器人技术到智能交通,各领域都在积极探索如何利用这一先进技术。而 Stereolabs 推出的ZED Box Mini,正是一款专为满足这些多样化…

arm之s3c2440的I2C的用法

基础概念 IC(Inter-Integrated Circuit)又称I2C,是是IICBus简称,所以中文应该叫集成电路总线。 IIC的总线的使用场景,所有挂载在IIC总线上的设备都有两根信号线,一根是数据线SDA,另一 根是时钟…

redis部署架构

一.redis多实例 如上图所示,我们经常使用实例的端口号来作为实例的安装目录名称。 1.创建实例安装目录 如上图所示,这是创建实例的安装目录, 2.拷贝实例的配置文件 如上图所示,将redis解压目录下的配置文件拷贝到对应的conf目录…

深入理解指针(4)(C语言版)

文章目录 前言一、回调函数是什么(一)定义(二)工作原理(三)应用场景 二、qsort举例(一)qsort函数简介(二)比较函数的定义(三)使用示例…

【HTML】验证与调试工具

个人主页:Guiat 归属专栏:HTML CSS JavaScript 文章目录 1. HTML 验证工具概述1.1 验证的重要性1.2 常见 HTML 错误类型 2. W3C 验证服务2.1 W3C Markup Validation Service2.2 使用 W3C 验证器2.3 验证结果解读 3. 浏览器开发者工具3.1 Chrome DevTools…

​​SenseGlove与Aeon Robotics携手推出HEART项目,助力机器人培训迈向新台阶

在自动化和机器人技术快速发展的今天,SenseGlove和Aeon Robotics联合推出了一项创新项目——HEART项目。该项目在欧盟资助的MasterXR框架内展开,旨在通过整合虚拟现实(VR)、力反馈触觉手套(SenseGlove项目Rembrandt&am…

mapbox进阶,仿照百度,加载marker点位,移入marker点切换图标,点击展示气泡,气泡和marker联动

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️marker 标注点位 api1.3.1 ☘️构造函数…

使用HTML5和CSS3实现3D旋转相册效果

使用HTML5和CSS3实现3D旋转相册效果 这里写目录标题 使用HTML5和CSS3实现3D旋转相册效果项目介绍技术栈核心功能实现思路1. HTML结构2. CSS样式解析2.1 基础样式设置2.2 3D效果核心样式2.3 卡片样式 3. JavaScript交互实现3.1 旋转控制3.2 自动播放功能 技术要点总结项目亮点总…

游戏引擎学习第186天

回顾并规划今天的任务 现在,我们站在了一个关键的时刻,准备突破,拥有一些优秀的性能分析代码。从目前来看,我们已经能够看到时间的消耗情况,我对这一点感到非常兴奋。昨天的直播中我们勉强让一些东西工作了&#xff0…

【redis】持久化之RDB与AOF

在数字世界的脉搏中,数据是流淌的血液,而持久化则是保障系统生命力的核心机制。作为内存数据库的标杆,Redis凭借其高性能特性成为互联网架构的基石,但其「易失性」的天然属性也催生了关键命题:如何在服务重启或故障时保…

Brainstorm绘制功能连接图(matlab)

上篇笔记简单介绍了Brainstorm,本次使用Brainstorm绘制功能连接图。而对于连接矩阵,软件中有几种方法:相关、相干、双变量格兰杰因果关系、相位锁相值、包络相关、相位转移熵。 首先,对数据进行预处理,保存为.set&…

华为HG532路由器RCE漏洞 CVE-2017-17215 复现

华为HG532路由器RCE漏洞 CVE-2017-17215 CVE-Description Huawei HG532 with some customized versions has a remote code execution vulnerability. An authenticated attacker could send malicious packets to port 37215 to launch attacks. Successful exploit could l…

LVS的三种工作模式简述

一、引言 在过去的十几年中,Internet从几个研究机构相连为信息共享的网络发展成为拥有大量应用和服务的全球性网络,它正成为人们生活中不可缺少的 一部分。虽然Internet发展速度很快,但建设和维护大型网络服务依然是一项挑战性的任务&#xf…

使用 Layers 扩展你的 Nuxt4 应用

面对一个臃肿的页面或项目,你会如何简化重构、扩展它? 当单个 Vue 文件中界面/业务足够多时,通常我们会把它拆分成多个 components 或 composables 来引入,以此来减少此文件复杂度和增加可维护性。 当一个项目的界面/业务逻辑足…

Excel处理控件Aspose.Cells指南:如何在不使用 Microsoft Excel 的情况下解锁 Excel 工作表

Microsoft Excel 允许用户使用密码保护工作表,以防止未经授权的更改。但是,在某些情况下,您可能需要在不使用 Microsoft Excel 的情况下解锁 Excel 工作表。在本指南中,我们将探讨解锁 Excel 工作表的不同方法,例如使用…

进军场景智能体,云迹机器人又快了一步

(图片来源:Pixels) 2025年,AI和机器人行业都发生了巨大改变。 数科星球原创 作者丨苑晶 编辑丨大兔 2025年,酒店行业正掀起一股批量采购具备AI功能的软硬一体解决方案的热潮。 在DeepSeek、Manus等国产AI软件的推动…

PHP 应用SQL 注入符号拼接请求方法HTTP 头JSON编码类

#PHP-MYSQL- 数据请求类型 SQL 语句由于在黑盒中是无法预知写法的, SQL 注入能发成功是需要拼接原 SQL 语句, 大部分黑盒能做的就是分析后各种尝试去判断,所以有可能有注入但可能出现无法注入成 功的情况。究其原因大部分都是原 SQL …

【React】基础版React + Redux实现教程,自定义redux库,Redux Toolkit教程

本项目是一个在react中,使用 redux 管理状态的基础版实现教程,用简单的案例练习redux的使用,旨在帮助学习 redux 的状态管理机制,包括 store、action、reducer、dispatch 等核心概念。 项目地址:https://github.com/Yv…