call,apply,bind的原理及手写

是什么?

callapplybind是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

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

相关推荐
粤M温同学几秒前
Web前端基础之HTML
前端·html
love530love6 分钟前
是否需要预先安装 CUDA Toolkit?——按使用场景分级推荐及进阶说明
linux·运维·前端·人工智能·windows·后端·nlp
咖啡の猫21 分钟前
JavaScript基础-DOM事件流
开发语言·javascript·microsoft
求职小程序华东同舟求职36 分钟前
互联网校招腾讯26届校招暑期实习综合素质测评答题攻略及真题题库
面试·职场和发展·求职招聘·求职
测试19981 小时前
2025软件测试面试题汇总(接口测试篇)
自动化测试·软件测试·python·测试工具·面试·职场和发展·接口测试
泯泷1 小时前
「译」为 Rust 及所有语言优化 WebAssembly
前端·后端·rust
LinXunFeng1 小时前
Flutter - GetX Helper 如何应用于旧页面
前端·flutter·开源
紫薯馍馍1 小时前
Dify创建 echarts图表 (二)dify+python后端flask实现
前端·flask·echarts·dify
梦想很大很大2 小时前
把业务逻辑写进数据库中:老办法的新思路(以 PostgreSQL 为例)
前端·后端·架构
李三岁_foucsli2 小时前
从生成器和协程的角度详解async和await,图文解析
前端·javascript