【前端面试】当面试官叫你手写显示绑定中Call方法的源码 其实只需搞明白隐式绑定规则

序言

深入探讨 JavaScript 中 this 的绑定规则及箭头函数

在前面文章中我们讲到了this的五种绑定规则,对此不太熟悉的小伙伴可以去翻下我之前的这篇文章,其中我们今天要讲的内容是显示绑定中Call方法的源码,在前端面试中经常会遇到面试官叫我们手写一个Call方法的源码,其实它就是利用隐式绑定实现的,让我们先温习一遍这两种绑定规则,然后我再为大家详细讲述Call方法的源码。

显式绑定规则

在 JavaScript 中,this 显示绑定可以通过 call、apply 和 bind 方法来实现。这些方法允许我们手动设置函数体内 this 的指向,这里我们只讲call方法来实现显示绑定

  1. call 方法: 使用 call 方法可以立即调用一个函数,同时指定函数内部 this 的指向。语法为 function.call(thisArg, arg1, arg2, ...),其中 thisArg 是需要绑定的 this 值,arg1、arg2 等是函数参数。
javascript 复制代码
function greet() {
    console.log('Hello, ' + this.name);
}
var person = { name: 'Alice' };
greet.call(person); // 输出 "Hello, Alice"

如果我们不给greet函数调用call方法时,这个函数里面的this会默认指向全局,浏览器状态下是window,但是我们这里调用了call方法,函数里面的this会指向person这个对象中。

隐式绑定规则

在 JavaScript 中,隐式绑定是指当一个函数成为一个对象的属性被引用然后再通过这个对象被调用时,this会隐式地绑定到该对象上。这样,函数内部的this引用就会指向这个对象。以下是关于隐式绑定的简要说明:

  1. 对象方法调用: 当函数被一个对象所拥有,再调用时,此时this会指向该对象,函数内部的this会隐式地绑定到该对象。
javascript 复制代码
function foo() {
    console.log(this.a);
}
var obj = {
    a:2,
    foo:foo
}
obj.foo(); // 输出:2

这里我们定义了一个obj对象,然后这个对象有一个属性是对foo函数的引用,接着在全局我们通过obj这个对象进行foo函数的调用,触发了隐式绑定规则,于是函数中的this会指向这个对象也就是obj,最后查找到a的值为2。

call方法的源码

在上面我们简单回顾温习了一遍JavaScript中call方法进行显示绑定隐式绑定,接下来我们来了解以下call方法的源码是怎么样的,它又是如何利用隐式绑定实现的,话不多说,先上代码!

js 复制代码
Function.prototype.myCall = function(context){
    if(typeof this !== "function"){
        throw new TypeError('myCall is not a function');
    }

    let args = [...arguments].slice(1)  //Array.from(arguments).slice(1) 类数组可以被解构 可以直接将类数组解构放进一个新数组或者强转成数组

    context.fn=this
    let res = context.fn(...args) 
    delete context.fn
    return res
}

源码解释

我们都知道所有函数的隐式原型(foo.[[proto]] )都继承自Function.prototype,而我们在这个原型身上添加一条为函数的属性,这个方法利用了隐式绑定,但是我们将通过这个方法实现显示绑定的过程,让我来为大家对这份代码逐步进行解析。

  • 因为这里面的this指向的是通过new方法实例化的一个Function对象,所以this会指向这个实例化对象也就是被调用的函数。首先检查被调用的函数是否是一个函数,如果不是,则抛出一个类型错误。

  • 接着我们对参入的参数通过...arguments扩展运算符对所有传入的参数进行解构,并且以数组的形式返回除了第一个参数以外其他剩余的参数部分,并且声明一个变量args来接受这部分参数。

  • 然后我们通过传入的context这个参数来作为我们要将this绑定到的对象,接着在这个对象里面新增一条fn属性,键值就是这个被调用的函数的结构体,这样就实现了我们希望绑定到的对象对这个函数的引用而不是调用,于是this就会指向这个context对象

  • 接着我们不能影响原函数的正常功能,再声明一个变量res来接收函数处理剩余参数的结果并且返回出去。

  • 因为我们这里不能对对象中的属性进行无故添加,所以最后完成目的还需要将fn这个属性输出,到这里我们的call方法的源码就实现完毕了,接着来看效果。

实例验证

js 复制代码
var obj ={
    a:1
}
function foo(a,b){
    console.log(this.a,a+b);
}
Function.prototype.myCall = function(context){
    if(typeof this !== "function"){
        throw new TypeError('myCall is not a function');
    }

    let args = [...arguments].slice(1)  //Array.from(arguments).slice(1) 类数组可以被解构 可以直接将类数组解构放进一个新数组或者强转成数组

    context.fn=this
    let res = context.fn(...args) //触发this隐式绑定规则
    delete context.fn
    return res
}

foo.myCall(obj,2,3) // 输出 1 5

我们的创建的foo函数中的this成功指向了对象obj,并且函数的功能并没受影响,原理其实很简单,搞清除了隐式绑定,我们利用隐式绑定规则来实现call方法就很简单了,其实只是多了最后一步收尾工作。

结语

那么到了这里我们今天的文章就结束了,如果思路还是有点不清晰收藏起来,多看几遍这份代码的解析,逐步理解其实过程很简单。

创作不易,如果感觉这个文章对你有帮助的话,点个赞吧♥

作者所有文章的源码,给作者的开源git仓库点个收藏吧: gitee.com/cheng-bingw...

相关推荐
一颗花生米。1 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐012 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19952 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&3 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈3 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水4 小时前
简洁之道 - React Hook Form
前端
正小安6 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
小飞猪Jay7 小时前
C++面试速通宝典——13
jvm·c++·面试
_.Switch7 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光7 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js