(执行上下文作用域链)前端八股文修炼Day4

news2025/1/4 19:06:22

在这里插入图片描述

一 作用域作用域链

作用域(Scope)是指程序中定义变量的区域,作用域规定了在这个区域内变量的可访问性。在 JavaScript 中,作用域可以分为全局作用域和局部作用域。

  • 全局作用域:在代码中任何地方都可以访问的作用域,全局作用域中声明的变量在整个程序中都可以被访问。

  • 局部作用域:在函数内部定义的作用域,只能在函数内部访问到的变量称为局部变量,它们的作用域仅限于所在的函数内部。

在 JavaScript 中,作用域链(Scope Chain)是指当代码在某个作用域(比如函数)执行时,JavaScript 引擎会按照定义变量的位置来查找变量的过程。作用域链是由当前执行环境的变量对象、外部函数的变量对象和全局变量对象组成的链式结构。

作用域链的工作原理如下:

  1. 当函数被调用时,JavaScript 引擎会创建一个执行环境(Execution Context)。
  2. 在执行环境中,会创建一个变量对象(Variable Object),用于存储该函数内定义的变量。
  3. 如果在当前函数中无法找到某个变量,JavaScript 引擎会沿着作用域链向上查找外部函数的变量对象,直到找到匹配的变量或者到达全局作用域为止。

理解作用域和作用域链对于编写复杂的 JavaScript 程序至关重要,因为它决定了变量的可见性和访问规则。正确理解作用域链有助于避免变量命名冲突、提高代码可读性和维护性。

回答示例:

当回答关于作用域和作用域链的问题时,你可以采取以下方法来清晰地向面试官解释:

  1. 简明扼要的定义:开始时给出一个简单而清晰的定义,例如:“作用域是指变量和函数的可访问性范围,而作用域链是用来解析变量位置的机制”。

  2. 全局作用域和局部作用域:解释全局作用域和局部作用域的概念,说明全局作用域中定义的变量可以在整个程序中访问,而局部作用域中定义的变量只能在特定的函数内部访问。

  3. 作用域链的形成:说明作用域链是由当前执行环境的变量对象、外部函数的变量对象和全局变量对象组成的链式结构,用来确定变量的访问顺序。

  4. 作用域链的查找过程:描述在 JavaScript 中如何通过作用域链查找变量的过程,即从当前作用域开始查找,如果找不到则沿着作用域链向上查找,直至找到变量或者到达全局作用域。

  5. 闭包与作用域链的关系:提及闭包(Closure)是作用域链的一个重要应用,它可以让函数访问其父函数作用域中的变量。

  6. 举例说明:通过具体的代码示例来演示作用域和作用域链的概念,展示变量在不同作用域中的访问方式以及作用域链的影响。

  7. 回答问题:准备回答面试官可能提出的相关问题,以确保你能清晰表达你对作用域和作用域链的理解。

二 执行上下文

  1. 定义:执行上下文是 JavaScript 代码执行时的环境,其中包含了变量、函数和其他数据的作用域和环境信息。

  2. 执行上下文的:说明执行上下文是在代码执行时创建的,每个函数调用都会生成一个新的执行上下文。

  3. 执行上下文的组成部分:详细介绍执行上下文的主要组成部分:

    • 变量对象(Variable Object):用于存储该执行上下文中定义的变量、函数声明和形参。
    • 作用域链(Scope Chain):描述了当前执行上下文中可以访问的变量的链式结构。
    • this 值:指向当前执行上下文所在的对象,具体取决于函数被调用的方式。
  4. 执行上下文的生命周期:解释执行上下文的生命周期,包括创建、执行代码和销毁的过程。

  5. 作用域链的作用:强调作用域链在确定变量访问权限时的重要性,以及如何通过作用域链查找变量。

  6. 举例说明:通过一个简单的代码示例来演示执行上下文的概念,例如创建一个函数并访问其中定义的变量,展示执行上下文如何影响变量的访问。

三 闭包

闭包是指一个函数能够访问其词法作用域外部的变量,即使这个函数在词法作用域外被调用。闭包实际上是由函数和其相关的引用环境(包含了该函数创建时所处的词法作用域)组合而成的实体。

以下是一个闭包的示例:

function outerFunction() {
    let outerVariable = 'I am from the outer function';
    
    function innerFunction() {
        console.log(outerVariable);
    }
    
    return innerFunction;
}

let closureExample = outerFunction();
closureExample(); // 输出:I am from the outer function

在这个示例中,innerFunction 是一个闭包,因为它可以访问外部函数 outerFunctionouterVariable 变量。

