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

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);

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我可以帮你手写三个方法并解析。 1. Call 方法 Call 方法JavaScript 中函数对象的一个方法,它可以在指定的 this 值和参数列表的前提下,调用一个函数。 手写 Call 方法的基本思路是: - 把当前函数设为指定的 this 值; - 执行当前函数,并把参数列表传递进去; - 返回当前函数执行的结果。 下面是一个简单的实现: ```javascript Function.prototype.myCall = function(context, ...args) { // 首先要判断传入的 context 是否为 null 或 undefined,如果是,那么 this 指向全局对象 window context = context || window; // 将当前函数设为传入的 context 的方法 context.fn = this; // 执行当前函数,并将参数列表传递进去 let result = context.fn(...args); // 删除当前函数作为 context 的方法 delete context.fn; // 返回当前函数执行的结果 return result; }; ``` 使用示例: ```javascript function greet(name) { console.log(`Hello, ${name}! My name is ${this.name}.`); } const person = { name: 'John' }; greet.myCall(person, 'Alice'); // 输出:Hello, Alice! My name is John. ``` 2. Apply 方法 Apply 方法和 Call 方法类似,只不过它的参数列表是一个数组或类数组对象。 手写 Apply 方法的基本思路是: - 把当前函数设为指定的 this 值; - 执行当前函数,并把参数列表传递进去; - 返回当前函数执行的结果。 下面是一个简单的实现: ```javascript Function.prototype.myApply = function(context, args) { // 首先要判断传入的 context 是否为 null 或 undefined,如果是,那么 this 指向全局对象 window context = context || window; // 将当前函数设为传入的 context 的方法 context.fn = this; // 执行当前函数,并将参数列表传递进去 let result = context.fn(...args); // 删除当前函数作为 context 的方法 delete context.fn; // 返回当前函数执行的结果 return result; }; ``` 使用示例: ```javascript function add(a, b) { return a + b; } const numbers = [1, 2]; console.log(add.myApply(null, numbers)); // 输出:3 ``` 3. Bind 方法 Bind 方法会创建一个新的函数,它的 this 值被绑定到传入的参数上。新函数被调用时,它的 this 值将永久地被绑定到传入的参数上,不会再被改变。 手写 Bind 方法的基本思路是: - 把当前函数保存起来,以便后面返回一个新的函数; - 返回一个新的函数,这个新函数的 this 值被绑定到传入的参数上; - 当新函数被调用时,它会调用原函数,并把传入的参数和绑定的 this 值一起传递进去。 下面是一个简单的实现: ```javascript Function.prototype.myBind = function(context, ...args1) { // 保存当前函数 const fn = this; // 返回一个新的函数 return function(...args2) { // 调用原函数,并绑定 this 值和参数列表 return fn.call(context, ...args1, ...args2); }; }; ``` 使用示例: ```javascript const person = { name: 'John', greet(name) { console.log(`Hello, ${name}! My name is ${this.name}.`); } }; const greet = person.greet.myBind({ name: 'Alice' }, 'Bob'); greet('Charlie'); // 输出:Hello, Bob! My name is Alice. ``` 以上就是手写 Call、ApplyBind 方法的基本实现和解析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LiuJie_Boom

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

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

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

打赏作者

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

抵扣说明:

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

余额充值