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

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

相关推荐
许强0xq11 小时前
Q6: 如何计算以太坊交易的美元成本?
面试·web3·区块链·智能合约·dapp
几何心凉13 小时前
openGauss:多核时代企业级数据库的性能与高可用新标杆
前端·数据库·数据库开发
AiXed15 小时前
PC微信协议之AES-192-GCM算法
前端·数据库·python
AllData公司负责人15 小时前
实时开发平台(Streampark)--Flink SQL功能演示
大数据·前端·架构·flink·开源
小满zs15 小时前
Next.js第五章(动态路由)
前端
清沫15 小时前
VSCode debugger 调试指南
前端·javascript·visual studio code
一颗宁檬不酸16 小时前
页面布局练习
前端·html·页面布局
zhenryx17 小时前
React Native 自定义 ScrollView 滚动条:开箱即用的 IndicatorScrollView(附源码示例)
javascript·react native·react.js·typescript
金木讲编程17 小时前
Claude、Agent与Copilot协作生成Angular应用
前端·ai编程
振华OPPO18 小时前
Vue:“onMounted“ is defined but never used no-unused-vars
前端·javascript·css·vue.js·前端框架