call,apply,bind的原理及手写

是什么?

call,apply,bind是JavaScript中用于改变普通函数 this指向(无法改变箭头函数this指向 )的方法,这三个函数实际上都是绑定在Function构造函数的prototype上,而每一个函数都是Function的实例,因此每一个函数都可以直接调用call,apply,bind。

区别

  • call、apply直接调用,bind返回一个新函数并不立即执行。
  • call、bind接受多个参数,apply只接受两个参数:第一个参数是this要指向的对象,第二个参数是参数列表。

核心思想:借用方法

  • A对象有个方法,B对象因为某种原因也需要用到同样的方法,那么这时候我们是单独为 B 对象扩展一个方法呢,还是借用一下 A 对象的方法呢?

  • 当然是借用 A 对象的方法啦,既达到了目的,又节省了内存。

  • 这就是call/apply/bind的核心理念:借用方法。

  • 借助已实现的方法,改变方法中数据的this指向,减少重复代码,节省内存。

手写call、apply、bind

call实现

js 复制代码
Function.prototype.myCall = function (context) {
    // 判断调用myCall的是否为函数
    if (typeof this !== 'function') {
        throw new TypeError("被call调用的对象必须是函数");
    }
    // 判断 context 是否传入,如果未传入则设置成 window
    context = context || window;
    // 获取参数,除了context其余都合并为参数列表
    let args = [...arguments].slice(1);
    // 用Symbol来创建唯一的fn,防止名字冲突
    let fn = Symbol("key");
    // this是调用myCall的函数,将函数绑定到上下文对象的新属性上
    context[fn] = this;
    // 传入myCall的多个参数
    const result = context[fn](...args);
    // 将增加的fn方法删除
    delete context[fn];

    return result;
}
  • 测试
js 复制代码
const ddl = {
    name: "zyzy",
    hello: function () {
        console.log(`hello,${this.name}!`);
    },
};

const obj = { name: "world" };
ddl.hello.myCall(obj); // hello,world!
ddl.hello.call(obj);// hello,world!

apply实现

js 复制代码
Function.prototype.myApply = function (context) {
    // 判断调用myApply的是否为函数
    if (typeof this !== 'function') {
        throw new TypeError('被apply调用的对象必须是函数');
    }
    // 判断myApply有传入第二个参数则判断其是否是数组
    if (arguments[1] && !Array.isArray(arguments[1])) {
        throw new TypeError('apply的第二个参数必须是数组');
    }
    // 判断 context 是否传入,如果未传入则设置成 window
    context = context || window;
    // 获取参数,本身传入的arguments[1]就是一个参数数组,如果不存在则传空数组
    let args = arguments[1] || [];
    // 用Symbol来创建唯一的fn,防止名字冲突
    let fn = Symbol("key");
    // this是调用myAppky的函数,将函数绑定到上下文对象的新属性上
    context[fn] = this;
    // 传入myApply的多个参数
    const result = context[fn](...args);
    // 将增加的fn方法删除
    delete context[fn];

    return result;
}
  • 测试
js 复制代码
const ddl = {
    name: "zyzy",
    hello: function (params) {
        console.log(`hello,${this.name}!I am ${params}`);
    },
};

const obj = { name: "world" };
ddl.hello.myApply(obj,['yqyq']); // hello,world!I am yqyq
ddl.hello.apply(obj,['yqyq']);// hello,world!I am yqyq

bind实现

js 复制代码
Function.prototype.myBind = function (context) {
    // 判断调用myBind的是否为函数
    if (typeof this !== 'function') {
        throw new TypeError('被bind调用的不是函数');
    }
    // 判断 context 是否传入,如果未传入则设置成 window
    context = context || window;
    // 获取参数,除了context其余都合并为参数列表
    let args = [...arguments].slice(1);
    // 用Symbol来创建唯一的fn,防止名字冲突
    let fn = Symbol("key");
    // this是调用myBind的函数,将函数绑定到context对象的新属性上
    context[fn] = this;
    // 返回一个新的函数
    return function func() {
        // 判断返回出去的函数有没有被new,this指向中构造器调用的优先级最高
        if (this instanceof func) {
            return new context[fn](...args, ...arguments);
        }
        // 作为context对象的方法调用
        return context[fn](...args, ...arguments);
    }
}
  • 测试
js 复制代码
const ddl = {
    name: "zyzy",
    hello: function (a,b,c) {
        console.log(`hello,${this.name}!`,a + b + c);
    },
};

const obj = { name: "world" };

let hello1 = ddl.hello.myBind(obj, 1);
let hello2 = ddl.hello.bind(obj, 1);
hello1(2, 3);// hello,world! 6
hello2(2, 3);// hello,world! 6
new hello1(4, 5);// hello,undefined! 10
new hello2(4, 5);// hello,undefined! 10

如有问题欢迎在评论区讨论,一起进步!

相关推荐
Ai-_Man1 小时前
您您这可以把Grok的多个会话比如说。左侧的多个会话一次性导出吗?不是单条会话里面的多次会对话。AI导出鸭
javascript·人工智能·ai·小程序·电脑
传人once1 小时前
多图片上传预览功能如何写
前端·css·html·jquery·html5
Eric.462 小时前
Wan‑Animate+MiniMax-H3 本地部署 AI 漫剧流水线:8G 显存动作迁移与动态分镜工程实战
前端·人工智能·comfyui·ai漫剧
liangshanbo12152 小时前
JavaScript 异步核心主线
javascript·事件循环·异步操作
传人once3 小时前
悬停旋转放大和位移效果如何写
javascript·css·动画
库拉镜像AI牛牛3 小时前
短剧内容自动化生产:知漫剧工作室落地教程
大数据·服务器·前端·人工智能·语音识别
律宏阔3 小时前
Flutter 列表图片内存优化:CachedNetworkImage 与 ClipRRect 的取舍
前端·flutter
行者全栈架构师3 小时前
【鸿蒙心迹】相册文搜图工程化落地:HarmonyOS 7.0 端侧 AI 检索全链路实战与 5 个踩坑解法
前端
律宏阔3 小时前
Dart 3 Record:解决 Future.wait 异构返回值的类型问题
前端·flutter
花间相见4 小时前
【后端开发|Redis进阶01】—— Redis分布式锁全解:从SETNX到Redisson看门狗,再到RedLock
后端·面试