首页 > 编程语言 >解码JavaScript作用域:var a = 1; 背后的故事

解码JavaScript作用域:var a = 1; 背后的故事

时间:2024-11-15 17:14:15浏览次数:3  
标签:console 变量 作用域 JavaScript 查找 var

1、引言

JavaScript 是一种广泛使用的编程语言,其灵活的语法和强大的功能使其成为前端开发的首选语言。然而,对于初学者来说,理解 JavaScript 中的作用域机制可能会有些困难。本文将通过一句简单的代码 var a = 1;,深入解析 JavaScript 的执行机制作用域管理

2、变量声明与初始化

在 JavaScript 中,var a = 1; 这一行代码虽然简单,但其实涉及到了多个重要的概念:变量声明、初始化、编译和执行阶段。

2.1 变量声明

  • 声明var a; 这一行代码在编译阶段执行。JavaScript 引擎会在当前作用域中创建一个名为 a 的变量。这个过程称为变量声明。
  • 关键字 varvar 是 JavaScript 中用于声明变量的关键字。它告诉编译器在当前作用域中创建一个新的变量。
  • 变量标识符 aa 是变量的名称,也称为标识符。在 JavaScript 中,标识符必须遵循一定的命名规则,例如不能以数字开头,不能包含特殊字符等。

2.2 变量初始化

  • 赋值a = 1; 这一行代码在执行阶段执行。JavaScript 引擎会查找变量 a,找到后为其赋值为 1。这个过程称为变量初始化。
  • 内存存储:变量 a 实际上存储在内存中。JavaScript 引擎会为每个变量分配一块内存空间,用于存储变量的值。

当然,变量的作用域是 JavaScript 中一个非常重要的概念。理解作用域可以帮助我们更好地管理变量的生命周期和可见性,避免命名冲突和潜在的错误。

3、作用域的概念

3.1 作用域

  • 作用域定义了变量的可访问范围,即变量在哪些代码块中是可见的。
  • 作用域决定了变量的生命周期,即变量何时被创建和销毁。

3.2 作用域的类型

  • 全局作用域:在整个程序或文件中都可访问的变量。

  • 局部作用域

    • 函数作用域:在函数内部定义的变量只能在该函数内部访问。
    • 块作用域:在 {} 块内定义的变量只能在该块内访问,常见于 if 语句、for 循环等。

4、作用域的查找规则

4.1 编译阶段:

  • 在编译阶段,JavaScript 引擎会解析代码,识别出所有的变量声明,并将它们提升到相应的作用域顶部。
  • 变量声明(如 varletconst)会被提升,但只有 var 会同时提升初始化。

4.2 执行阶段:

  • 当代码执行时,JavaScript 引擎会按照以下顺序查找变量
    • 当前作用域:首先在当前作用域中查找变量。
    • 父级作用域:如果在当前作用域中找不到,会在父级作用域中继续查找。
    • 全局作用域:如果在所有父级作用域中都找不到,会在全局作用域中查找。
    • undefined:如果在全局作用域中也找不到,变量被视为 undefined,此时会抛出引用错误(ReferenceError)。

4.3 局部作用域

局部作用域通常指函数内部。在函数内部声明的变量只能在其内部被访问,不能在函数外部访问。

function example() {
  var a = 1;
  console.log(a); // 输出 1
}
console.log(a); // 抛出 ReferenceError: a is not defined

4.4 父级作用域

如果一个变量在当前作用域找不到,则会向上一级作用域查找,直到找到为止。这个过程称为作用域链。

var a = 1;
function example() {
  console.log(a); // 输出 1
}
example();

4.5 全局作用域

如果变量在所有局部作用域中都找不到,则会检查全局作用域。在浏览器环境中,全局作用域就是 window 对象。

var a = 1;
function example() {
  console.log(window.a); // 输出 1
}
example();

4.5 undefined

undefined 是 JavaScript 中的一个基本数据类型,表示一个未定义的值。在变量声明但未赋值的情况下,变量的默认值为 undefined

var a = 1;
var b = 4;
function foo(){
    // 编译阶段 完成声明 undefined
    console.log(a,b);
    var a = 2;
    var a = 3;
    console.log(a,b);
}
foo();// 输出 undefined 4
                      3 4

第一次 console.log(a, b);

  • 在这一步,a 和 b 都已经在编译阶段被声明了,但 a 还没有被赋值,所以它的值是 undefined
  • b 是在全局作用域中声明并初始化的,所以在 foo 函数中可以通过作用域链找到 b,其值为 4。

因此,第一次 console.log(a, b); 的输出是:undefined 4

第二次 console.log(a, b);

  • 在这一步,a 的值已经被赋值为 3,b 的值仍然是 4。

因此,第二次 console.log(a, b); 的输出是:3 4

5、LHS 查找和 RHS 查找

在JavaScript中,引擎在执行代码时会进行变量查找,这些查找可以分为两种类型:LHS(Left-Hand Side)查找和RHS(Right-Hand Side)查找。这两种查找方式在不同的上下文中有着不同的用途和行为。

