手写js的call、apply和bind方法(成为不是只会用这三个方法的人)

本文详细介绍了JavaScript中的call、apply和bind方法的使用,包括它们的区别、应用场景及原理。通过示例展示了如何在setName函数中通过这三种方法改变this的指向,并分析了参数传递的过程。同时,还提供了手动实现call、apply和bind的代码,帮助读者深入理解其工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

call、apply和bind方法的使用

call

function setName(name,age) {
    this.name = name;
    this.age = age;
}

const person = {
    name: "张三",
    age: 18
};

setName.call(person, "李四", 20);
console.log(person.name); // "李四"
console.log(person.age); // 20

apply

function setName(name, age) {
    this.name = name;
    this.age = age;
}

const person = {
    name: "张三",
    age: 18
};

setName.apply(person, ["李四", 20]);
console.log(person.name); // "李四"
console.log(person.age); // 20

bind

function setName(name, age) {
    this.name = name;
    this.age = age;
}

const person = {
    name: "张三",
    age: 18
};

const result = setName.bind(person, "李四", 20);
console.log(person.name); // "张三"
console.log(person.age); // 18
result();
console.log(person.name); // "李四"
console.log(person.age); // 20

它们的区别

参数执行时机
callcall 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表,当第一个参数为null、undefined的时候,默认指向window。调用的时候,会立即执行
applyapply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window同call
bind同callbind会返回一个值,值为一个函数,this指向传入的第一个参数对象,后续的参数列表会传递给这个返回值函数

对它们进行分析

为什么我们定义的setName,可以直接调用call、apply、bind方法

试着console.log(setName.__proto__.call);你会发现在原型
上有call方法,同理也有apply和bind方法。这是因为js在
Function构造函数的prototype上添加了call,bind,apply
方法。当我们声明一个函数时,实际上就是执行了new Function(),
因此我们定义的函数实例身上也有对应的方法。

为什么setName中的this.name的this指向person

从这点可知,call方法肯定帮我们做了处理。在call方法中的this
指向调用者,call在内部将this指向的实例的this指向了传入的
第一个参数。可能有点绕,从后面代码中分析可能会比较好理解。
apply,bind同理。

为什么传递给它们的参数都可以作为参数传递给我们定义的setName函数

call方法可以接收到所有的参数,然后call方法中的this指向了
调用者,call方法在内部将参数传递给了this指向的调用者。
apply,bind同理。

手写实现

call

function setName(name, age) {
    this.name = name;
    this.age = age;
}

const person = {
    name: "张三",
    age: 18,
};

// 1、在函数原型上添加我们使用的call方法newCall
Function.prototype.newCall = newCall;

function newCall(obj, ...args) {
    if (obj === null || obj == undefined) {
        // 5、如果传入的值是null或者undefined那么this指向window
        // obj = window;
        // 在node环境下为global
        obj = global;
    }
    /* 2、this指向调用者,在这个例子中就是setName
                    将setName赋值给obj的fn属性 */
    /* 将obj.fn = this;如果obj对象身上有一个fn属性,
    那么将会导致属性冲突,故改变思路将这个fn加到Object原型上*/
    Object.prototype._fn = this;
    /* 3、此时调用setName的调用者变成了obj,
                也就是setName中的this指向了obj */
    obj._fn(...args);
    /* 4、我们前面在Object.prototype身上添加
              了fn方法,污染了它,需要删除掉 */
    delete Object.prototype._fn;
}

setName.newCall(person, "李四", 20);
console.log(person.name); // "李四"
console.log(person); // {name:"李四",age:20}
setName.newCall(null, "李四", 20);
console.log(global.name); // "李四"
console.log(global.age); // 20

在这里插入图片描述

apply

function setName(name, age) {
    this.name = name;
    this.age = age;
}

const person = {
    name: "张三",
    age: 18,
};

// 1、在函数原型上添加我们使用的call方法newCall
Function.prototype.newApply = newApply;

function newApply(obj, arr) {
    if (obj === null || obj == undefined) {
        // 5、如果传入的值是null或者undefined那么this指向window
        // obj = window;
        // 在node环境下为global
        obj = global;
    }
    /* 2、this指向调用者,在这个例子中就是setName
                      将setName赋值给obj的fn属性 */
    /* 将obj.fn = this;如果obj对象身上有一个fn属性,
    那么将会导致属性冲突,故改变思路将这个fn加到Object原型上*/
    Object.prototype._fn = this;
    /* 3、此时调用setName的调用者变成了obj,
                  也就是setName中的this指向了obj */
    obj._fn(...arr);
    /* 4、我们前面在Object.prototype身上添加
                了fn方法,污染了它,需要删除掉 */
    delete Object.prototype._fn;
}

setName.newApply(person, ["李四", 20]);
console.log(person.name); // "李四"
console.log(person); // {name:"李四",age:20}
setName.newApply(null, ["李四", 20]);
console.log(global.name); // "李四"
console.log(global.age); // 20

在这里插入图片描述

bind

function setName(name, age) {
    this.name = name;
    this.age = age;
}

const person = {
    name: "张三",
    age: 18,
};

// 1、在函数原型上添加我们使用的call方法newCall
Function.prototype.newBind = newBind;

function newBind(obj, ...args) {
    if (obj === null || obj == undefined) {
        // 5、如果传入的值是null或者undefined那么this指向window
        // obj = window;
        // 在node环境下为global
        obj = global;
    }
    return () => {
        /* 2、this指向调用者,在这个例子中就是setName
            将setName赋值给obj的fn属性 */
        /* 将obj.fn = this;如果obj对象身上有一个fn属性,
    那么将会导致属性冲突,故改变思路将这个fn加到Object原型上*/
    	Object.prototype._fn = this;
        /* 3、此时调用setName的调用者变成了obj,
            也就是setName中的this指向了obj */
        obj._fn(...args);
        /* 4、我们前面在Object.prototype身上添加
            了fn方法,污染了它,需要删除掉 */
        delete Object.prototype._fn;
    };
}

const result1 = setName.newBind(person, "李四", 20);
console.log(person.name);
console.log(person);
console.log("-------------------");
result1();
console.log(person.name);
console.log(person);
const result2 = setName.newBind(null, "李四", 20);
console.log("-------------------");
console.log(global.name);
console.log(global.age);
result2();
console.log("-------------------");
console.log(global.name);
console.log(global.age);

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LiuJie_Boom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值