面试官: 请你手写一份 Call()源码,看完此篇不用担心!

简介: 面试官: 请你手写一份 Call()源码,看完此篇不用担心!

前言

一盏茶的功夫带你掌握烦人的this指向问题

在上篇文章中,我们讲到了this的五种绑定规则,了解了这五种绑定规则对this的指向问题可以不用再害怕,对this或是五种绑定规则不太熟悉的小伙伴,建议先去看看我的上篇文章,文章链接已放在上面,配合本章食用效果更佳~

今天我们要讲的是显示绑定中的一种方法,call()方法的源码,我们在面试的过程中,面试官大大有时会叫我们手写一个call()的源码,这时候有些小伙伴就会慌了,其实call()就是靠隐式绑定实现的,我们先来复习一下隐式绑定规则显式绑定规则

隐式绑定

如果函数被一个对象作为方法所调用时,那么this就会指向该对象。我们来看一个例子:

function foo() {
    console.log(this.a);
  }
  var obj = {
    a: '来颗奇趣蛋',
    func: foo
}
obj.func()

调用 objfunc 方法,在这一步,func 方法被作为 obj 对象的方法调用,因此隐式绑定规则会将 this 绑定到 obj。因此,this.a 将打印出 obj 对象的属性 a 的值:

我们来看看输出结果:

image.png

显示绑定 ---call, apply, bind

显示绑定是我们通过call,apply,bind,改变this的指向,我们一起来看看例子:

function foo() {
    console.log(this.a);
}
var obj = {
    a: 2
}
foo()

如果这样调用foo(),那么就是this默认绑定,this指向全局,那么输出undefined,那么我们怎么让this指向obj,输出2呢?

1。 使用call()

function foo() {
    console.log(this.a);
}
var obj = {
    a: 2
}
foo.call(obj)

call的作用就是强行把foo中的this指向obj,我们来输出以下来看看:

image.png

我们今天就主要讲讲call(),对另外两种方法感兴趣的小伙伴们可以点击链接去那篇文章看看。

call()源码

func.call(obj)为什么可以将函数functhis指向绑定的对象obj,这是因为call()干了一个操作,它先将函数func放进obj里面,然后obj.func(),使函数作为对象的方法调用,达成隐式绑定的规则,这样就成功使函数func中的this指向obj,之后再将对象object内部的func给删除。我们用伪代码来看一下:

{
    func: func
}
obj.func()
delete obj.func

这样大家是不是了解一些call()方法的底层逻辑了呢?它就是通过隐式绑定规则,将函数中的this指向对象obj中。

自己打造一份call()源码

在面试过程中,面试官有时会叫我们自己打造一份call(),功能需要跟call()一样,我们一起来看看怎么实现吧:

Function.prototype.myCall = function (context) {
    if (!this instanceof Function) {
        throw new TypeError('myCall is not a function')
    }
    context.fn = this
    context.fn()
    delete context.fn
}

这段代码是一个简化版的 call 方法的自定义实现,添加到 Function 原型上,可以用 myCall 来模拟 call 方法。让我逐步解释这段代码:

Function.prototype.myCall = function (context) {
    if (!this instanceof Function) {
        throw new TypeError('myCall is not a function');
    }
    context.fn = this;
    context.fn();
    delete context.fn;
};
  1. Function.prototype.myCall: 这一行将一个自定义的 myCall 方法添加到 Function 的原型上。这样一来,所有的函数对象都可以调用这个方法。
  2. throw new TypeError('myCall is not a function'): 如果检测到 this 不是函数,抛出一个类型错误。
  3. context.fn = this: 在传入的 context 对象上创建一个名为 fn 的属性,并将其值设置为调用 myCall 方法的函数(this)。
  4. context.fn():context 上调用 fn 方法,即调用了原始函数,并且此时 this 就是 context 对象。
  5. delete context.fn: 删除 context 上的 fn 属性,以防止给 context 对象添加了不必要的属性。

我们来验证一下此函数有没有实现call()的功能:

var obj = {
    a: 1,
}
function foo(x, y) {
    console.log(this.a);
}    
Function.prototype.myCall = function (context) {
    if (!this instanceof Function) {
        throw new TypeError('myCall is not a function')
    }
    context.fn = this
    context.fn()
    delete context.fn
}
foo.myCall(obj)
我们来输出一下:

image.png

可以看到,输出了 1,说明函数中的this已经指向了对象obj,我们这个手写函数也算是实现了call()的功能啦。

不过,我们在实际项目中,应使用原生的 call 方法,因为它已经经过严格的测试和优化。

相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
4月前
|
JavaScript 前端开发
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
这篇文章主要讨论了axios的使用、原理以及源码分析。 文章中首先回顾了axios的基本用法,包括发送请求、请求拦截器和响应拦截器的使用,以及如何取消请求。接着,作者实现了一个简易版的axios,包括构造函数、请求方法、拦截器的实现等。最后,文章对axios的源码进行了分析,包括目录结构、核心文件axios.js的内容,以及axios实例化过程中的配置合并、拦截器的使用等。
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
|
18天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
4月前
|
JavaScript 前端开发
【Vue面试题二十七】、你了解axios的原理吗?有看过它的源码吗?
文章讨论了Vue项目目录结构的设计原则和实践,强调了项目结构清晰的重要性,提出了包括语义一致性、单一入口/出口、就近原则、公共文件的绝对路径引用等原则,并展示了单页面和多页面Vue项目的目录结构示例。
|
3月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
484 37
|
5月前
|
存储 安全 Java
Android面试题之ArrayList源码详解
ArrayList是Java中基于数组实现的列表,提供O(1)的索引访问,但插入和删除操作平均时间复杂度为O(n)。默认容量为10,当需要时会通过System.arraycopy扩容。允许存储null,非线程安全。面试常问:List是接口,ArrayList是其实现之一,推荐使用List接口编程以实现更好的灵活性。更多详情见[ArrayList源码](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ArrayList.java#ArrayList.Node)。
36 2
|
4月前
|
存储 JavaScript 前端开发
JS浅拷贝及面试时手写源码
JS浅拷贝及面试时手写源码
|
6月前
|
缓存 NoSQL Java
15分钟面试被5连CALL,你扛得住么?
在Java并发编程中,锁是控制共享资源访问的关键,用于避免数据竞争、保证原子性、维护执行顺序、提高性能、实现同步及避免死锁。分布式锁在多节点系统中同样重要,确保一致性、防止资源冲突、提高可扩展性并解决竞态条件。实现分布式锁的方法包括基于数据库、缓存(如Redis)、Zookeeper等。选型时要考虑性能、可靠性、可扩展性和特定场景需求,如一致性、可用性和分区容忍性。
|
6月前
|
存储 安全 Java
《ArrayList & HashMap 源码类基础面试题》面试官们最喜欢问的ArrayList & HashMap源码类初级问,你都会了?
《ArrayList & HashMap 源码类基础面试题》面试官们最喜欢问的ArrayList & HashMap源码类初级问,你都会了?
43 0
|
7月前
|
前端开发 JavaScript 程序员
async-validator 源码学习(一):文档翻译,2024年最新如何面试大厂
async-validator 源码学习(一):文档翻译,2024年最新如何面试大厂
下一篇
DataWorks