使用场景:

  1. 保留状态:闭包可以在函数执行完毕后仍然保持对外部变量的引用,因此可以用于保留状态。
  2. 数据封装:闭包可以创建私有变量,实现数据的封装和隐藏,避免全局命名冲突。
  3. 模块化开发:闭包在模块化开发中起到重要作用,可以隐藏实现细节,提供公共接口。
  4. 事件处理程序:在事件处理程序中,闭包可以用来维持回调函数对外部状态的访问。
  5. 异步操作:闭包可以解决异步操作中的变量共享和保持状态的问题。

优点:

  1. 保留状态:可以保持函数执行时的状态,实现状态的保留。
  2. 数据封装:通过闭包可以创建私有变量,避免全局变量污染。
  3. 模块化开发:闭包可以帮助实现模块化设计,提高代码的模块性和可维护性。

缺点:

  1. 内存泄漏:如果闭包中持有大量变量或被长时间引用,可能导致内存泄漏问题。
  2. 性能开销:闭包会增加内存消耗和函数调用的复杂性,可能影响性能。
  3. 理解困难:对于初学者来说,闭包的概念可能比较抽象和难以理解,容易出现使用错误。

综上所述,闭包是 JavaScript 中强大且常用的特性,能够带来很多便利,但也需要注意潜在的问题。正确使用闭包可以提高代码的灵活性和可维护性。如果有任何疑问或需要进一步解释,请随时告诉我!

三 this

在 JavaScript 中,this 是一个关键字,指向当前对象。call()apply()bind() 是用来改变函数中 this 的指向的方法。

  • this:在函数内部,this 指向调用该函数的对象。
  • call():立即调用函数,可以指定函数内 this 的指向,并且允许传入参数列表。
  • apply():立即调用函数,可以指定函数内 this 的指向,并且允许传入参数数组。
  • bind():返回一个新函数,不会立即调用原函数,而是返回一个新函数,可以随后调用,并且固定了 this 的指向。
this 指向

在 JavaScript 中,this 的指向是动态的,取决于代码执行的上下文。下面是一些常见情况下 this 的指向:

  1. 全局环境:在全局环境中,this 指向全局对象(在浏览器中通常是 window 对象)。

  2. 函数中

    • 当函数作为普通函数调用时,this 指向全局对象或者 undefined(在严格模式下)。
    • 当函数作为对象的方法调用时,this 指向调用该方法的对象。
    • 当函数作为构造函数使用(通过 new 关键字调用)时,this 指向新创建的实例对象。
    • 使用 callapplybind 方法可以显式指定 this 的值。
  3. 事件处理函数:在事件处理函数中,this 通常指向触发事件的 DOM 元素。

  4. 箭头函数:箭头函数没有自己的 this,它会继承外层作用域的 this 值,且无法通过 callapplybind 方法改变。

总的来说,this 的指向是动态变化的,根据代码执行的上下文而定。

五 call/apply/bind

在 JavaScript 中,call()apply()bind() 这三个方法都可以用来改变函数内部的 this 指向。它们的参数传入方式略有不同:

  • call() 方法传入的参数是一个列表,可以是一个一个的参数;
  • apply() 方法传入的参数是一个数组,可以包含多个参数;
  • bind() 方法传入的参数是一个列表,与 call() 类似,但它不会立即调用原函数,而是返回一个新的函数。

下面是具体的解释:

  • call(thisArg, arg1, arg2, ...)

    • thisArg:指定函数内部的 this 指向的对象。
    • arg1, arg2, ...:函数的参数,可以是多个单独的参数,按顺序传入。
  • apply(thisArg, [argsArray])

    • thisArg:指定函数内部的 this 指向的对象。
    • argsArray:一个数组,包含函数的参数,数组中的每个元素对应一个函数参数。
  • bind(thisArg, arg1, arg2, ...)

    • thisArg:指定函数内部的 this 指向的对象。
    • arg1, arg2, ...:函数的参数,可以是多个单独的参数,按顺序传入。

下面是一个示例,演示了这三种方法的用法:

function greet(name) {
    console.log(`Hello, ${name}!`);
}

const person = {
    name: 'Alice'
};

// 使用 call()
greet.call(person, 'Bob');

// 使用 apply()
greet.apply(person, ['Bob']);

// 使用 bind()
const greetBound = greet.bind(person, 'Bob');
greetBound();

当然,我可以为你提供一个简单的实现示例,请查看下面的代码:

// 实现 call 方法
Function.prototype.customCall = function(context, ...args) {
    context = context || window; // 如果未传入 context,默认为全局对象 window
    const fn = Symbol(); // 创建一个唯一的 Symbol 属性以防冲突
    context[fn] = this; // 将当前函数赋值给 context 的一个属性
    const result = context[fn](...args); // 执行函数
    delete context[fn]; // 删除临时属性
    return result; // 返回执行结果
};

