JavaScript—手写模拟实现call和apply

JavaScript—手写模拟实现call和apply

Call

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

举个例子

 

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

 

如果直接执行bar() 就相当于是 window.bar() 会从window找value,因为没有定义 所以打印 undefine

注意两点

1.call改变了this的指向,指向到foo

2.bar 函数执行了

模拟实现第一步

如果想实现bar函数中调用this改this指向 把 foo函数改一下结构

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

进行这样的操作就可以把this指向 foo

但是这样的话相当于给foo多加了一个bar的函数属性 所以要在使用过bar()之后使用delete 把 foo中的bar这个删除

所以模拟的步骤可以分为以下三步:

  1.将bar函数设为foo的属性

  2.执行foo中的bar函数

  3.删除foo中的bar函数

我们用代码实现一下

// 第一步
foo.fn = bar()
// 第二步
foo.fn()
//第三步
delete foo.fn

上述代码中 fu是foo新建的属性,然后执行,然后删除

根据这个思路,尝试实现第一版的call2函数:

复制代码
// 第一版
Function.prototype.call2 = function(context){
    context.fn = this 
    context.fn()
    delete context.fn 
}
//测试一下 
var foo = {
    value:1
}

var bar = function(){
    console.log(this.value)
}

bar.call2(foo) // 1
复制代码

打印结果为 1 ,最初版的call模拟实现完成!!!

但是可能会有疑问就是为什么要 context.fn = this呢,这个this到底是谁呢
  解释一下:

    this指向调用它的对象,call2 是 bar调用的 所以 第一版中 context.fn = this 其中的this就是bar();如果在控制台打印 console.log(this)的话结果如下
    

ƒ (){
    console.log(this.value)
}

模拟实现第二步

开始的时候讲了,call函数是可以指定参数进行传递的。举个例子:

复制代码
var foo = {
    value: 1
};

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

bar.call(foo, 'sxh', 18);
// sxh
// 18
// 1
复制代码

所以我们的这些参数应该怎么获取呢,所以JS函数执行提供一个 对象叫 Arguments,使用Arguments可以获取到函数传入的参数。所以既然可以获取到参数,我们在第一版上优化一下:

复制代码
Function.prototype.call2 = function(context){
    context.fn = this
    // 添加一个接受参数的数组
    let argArr = []
    // 从一开始 因为第一个参数是context 也就是this
    for(let i = 1;i<arguments.length;i++){
        argArr.push(arguments[i])
    } 
    context.fn(...argArr) 
delete context.fn } // 测试一下 var foo = { value: 1 }; function bar(name, age) { console.log(name) console.log(age) console.log(this.value); } bar.call2(foo, 'sxh', 18); // sxh // 18 // 1
复制代码

OKOK 现在已经完成90%!!!

模拟实现第三步

现在大致功能已经完成,但是还有几个点需要注意

  1.this参数可以传递null,如果当是null的话,在浏览器环境下视为指向window

    举个例子:

var value = 1;

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

bar.call(null); // 1

虽然这个例子本身不适用call 最后根据作用域链还是会找到全局中的value 最后 打印 1;但是还是得优化一下 如果传输this = null 应该怎么进行判断

  2.函数是可以有返回值的

    举个例子:

复制代码
var obj = {
    value: 1
}

function bar(name, age) {
    return {
        value: this.value,
        name: name,
        age: age
    }
}

console.log(bar.call(obj, 'sxh', 18));
// Object {
//    value: 1,
//    name: 'sxh',
//    age: 18
// }
复制代码

这是需要注意两个点,所以现在优化一下call2 完成第三版代码:

复制代码
Function.prototype.call2= function(context){
    // 新创建一个context 判断形参数据是否为null,如果为null 赋值为window
    var context = context||window;
    context.fn = this  
   var argArr = []
   for(let i =1;i<arguments.length;i++){
argArr.push(arguments[i])
}
var result; if(argArr.length === 0){ result = context.fn() }else{ result = context.fn(...argArr) } delete context.fn return result }

// 测试一下
var value = 2;

var foo = {
    value: 1
}

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

bar.call2(null); // 2

console.log(bar.call2(foo , 'sxh', 18)); 
// {value: 1, name: 'sxh', age: 18}
复制代码

OKOK, 完成了手写call!!!

Apply

Apply的使用方式跟call 大同小异

  举个例子:

复制代码
var foo = {
    value: 1
}

function bar(name, age) {
    console.log(this.value);
    return {
        value: this.value,
        name: name,
        age: age
    }
} 
console.log(bar.call(foo,'sxh',18))
// 1 {value: 1, name: 'sxh', age: 18}
console.log(bar.apply(foo,['sxh',18]))
// 1 {value: 1, name: 'sxh', age: 18}
复制代码

区别在于传参形式不同:

  call 传参按照个数

  apply 传参是多个参数按照数组形式传入

所以我们直接实现apply 好吧

复制代码
Function.prototype.apply2 = function(context,argArr){
    // 新创建一个context 判断形参数据是否为null,如果为null 赋值为window
    var context = context||window;
    context.fn = this  
    var result;
    if(argArr.length === 0){
        result = context.fn()
    }else{
        result =  context.fn(...argArr)
    } 
    delete context.fn
    return result
}   
var foo = {
    value:1
}
var bar = function (name,age) {
    console.log(this.value)
    console.log(name,age)
}
bar.apply2(foo,['sxh',18])
// 1
// sxh 18
复制代码

上述是Apply手写实现 根据第二个参数不用使用arguments函数截取 直接就可以使用。
借鉴:https://juejin.cn/post/6844903476477034510

 

posted @   sxh132  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示