js中apply、call和bind方法

js中函数有三个显式更改this指向的方法,分别是apply、call以及bind方法。
至于改变this指向的其他方式,见:juejin.cn/post/697068…

三种方法的使用规则

1、三种方法的使用方式

let per1 = {
    name: "Rose",
    age: 14,
    introduce: function(){
        console.log(`我叫${this.name}, 今年${this.age}岁。`);
    }
}
let per2 = {
    name: "Jack",
    age: 15
}
console.log(per1.introduce.call(per2));  // 我叫Jack, 今年15岁。
console.log(per1.introduce.apply(per2));  // 我叫Jack, 今年15岁。
console.log(per1.introduce.bind(per2)());  // 我叫Jack, 今年15岁。
复制代码

通过以上实例可以看出,call()apply()方法是直接调用函数,而bind()方法是生成一个函数。

如果三种方法后面使用null,则this指向全局对象,否则指向空对象(指向空对象描述可能不准确,但是这么解释很好理解)。

let per1 = {
    name: "Rose",
    age: 14,
    introduce: function(){
        console.log(`我叫${this.name}, 今年${this.age}岁。`);
    }
}
window.name = "window";
window.age = 666;
console.log(per1.introduce.call(null));  // 我叫window, 今年666岁。
console.log(per1.introduce.apply(123));  // 我叫undefined, 今年undefined岁。
console.log(per1.introduce.bind("abc")());  // 我叫undefined, 今年undefined岁。
复制代码

2、三种方式的传参

let per1 = {
    fn: function(fm, to){
        console.log(`我来自${fm}, 即将去往${to}。`);
    }
}
let per2 = {};
console.log(per1.fn.call(per2, "武汉", "杭州"));  // 我来自武汉,即将去往杭州。
console.log(per1.fn.apply(per2, ["武汉", "杭州"]));  // 我来自武汉,即将去往杭州。
console.log(per1.fn.bind(per2, "武汉", "杭州")());  // 我来自武汉,即将去往杭州。
console.log(per1.fn.bind(per2)("武汉", "杭州"));  // 我来自武汉,即将去往杭州。
console.log(per1.fn.bind(per2, "武汉")("杭州"));  // 我来自武汉,即将去往杭州。
复制代码

通过上面的示例可以看出:
call()传参除了第一个参数,其他参数和直接使用原始方法fn()方法传参一样,一个个将参数放进去。
apply()方法则不同,直接传入一个数组,作为fn()的arguments。
bind()方法传参为,将两个函数的参数组合使用,具体可参考下文bind()的实现方式。

三种方法的实现方式

1、call()

Function.prototype.call_ = function(obj = window, ...args) {
    // 首先判断obj是否为对象,否则指向空对象
    if(typeof obj !== "object" || typeof obj !== "function") {
        obj = {};
    }
    
    // 给obj增加属性,避免属性名重复,使用Symbol
    const fn = Symbol("fn");
    obj[fn] = this;
    
    // 执行函数
    const res = obj[fn](...args);
    // 清除新增的fn属性
    delete obj[fn];
    // 返回函数执行结果
    return res;
}
复制代码

2、apply()

Function.prototype.apply_ = function(obj = window, args) {
    // 首先判断obj是否为对象,否则指向空对象
    if(typeof obj !== "object" || typeof obj !== "function") {
        obj = {};
    }
    
    // 给obj增加属性,避免属性名重复,使用Symbol
    const fn = Symbol("fn");
    obj[fn] = this;
    
    // 执行函数
    const res = obj[fn](...args);
    // 清除新增的fn属性
    delete obj[fn];
    // 返回函数执行结果
    return res;
}
复制代码

3、bind()

Function.prototype.bind_ = function(obj = window, ...args) {
    // 首先判断obj是否为对象,否则指向空对象
    if(typeof obj !== "object" || typeof obj !== "function") {
        obj = {};
    }
    // 通过self来保存this
    const self = this;
    
    return function F(){
        // 考虑new的情况
        if (this instanceof F) {
            return new self(...args, ...arguments);
        }
        return self.apply(context, [...args, ...arguments]);
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享