// 实现 apply 方法
Function.prototype.customApply = function(context, args) {
    context = context || window;
    const fn = Symbol();
    context[fn] = this;
    const result = context[fn](...args);
    delete context[fn];
    return result;
};

// 实现 bind 方法
Function.prototype.customBind = function(context, ...args) {
    const fn = this;
    return function(...innerArgs) {
        return fn.customCall(context, ...args, ...innerArgs);
    };
};

// 测试
function greet(name) {
    console.log(`Hello, ${name}! My name is ${this.name}.`);
}

const person = {
    name: 'Alice'
};

greet.customCall(person, 'Bob');
greet.customApply(person, ['Bob']);
const greetBound = greet.customBind(person, 'Bob');
greetBound();

以上代码演示了如何实现自定义的 call()apply()bind() 方法。这些方法会改变函数内部的 this 指向,并且允许传入参数来调用函数。
这两段代码是用来实现自定义的 call()apply() 方法的。它们的功能是类似的,都是用来改变函数的 this 指向并调用该函数,只是参数传入方式略有不同。让我解释一下它们之间的区别:

区别:
  1. 参数传入方式

    • customCall() 方法使用了 rest 参数 ...args,可以接收多个参数,这些参数会直接传递给被调用的函数。
    • customApply() 方法接收的第二个参数是一个数组 args,它接收一个包含函数参数的数组,然后将数组中的参数展开传递给被调用的函数。
  2. 调用方式

    • customCall() 方法中,使用 ...args 直接将参数列表传递给被调用的函数。
    • customApply() 方法中,将参数数组 args 直接传递给被调用的函数。

虽然它们的功能类似,但是在使用方式上有一定的差异。在实际使用时,可以根据具体情况选择使用哪种方式,如果参数是以数组的形式传递更合适的话,可以选择使用 customApply() 方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1547047.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FlyControls 是 THREE.js 中用于实现飞行控制的类,它用于控制摄像机在三维空间中的飞行。

demo演示地址 FlyControls 是 THREE.js 中用于实现飞行控制的类,它用于控制摄像机在三维空间中的飞行。 入参: object:摄像机对象,即要控制的摄像机。domElement:用于接收用户输入事件的 HTML 元素,通常…

某音乐歌曲链接接口加密算法刨析

如可逆向:某抑云音乐歌曲链接接口逆向-CSDN博客 补环境方式:某抑云音乐歌曲链接接口逆向之补环境-CSDN博客 问题由来 上述方式中【特指补环境】当我们将环境补齐 Javascript 【V8环境】去运行构建出来得值是可以使用得, 但使用Python去调起使…

SpringBoot+ElasticSearch实现文档内容抽取、高亮分词、全文检索

需求 产品希望我们这边能够实现用户上传PDF、WORD、TXT之内得文本内容,然后用户可以根据附件名称或文件内容模糊查询文件信息,并可以在线查看文件内容。 一、环境 项目开发环境: 后台管理系统springbootmybatis_plusmysqles 搜索引擎&#…

2024.3.26学习笔记

今日学习韩顺平java0200_韩顺平Java_对象机制练习_哔哩哔哩_bilibili 今日学习p273-p285 包 包的本质实际上就是创建不同的文件夹/目录来保存类文件 包的三大作用 区分相同名字的类 当类很多时,可以很好的管理类 控制访问范围 包的基本语法 package com.xx…

函数进阶-Python

