JavaScript深入之call和apply的模拟实现(笔记)

156 阅读1分钟

读大佬 @冴羽 JavaScript深入系列juejin.cn/post/684490… 有感

call

call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}
bar(); // undefined
bar.call(foo); // 1

由上面代码可见,call改变了this的指向。

模拟实现第一版

var foo = {
    value: 1,
    bar: function() {
        console.log(this.value)
    }
};

foo.bar(); // 1
delete foo.bar;

这样,this就指向foo了。

以上可总结为

// 第一步:将函数变为对象属性
foo.fn = bar
// 第二步:执行函数
foo.fn()
// 第三步:删除函数属性
delete foo.fn

第一版成品

Function.prototype.call2 = function(context) {
    context.fun = this;
    context.fun();
    delete context.fun;
}
let obj = {
    value: 10
}
function bar(){
    console.log(this.value)
}
bar.call2(obj)  // 10

模拟实现第二版

call 函数给定参数执行函数

var foo = {
    value: 1
};

function bar(name, age) {
    console.log(name)
    console.log(age)
    console.log(this.value);
}

bar.call(foo, 'kevin', 18);
// kevin
// 18
// 1
Function.prototype.call2 = function(context) {
    
    if(!context){
        // 若参数为null,则this指向window
        context = window
    }else if(typeof(context)!="object") {
        // 如果不是对象的话,则将其转成对象
        context = Object(context)
    }
    context.fun = this;
    // 将参数放入一个数组中,需要排除第一个参数
    let arr = [];
    for(let i = 1; i < arguments.length; i++){
        arr.push(arguments[i])
    }
    // 使用展开语法,将数组展开成函数的参数
    let result = context.fun(...arr);
    delete context.fun;
    // 函数的返回值
    return result
}

// 测试
var value = 2;
let obj = {
    value: 10
}
function bar(name, age) { 
    console.log(name) 
    console.log(age) 
    console.log(this.value); 
} 

function bar1(name, age) { 
    console.log(this.value); 
    return { 
        value: this.value,
        name: name, 
        age: age 
    } 
} 
bar.call(null); // 2 
console.log(bar1.call2(obj, 'kevin', 18));
// 1 
// Object { 
    // value: 1, 
    // name: 'kevin', 
    // age: 18 
// }

bar.call2(foo, 'kevin', 18);
// kevin 
// 18 
// 1

apply

apply与call的区别只是传入的参数不同,但是第一个参数还是相同的。

Function.prototype.apply2 = function(context, args) {
    if(!context){
        // 若参数为null,则this指向window
        context = window
    }else if(typeof(context)!="object") {
        // 如果不是对象的话,则将其转成对象
        context = Object(context)
    }
    args = args || [];
    context.fun = this;
    
    // 使用展开语法,将数组展开成函数的参数
    let result = context.fun(...args);
    delete context.fun;
    // 函数的返回值
    return result
}