这不是为难我吗 又要我手写一个call?

这又是干嘛呀,明明有现成的方法可以直接用,还要手写?主打一个悉听尊便好吧 TuT

学完了this指向,我知道了有那么一些函数可以显式的修改this的绑定,那时我就在想,能有这么厉害?你咋做到的?继手写一个new方法之后,我又想试试call是如何做到如此'神通'的。

scss 复制代码
call(thisArg, arg1, arg2, /* ..., */ argN)

话不多说,我直接从官方文档借来了一个call的代码

js 复制代码
function greet() {
  console.log(this.animal, "的睡眠时间一般在", this.sleepDuration, "之间");
}

const obj = {
  animal: "猫",
  sleepDuration: "12 到 16 小时",
};

greet.call(obj); // 猫 的睡眠时间一般在 12 到 16 小时 之间

在上述示例中,call使得greet中的this指向了obj,所以最后输出的是obj.animal和obj.sleepDuration。那么只是如何完成的呢?

- 首先,它接受了一个参数xobj

- 让调用call的函数中的this指向obj

大概就是这样,对吧
那我们开始手撕 嘿嘿
  1. 我们知道都函数对象能调用call,那么我们定义的mycall,自然也要安排到位。

    • Function.prototype.mycall = function(xobj){}
  2. 通过参数我们拿到了xobj,如何让this指向xobj呢? 要是没学会this我还真不知道,大家有没有想到隐式绑定呢?当一个函数被某个对象所拥有,或者函数被某个上下文对象调用时,该函数中的this指向该上下文对象。就差这个东风了。

    • xobj.fn=this 这里的this指向调用mycall的函数
    • xobj.fn() 调用触发隐式绑定
    • delete xobj.fn 我们完成了this指向的改变,但不能让xobj增加额外的属性,因此我们要删除我们附加的属性。
js 复制代码
Function.prototype.mycall = function(xobj) {
    // 将当前函数(调用 mycall 的函数)作为 xobj 的一个属性
    xobj.fn = this;
    // 调用该属性函数,触发隐式绑定
    xobj.fn();
    // 删除临时添加的属性
    delete xobj.fn;
};
// 定义一个简单的测试函数
function greet() {
    console.log(this.name);
}
// 创建一个对象
const person = {
    name: 'Alice'
};
// 使用 mycall 方法调用 greet 函数,将 this 指向 person 对象
greet.mycall(person); //输出Alice

啊?天晴了雨停了,不会觉得自己行了吧?

有点东西,但不多,确实,这不过是一个雏形,我们还需要考虑其他的方面来完善。

还有一件事 我们并不知道要传递的参数有多少,但我们可以确定一定有一个对象,因此我们可以这样优化。

js 复制代码
Function.prototype.mycall = function(xobj, ...args) {
    // 将当前函数(调用 mycall 的函数)作为 xobj 的一个属性
    xobj.fn = this;   
    // 调用该属性函数,并传递参数,触发隐式绑定
    xobj.fn(...args);
    // 删除临时添加的属性
    delete xobj.fn;
};

还有一件事 那么如果调用的函数本身就有返回值呢,我们也需要捕获并返回

js 复制代码
Function.prototype.mycall = function(xobj, ...args) {
    // 将当前函数(调用 mycall 的函数)作为 xobj 的一个属性
    xobj.fn = this;

    // 调用该属性函数,并传递参数,触发隐式绑定
    const result = xobj.fn(...args);

    // 删除临时添加的属性
    delete xobj.fn;

    // 返回函数调用的结果
    return result;
};

还有一件事,代码中的xobj.fn = this;,如果本来就存在呢?

为了避免覆盖 xobj 对象上可能已经存在的同名属性,我们可以使用一个唯一的标识符(例如 Symbol),或者确保我们的属性名称不与现有属性冲突。

赶紧补一下symbol

symbol 是一种基本数据类型(primitive data type)。Symbol() 函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的 symbol 注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。

每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数据类型仅有的目的。

js 复制代码
Function.prototype.mycall = function(xobj, ...args) {
    // 如果没有传递上下文,默认为全局对象(非严格模式下)
    xobj = xobj || globalThis;

    // 创建一个唯一的 Symbol 作为属性名,以防止覆盖原有属性
    const fnSymbol = Symbol(); // 没有描述性字符串

    // 将当前函数(即被调用的函数)作为 xobj 对象的一个属性
    xobj[fnSymbol] = this;

    // 使用 xobj 调用该函数,并传递参数
    const result = xobj[fnSymbol](...args);

    // 删除临时添加的属性
    delete xobj[fnSymbol];

    // 返回函数调用的结果
    return result;
};

至此,一个相对完善的手撕call 就完成啦~

相关推荐
爱吃的强哥3 分钟前
vue3 使用 vite 管理多个项目,实现各子项目独立运行,独立打包
前端·javascript·vue.js
涵信11 分钟前
第十节:性能优化高频题-虚拟DOM与Diff算法优化
javascript·vue.js·性能优化
拖孩18 分钟前
【Nova UI】十一、组件库中 Icon 组件的测试、使用与全局注册全攻略
前端·javascript·vue.js·ui·sass
丘山子41 分钟前
如何在 1000 亿级数据规模下实现高效的去重统计?
后端·面试·架构
天天扭码1 小时前
深入解析 JavaScript 中的每一类函数:从语法到对比,全面掌握适用场景
前端·javascript·面试
uhakadotcom1 小时前
Lovable:用AI轻松打造完整应用,零基础也能快速开发
后端·面试·架构
北上ing2 小时前
同一页面下动态加载内容的两种方式:AJAX与iframe
前端·javascript·ajax
纪元A梦2 小时前
华为OD机试真题——推荐多样性(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
java·javascript·c++·python·华为od·go·华为od机试题
小墨宝3 小时前
js 生成pdf 并上传文件
前端·javascript·pdf
www_pp_4 小时前
# 构建词汇表:自然语言处理中的关键步骤
前端·javascript·自然语言处理·easyui