5.1 LHS 查找(Left-Hand Side Lookup)

定义: LHS查找主要用于赋值操作,确保变量存在或创建新变量。简单来说,LHS查找是为了找到一个变量的存储位置,以便将一个值赋给它。

示例:

var a;
a = 10; // LHS 查找

在这个例子中,a = 10 这一行代码触发了一个LHS查找。JavaScript引擎需要找到变量a的存储位置,以便将值10赋给它。如果变量a之前已经声明过,则直接找到其存储位置;如果变量a之前未声明过,则在当前作用域中创建一个新的变量a

作用:

  • 赋值操作:将一个值赋给一个变量。
  • 变量声明:如果变量在当前作用域中不存在,则创建一个新的变量。

注意事项:

  • LHS查找不仅用于简单的赋值操作,还用于函数调用中的参数传递。
function foo(a) {
    console.log(a);
}

foo(10); // LHS 查找:找到函数foo的参数a的存储位置
  • 如果变量在当前作用域中不存在,则会创建一个新的变量。
function example() {
    // 在函数作用域中,a 之前未声明
    a = 10; // LHS 查找:在当前作用域中查找 a,如果不存在则创建新变量 a 并赋值为 10
    console.log(a); // 输出: 10
}

example();

// 检查 a 是否在全局作用域中被创建
console.log(a); // 输出: 10
  • 严格模式
'use strict';
function example() {
    a = 10; // 这里会抛出 ReferenceError
    console.log(a);
}

example();

// 检查 a 是否在全局作用域中被创建
console.log(a); // 这行代码不会执行,因为前面已经抛出了错误

5.2 RHS 查找(Right-Hand Side Lookup)

定义: RHS查找主要用于获取变量的值。简单来说,RHS查找是为了找到一个变量的值,而不是它的存储位置。如果变量不存在,则会抛出一个引用错误(ReferenceError)。

示例:

var a = 20;
var b = a; // RHS 查找

在这个例子中,let c = b 这一行代码触发了一个RHS查找。JavaScript引擎需要找到变量b的值,然后将这个值赋给变量c。如果变量b之前未声明或未初始化,则会抛出一个引用错误。

作用:

  • 获取变量的值:用于读取变量的值。
  • 表达式求值:在表达式中使用变量时,会触发RHS查找。

注意事项:

  • RHS查找在变量未声明或未初始化时会抛出引用错误(ReferenceError)。例如:
var a = e; // ReferenceError: e is not defined

6、作用域嵌套和作用域链

在JavaScript中,作用域(Scope)决定了变量和函数的可访问性。作用域可以嵌套,形成一个作用域链(Scope Chain),这个链决定了变量的查找顺序。理解作用域嵌套和作用域链对于编写高效、可维护的代码至关重要。

6.1 作用域嵌套

作用域可以嵌套,形成一个层级结构。每个内部作用域都可以访问其外部作用域中的变量和函数,但外部作用域不能访问内部作用域中的变量和函数。

function outerFunction() {
    var outerVariable = 'I am outer';

    function innerFunction() {
        var innerVariable = 'I am inner';
        console.log(outerVariable); // 访问外部作用域的变量
        console.log(innerVariable); // 访问内部作用域的变量
    }

    innerFunction();
}

outerFunction();
// console.log(innerVariable); // ReferenceError: innerVariable is not defined

6.2 作用域链

作用域链是JavaScript引擎在查找变量时遵循的一系列作用域。查找顺序是从当前作用域开始,逐级向上查找,直到找到变量或到达全局作用域。

  • 作用域链的查找顺序:当前作用域 -> 父级作用域 -> 全局作用域 -> undefined
var globalVariable = 'I am global';

function outerFunction() {
    var outerVariable = 'I am outer';

    function innerFunction() {
        var innerVariable = 'I am inner';
        console.log(innerVariable); // I am inner
        console.log(outerVariable); // I am outer
        console.log(globalVariable); // I am global
        //console.log(undefinedVariable); // ReferenceError: undefinedVariable is not defined
    }

    innerFunction();
}

outerFunction();

7、执行原理

var a = 1; 是一条JavaScript语句,用于声明一个变量 a 并将其初始化为数值 1。这条语句在JavaScript环境中执行,可以是浏览器中的JavaScript引擎(如Chrome的V8引擎),Node.js服务器环境,或者其他支持JavaScript的环境。

当这条语句被执行时,以下是一些与之相关的工作原理或组件:

7.1 解析器 (Parser)

当代码被加载时,解析器会将源代码转换成抽象语法树(AST)。对于 var a = 1; 这样的语句,解析器会识别出这是一个变量声明,并且该变量被赋予了一个初始值。

7.2 执行上下文 (Execution Context)

在JavaScript中,每当一个函数被调用或者全局代码开始执行时,都会创建一个新的执行上下文。在这个上下文中,变量和函数会被初始化。对于全局执行上下文,var a = 1; 将会在全局对象(在浏览器中是window对象)上创建一个属性。

7.3 内存分配 (Memory Allocation)