师从黑马程序员 函数中多个返回值的接收 def test_return():return 1,"hello",3x,y,ztest_return() print(x) print(y) print(z) 多种参数的使用 函数参数种类 位置参数 关键字参数 def user_info(name,age,gender):print(f"姓名是{name},年龄是:{age},性别是…

nandgame中的控制单元(Control Unit)

关卡说明的翻译: 控制单元除了ALU指令之外,计算机还应支持数据指令。在数据指令中,指令值直接写入A寄存器。创建一个控制单元,根据指令I的高位执行数据指令或ALU指令:位 15 0 数据指令 1 ALU指令ALU指令 对于ALU指令&…

Linux虚拟机的安装部署--尚硅谷笔记

part1 VMware的使用 学习目标 1 熟悉VMware软件的使用 2 可以熟练为虚拟计算机安装Linux操作系统 3 能独立解决安装过程中的常见问题 第一节 VMware的作用 VMware软件的作用 ![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传] 第一步,在W…

【最新!红外小目标检测算法HCFNet】

文章目录 摘要1 引言2 相关工作2.1 传统方法2.2 深度学习方法 3 方法3.1 PPA3.2 维度感知选择性整合模块3.3 多稀释通道细化器模块3.4 损失函数设计 4 实验4.1 数据集与评估指标4.2 实现细节4.3 消融和对比 5 结论 论文:HCF-Net: Hierarchical Context Fusion Netwo…

监控API的指标

监控服务器已经是常态了,但是监控API的表现是啥意思呢?还有监控指标?今天就来看看如何监控API。 正如监控应用程序以确保高质量性能一样,也必须监控API。 API是应用程序相互通信的管道。更具体地说,API提供了一种方法…

Day42:WEB攻防-PHP应用MYSQL架构SQL注入跨库查询文件读写权限操作

目录 PHP-MYSQL-Web组成架构 PHP-MYSQL-SQL常规查询 手工注入 PHP-MYSQL-SQL跨库查询 跨库注入 PHP-MYSQL-SQL文件读写 知识点: 1、PHP-MYSQL-SQL注入-常规查询 2、PHP-MYSQL-SQL注入-跨库查询 3、PHP-MYSQL-SQL注入-文件读写 MYSQL注入:&#xff…

ROM-IP

1.原理 通过添加数据文件,使ROM看起来不是易失性存储器, 产生256个数据,每个数据的位宽是8 如果前面为x,后面就是x256-1 2.单端口ROM配置 FPGA内部没有非易失性存储器。调用的ROM和RAM都是用RAM来生成的 3.双端口ROM配置 使用第一…

《操作系统导论》第10章读书笔记:多处理器调度(高级)

《操作系统导论》第10章读书笔记:多处理器调度(高级) —— 杭州 2024-03-26 夜 文章目录 《操作系统导论》第10章读书笔记:多处理器调度(高级)1.背景:多处理器架构2.别忘了同步3.最后一个问题:缓存亲和度4.单队列调度和多队列调度…

GDAl 之绘制栅格图像的大致直方图和精准直方图(8)

gdal的绘制大致直方图是仅查看概览或者抽样像素的一个子集 import os from osgeo import gdal import matplotlib.pyplot as plt import numpy as np# Dont forget to change directory. os.chdir(rD:\DeskTop\learn_py_must\Learn_GDAL\osgeopy-data\osgeopy-data\Switzerlan…

Obsidian+PicGo+Gitee搭建免费图床

之前使用PicGoGitee配合Typora,后来因为换电脑Typora管理笔记不方便,换到Obsidian笔记,此处记录重新搭建图床的坑与经验。 主要参考# picGogitee搭建Obsidian图床,实现高效写作! 1 下载安装PicGo 下载链接https://mo…

Nginx(Docker 安装的nginx)配置域名SSL证书

1.首先确保Linux环境上已经安装了docker(可参考VMware使用和Linux安装Docker_wmware直接部署linux和安装docker后-CSDN博客 2.通过docker 安装nginx(可参考Linux 环境安装Nginx—源码和Dokcer-CSDN博客) 3.安装SSL证书 3.1 在宿主机中创建…

Java零基础入门到精通_Day 2

18 算数运算符 - * / % 整数的运算只能得到整数 除非用浮点数进行运算(得到浮点数) public class Base_002 {public static void main(String[] args) {double a 6.0;int b 4;System.out.println(a/b); //1.5} } 19 字符的操作 public class Base_0…

鸿蒙OS封装【axios 网络请求】(类似Android的Okhttp3)

Okhttp.ets /*** 网络请求*/ import axios from ohos/axios import httpConstants from ../net/HttpConstants import errorCode from ../utils/errorCode import toast from ../utils/ToastUtils import router from ../utils/RouterUtils import SPUtils from ../utils/SPUt…

Transformer的前世今生 day08(Positional Encoding)

前情提要 Attention的优点:解决了长序列依赖问题,可以并行。Attention的缺点:开销变大了,而且不存在位置关系为了解决Attention中不存在位置关系的缺点,我们通过位置编码的形式加上位置关系 Positional Encoding&…

【保姆级讲解Edge兼容性问题解决方法】

🌈个人主页:程序员不想敲代码啊🌈 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家🏆 👍点赞⭐评论⭐收藏 🤝 希望本文对您有所裨益,如有不足之处,欢迎在评论区提…

头条网盘如何快速获取授权推广

近期可以说是网盘拉新的一个盛宴,好几家网盘为了抢夺用户,都在付费拉新用户,而如今头条网盘也需要开拓市场,方式也很简单粗暴,就是拿钱砸,而对于普通用户来说,只要获得授权,正是赚钱…