实现call()、apply()、bind()函数及各自使用场景

本文主要讲解实现call()、apply()、bind()函数及使用场景举例。在JavaScript中,call()apply(), 和 bind() 是函数对象的方法,它们允许你以不同的方式调用函数,并可以设置函数体内 this 的值。this相关与这三个函数介绍:文章第二部分

1. 实现 call()

实现 call() 方法时,需要创建一个新的函数上下文(通常是通过创建一个新的函数并立即执行它),以便在该上下文中调用原始函数,并将 this 指向设置为提供的参数。

实现步骤:

(1)判断调用对象是否为函数

(2)判断传入上下文对象是否存在。如果存入的参数为null或undefined,则使用全局对象(在浏览器中为window)

(3)将调用函数(this)赋值给参数的某个属性

(4)调用参数的新函数,并传入参数

(5)清理环境,删除在参数上临时创建的属性

(6)返回结果

示例:

// 实现call函数
Function.prototype.myCall = function(context, ...args) { 
    // 判断函数是否存在
    if (typeof this!== 'function') {
            throw new TypeError('Error');
    }
    // 如果context为null或undefined,则使用全局对象(在浏览器中为window) 
    context = context || window; 
    // 将调用函数(this)赋值给context的某个属性
    context.fn=this;
    // 调用context上的新函数,并传入参数 
    const result = context.fn(...args); 
    // 清理环境,删除在context上临时创建的属性 
    delete context.fn;
    // 返回结果
    return result; 
}; 
// 示例
function greet(greeting, punctuation) { 
    console.log(greeting + ', ' + this.name + punctuation); 
} 
const personOne = { 
    name: 'Alice' 
}; 
greet.myCall(personOne, 'Hello', '!'); // 输出: Hello, Alice! 
// 注意:在非严格模式下,如果context为null或undefined,this将指向全局对象(在浏览器中为window) 
greet.myCall(null, 'Hello', '!'); // 输出: Hello, undefined!(如果全局对象中没有name属性) 
// 在严格模式下,如果context为null或undefined,则this保持为null或undefined 

2. 实现 apply()

apply() 方法调用一个函数,其具有一个指定的 this 值,以及作为一个数组(或类数组对象)提供的参数。

实现步骤:

(1)判断调用对象是否为函数

(2)判断传入上下文对象是否存在。如果存入的参数为null或undefined,则使用全局对象(在浏览器中为window)

(3)将调用函数(this)赋值给参数的某个属性

(4)调用参数的新函数,并传入参数

(5)清理环境,删除在参数上临时创建的属性

(6)返回结果

示例:

// 实现apply函数
Function.prototype.myApply = function(context) { 
    // 判断函数是否存在
    if (typeof this!== 'function') {
            throw new TypeError('Error');
    }
    // 如果context为null或undefined,则使用全局对象(在浏览器中为window) 
    context = context || window; 
    // 将调用函数(this)赋值给context的某个属性
    context.fn=this;
    // 调用context上的新函数,并传入参数 
    let result;
    if(arguments[1]){
        result = context.fn(...arguments[1]);
    }else{
        result = context.fn();
    }
    // 清理环境,删除在context上临时创建的属性 
    delete context.fn;
    // 返回结果
    return result; 
}; 
// 示例
function sum(a, b, c) {  
    return a + b + c;  
}  
console.log(sum.myApply(null, [1, 2, 3])); // 输出: 6 

注意:call()方法接收一个this参数和一系列单独的参数。apply()方法接收一个this参数和一个包含所有参数的数组或类数组对象。

3. 实现 bind()

实现bind函数主要是创建一个新的函数,这个新函数在被调用时,this值会被预设为bind的第一个参数,其余参数将作为新函数的参数,供调用时使用。

实现步骤:

(1)检查调用对象是否为函数

(2)创建并返回一个新函数

(3)处理新函数被用作构造函数的情况

(4)处理普通函数调用

(5)在新函数内部使用applycall调用原始函数

(6)返回新函数

示例:

// 实现bind函数
Function.prototype.myBind = function(context,...args) { 
    // 判断函数是否存在
    if (typeof this!== 'function') {
        throw new TypeError('Error');
    }
    // 创建一个新函数
    const fn = (...innerArgs) => {
        // 如果新函数被作为构造函数使用,则this指向新创建的对象
        if (this instanceof fn) {
            return new this(...innerArgs,...args);
        }
        // 否则,this指向context
        return this.apply(context, [...innerArgs,...args]);
    };
    // 复制原函数的prototype
    fn.prototype = Object.create(this.prototype);
    // 返回新函数
    return fn;
};
// 示例
const personTwo = {
    name: 'Bob'
};
const lizi = greet.myBind(personTwo, 'Hello');
lizi('!'); // !, BobHello

4.call()|apply()|bind()使用场景

call、‌apply和bind方法在JavaScript中主要用于改变函数执行时的上下文环境(即改变函数内部this的指向)。‌但是还有一些其他用处, 以下是这些方法的使用场景:‌