变量 a 需要占用一定的内存空间来存储其值。在声明 var a = 1; 时,JavaScript引擎会为这个变量分配适当的内存空间,并将数值 1 存储到这块内存中。

7.4 作用域链 (Scope Chain)

如果这条语句是在一个函数内部执行的,那么它将成为该函数局部作用域的一部分。这意味着只有在同一作用域或其子作用域内的代码才能访问到 a。如果是在全局作用域下,则任何地方都可以访问到 a

7.5 垃圾回收 (Garbage Collection)

如果变量 a 不再被任何活动的执行上下文引用,JavaScript的垃圾回收机制最终会释放与 a 相关的内存资源,以节省内存。

8、总结

作用域是JavaScript中的一个核心概念,它决定了变量的可见性、生命周期以及如何被访问。通过理解作用域及其相关概念,如LHS查找、RHS查找、作用域嵌套等,开发者可以编写出更加健壮、高效且易于维护的代码。同时,遵循最佳实践,如使用 let 和 const 代替 var、避免全局变量、合理使用作用域嵌套等,可以进一步提升代码质量,减少潜在的错误和问题。

标签:console,变量,作用域,JavaScript,查找,var
From: https://blog.csdn.net/Cwayhome/article/details/143804088

相关文章

  • 【JavaScript】LeetCode:96-100
    文章目录96单词拆分97最长递增子序列98乘积最大子数组99分割等和子集100最长有效括号96单词拆分动态规划完全背包:背包-字符串s,物品-wordDict中的单词,可使用多次。问题转换:s能否被wordDict中的单词组成。dp[i]:长度为i的字符串s[0,i]能否被wordDict组成,dp[i]=......
  • 【JavaScript】LeetCode:91-95
    文章目录91不同路径92最小路径和93最长回文子串94最长公共子序列95编辑距离91不同路径动态规划dp[i][j]:从[0,0]到[i,j]的路径条数。dp[i][j]=从[0,0]到[i,j]上面一格的路径条数+从[0,0]到[i,j]左边一格的路径条数。初始化:因为第一行的格子只能由左......
  • JavaScript介绍与使用
    1.认识jsjs全称(javascript):是网页上面的一个脚本语言运行执行代码逻辑从而达到我们需要的效果2.JavaScript组成核心语法-ECMAScript:规范了JS的基本语法Es是js的语法规范管理者js是Es的实现操作者DOM=>文档对象提供js操作BOM=>浏览器模型对象提供......
  • JavaScript常用对象方法二:数组(array)
    1.concat()用于连接两个或多个数组。该方法不会改变现有的数组,而是返回一个新的数组。个人感觉es6出来的扩展运算符比这个方法要简洁一些扩展运算符的方法:constarr1=[1,2];constarr2=[3,4];constarr3=[...arr1,...arr2];console.log(arr3);//[1,2,......
  • [原创]手把手教学之前端0基础到就业——day11( Javascript )
    文章目录day11(Javascript)01Javascript①Javascript是什么②JavaScript组成③Javascript的书写位置1.行内式(不推荐)2.内部位置使用(内嵌式)3.外部位置使用(外链式)02变量1.什么是变量2.定义变量及赋值3.注意事项4.命名规范03输入和输出1)输出形式1......
  • 检测 HTML5\CSS3\JAVASCRIPT 在浏览器的适应情况
    https://www.cnblogs.com/czhyuwj/p/4796690.html CSS3SelectorsTest:这是CSS3.INFO网站提供的css选择器测试页面,它能够详细显示当前浏览器对所有CSS3选择器的支持情况。启动测试,浏览器会自动测验,并已列表的方式显示当前浏览器对所有css3选择器的支持情况  http://tool......
  • Verilog中genvar 和 generate的使用
    1.genvar和generate的作用genvar是一种特殊的数据类型,用于在generate语句块中定义循环变量。与普通变量不同的是,genvar只能用于generate语句中,并且只能用于生成时刻(编译时)进行评估,而非仿真时。generate块用于生成硬件逻辑。它允许使用for循环、if条件语句等来创......
  • 两个新出的 JavaScript 运算符
    在ECMAScript2021(ES12)中,JavaScript引入了新的逻辑赋值操作符&&=和??=。这些操作符将逻辑运算符与赋值运算符相结合,提供了更加简洁、直观的赋值方式。虽然已经进入标准比较久了,但是我在实际开发中见到的还比较少,今天我们一起来学习下。逻辑与赋值操作符&&=&&=的工作原理......
  • vite3+vue3 实现前端部署加密混淆 javascript-obfuscator
    安装pnpminstalljavascript-obfuscator安装之后在项目根目录新建一个obfuscator.js在obfuscator.js写入以下代码直接复制粘贴`/**@用法vite打包完成后,使用命令行nodejs执行本文件:nodeobfuscator.js它会挨个把里面的js文件做混淆然后替换@说明本质就是依......
  • 24.11.12 JavaScript2
    prompt()confirm()这些函数会阻止js解析器(js解析器执行引擎读取运行js)执行不要使用2history对象历史记录对象对应浏览器前进后退按钮history在历史记录里back前进forward后退go0当前文档负数后......