改变函数执行时的上下文环境:‌当函数被作为对象的方法调用时,‌this指向调用该方法的对象。‌需要控制函数内部的this指向其他对象,可以使用call、‌applybind方法来达到目的。‌

function greet() {  
    console.log('Hello, ' + this.name + '!');  
}  
  
const person = {  
    name: 'Alice'  
};  
  
// 使用call  
greet.call(person); // 输出: Hello, Alice!  
  
// 使用apply(与call类似,但第二个参数是数组)  
greet.apply(person); // 输出: Hello, Alice!  

实现继承:‌可以使用这些方法来实现继承。‌例通过call方法调用父构造函数来实现子类继承父类的属性和方法。‌

function Parent(name) {  
    this.name = name;  
    this.sayHello = function() {  
        console.log('Hello, ' + this.name);  
    };  
}  
  
function Child(name, age) {  
    Parent.call(this, name); // 继承Parent的name属性  
    this.age = age;  
}  
  
const child = new Child('Bob', 10);  
child.sayHello(); // 输出: Hello, Bob  

与事件处理相关:‌在处理DOM事件时,‌经常需要改变事件处理函数的上下文环境,‌以便正确地访问事件对象或其他相关数据。‌可以使用bind方法来创建一个新的函数。‌

const button = document.getElementById('myButton');  
  
function showMessage() {  
    console.log('Button clicked by ' + this.name);  
}  
  
const person = {  
    name: 'Alice'  
};  
  
// 使用bind将showMessage的this绑定到person对象  
button.addEventListener('click', showMessage.bind(person));  

与回调函数相关:‌如果需要在回调函数内部访问外部作用域的变量或对象,‌并希望回调函数内部的this指向特定的对象,‌可以使用bind方法来实现。‌

function fetchData(callback) {  
    // 假设这是从服务器获取的数据  
    const data = 'Some data';  
    // 调用回调函数,但确保回调函数内部的this指向正确的对象   
    const boundCallback = callback.bind(this);  
    boundCallback(data);  
}  
  
const obj = {  
    name: 'Alice',  
    processData: function(data) {  
        console.log('Processing data for ' + this.name);  
        console.log(data);  
    }  
};  
  
fetchData(obj.processData.bind(obj));

与异步编程相关:‌在异步编程中,‌如使用Promise或async/await时,‌可能需要控制回调函数的上下文环境。‌可以使用bind方法来确保回调函数内部的this指向正确的上下文。‌

function fetchUser(userId, callback) {  
    setTimeout(() => {  
        // 假设是从服务器获取的用户信息  
        const user = { id: userId, name: 'Alice' };  
        // 确保回调函数内部的this指向正确  
        // callback.bind(this)(user); 
        const boundCallback = callback.bind(this);  
        boundCallback(user);  
    }, 1000);  
}  
  
const context = {  
    logUser: function(user) {  
        console.log('User:', user.name);  
    },  
};  
  
// 更好的做法是将bind的结果作为参数传递  
fetchUser(1, context.logUser.bind(context));

注意:在异步编程中直接使用bind然后立即调用不是最佳实践。因为bind返回的是一个新函数,如果直接调用(如callback.bind(this)(user);),则不会按预期工作。

通常会将bind的结果赋值给一个变量,然后使用该变量来调用函数。

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
`call`、`apply`和`bind`是JavaScript中的三个重要的函数方法,它们都可以用来绑定函数中的`this`关键字。 `call`和`apply`都是立即调用函数,而`bind`则是返回一个新的函数,可稍后调用。 下面是三个函数的示例: ``` // call const person1 = { name: 'Alice', age: 25 }; const person2 = { name: 'Bob', age: 30 }; function greet() { console.log(`Hello, ${this.name}. You are ${this.age} years old.`); } greet.call(person1); // Hello, Alice. You are 25 years old. greet.call(person2); // Hello, Bob. You are 30 years old. // apply const numbers = [5, 10, 15, 20]; function sumNumbers(a, b, c, d) { return a + b + c + d; } const result = sumNumbers.apply(null, numbers); console.log(result); // 50 // bind const person = { name: 'Mike', age: 35 }; function logPerson(job, hobbies) { console.log(`${this.name} is ${this.age} years old and works as a ${job}. ${this.name} likes ${hobbies.join(', ')}.`); } const logPersonBind = logPerson.bind(person); logPersonBind('developer', ['reading', 'swimming']); // Mike is 35 years old and works as a developer. Mike likes reading, swimming. ``` 在上面的示例中,`call`的作用是将函数`greet`中的`this`关键字绑定到`person1`和`person2`对象上。 `apply`可以将一个数组作为参数传给函数。在上面的示例中,`numbers`数组作为参数传递给`sumNumbers`函数。 `bind`创建一个新的函数,将`this`关键字绑定到`person`对象上,并返回一个新的函数`logPersonBind`。稍后执行`logPersonBind`函数时,`this`关键字将始终指向`person`对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

장숙혜

你的鼓励是我最大的动力!!!

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

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

打赏作者

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

抵扣说明:

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

余额充值