前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【前端】JavaScript作用域与预解析:深入理解问题与解答

【前端】JavaScript作用域与预解析:深入理解问题与解答

作者头像
CSDN-Z
发布于 2024-11-26 06:37:30
发布于 2024-11-26 06:37:30
12100
代码可运行
举报
文章被收录于专栏:AIGCAIGC
运行总次数:0
代码可运行

💯前言

  • 在 JavaScript 中,作用域、变量提升(Hoisting)、函数声明与赋值的行为经常是开发者在学习中遇到的难点。本文通过对几道经典的代码解析题目进行详细剖析,来帮助你更好地理解 JavaScript 的执行顺序和作用域链。我们会一一讨论题目中的预解析结果、具体的输出以及背后的原因。 JavaScript

💯题目一:局部变量与提升

代码分析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var x = 20;

function fn() {
    if (x) {
        var x = 30;
    }
    console.log(x); // 这里打印值是多少?
}

fn();
console.log(x); // 这里打印值是多少?

预解析后的代码结构 在 JavaScript 中,变量声明和函数声明会被提升到各自作用域的顶部。因此,函数 fn 中的 var x 也会被提升到函数顶部。预解析后的代码结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var x; // 全局变量声明提升,初始值为 undefined
function fn() {
    var x; // 局部变量声明提升,初始值为 undefined

    if (x) {
        x = 30; // 不会执行,因为 x 是 undefined,条件为 false
    }
    console.log(x); // 输出 undefined
}

// 执行阶段
x = 20; // 全局变量赋值
fn();   // 调用函数 fn,进入局部作用域
console.log(x); // 输出 20

详细执行过程

  1. 全局作用域提升阶段:
    • var x 提升到全局作用域顶部,初始值为 undefined
    • 函数 fn 提升到全局作用域顶部,完整保留。
  2. 执行 fn() 函数:
    • fn 内的 var x 被提升到函数顶部,初始值为 undefined
    • if (x) 语句中,x 的值为 undefined,所以条件为 false,不会进入 if 块。
    • console.log(x) 输出的是局部变量 x 的值,即 undefined
    • 然后在全局作用域中,console.log(x) 输出全局变量的值 20

输出结果:

  • fn() 内的 console.log(x) 输出:undefined
  • 全局的 console.log(x) 输出:20

思考要点:

  • 局部变量 x 遮蔽了全局变量 x,即使局部变量未赋值。
  • if (x) 判断时,使用的是函数作用域中的局部 x,初始值是 undefined

💯题目二:函数参数与变量提升

代码分析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function fun(param) {
    console.log(param); // 这里打印值是多少?
    var param = function () {
        console.log(1);
    };
    console.log(param); // 这里打印值是多少?
}

fun(5);

预解析后的代码结构 在 JavaScript 中,当函数参数和局部变量同名时,参数的声明会被优先考虑,但局部变量的声明会被忽略,赋值操作仍然会执行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function fun(param) {
    // 参数 `param` 被提升,作为函数的局部变量,初始值为传递的参数值 5
    console.log(param); // 输出 5
    param = function () { // 赋值操作覆盖了参数的值
        console.log(1);
    };
    console.log(param); // 输出函数体
}

// 执行阶段
fun(5);

详细执行过程

  1. 调用 fun(5) 时,参数 param 被赋值为 5
  2. 第一次 console.log(param)
    • 输出 5,因为此时 param 的值是传递的参数。
  3. 执行 param = function() { console.log(1); }
    • param 被赋值为一个函数,覆盖了之前的参数值。
  4. 第二次 console.log(param)
    • 输出 function () { console.log(1); },因为此时 param 已经被覆盖为该函数。

输出结果:

  • 第一次 console.log(param) 输出:5
  • 第二次 console.log(param) 输出:function () { console.log(1); }

思考要点:

  • 函数参数会优先于函数内部的 var 声明。
  • var param 的声明被忽略,但赋值操作仍然执行,因此覆盖了参数 param 的值。

💯题目三:函数声明与局部变量遮蔽

代码分析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var foo = 1;

function bar() {
    function foo() {}
    foo = 10;
    console.log(foo); // 这里打印值是多少?
}

bar();
console.log(foo); // 这里打印值是多少?

预解析后的代码结构

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var foo; // 全局变量声明,初始值 undefined
function bar() {
    function foo() {} // 局部变量 foo,初始值为函数
    foo = 10;         // 覆盖局部变量 foo 的值
    console.log(foo); // 输出 10
}

// 执行阶段
foo = 1; // 全局变量赋值
bar();   // 调用 bar 函数
console.log(foo); // 输出 1

详细执行过程

  1. 全局作用域提升阶段:
    • var foo 提升,初始值为 undefined
    • 函数 bar 提升到全局作用域顶部。
    • bar 函数中,function foo() 声明被提升到 bar 内部顶部,声明了一个局部变量 foo,初始值为函数。
  2. 执行阶段:
    • 调用 bar() 时,局部变量 foo(最初是一个函数)被重新赋值为 10
    • console.log(foo) 输出 10,因为局部变量 foo 已被赋值为 10
    • 最后在全局作用域中,console.log(foo) 输出全局变量的值 1,因为全局变量 foo 没有被 bar 内部的操作影响。

输出结果:

  • bar() 内的 console.log(foo) 输出:10
  • 全局的 console.log(foo) 输出:1

思考要点:

  • 函数声明 function foo()bar 中被提升,相当于声明了一个局部变量。
  • foo = 10 是对局部变量的重新赋值,不会影响全局变量。

💯拓展知识:函数声明与变量声明的提升

在 JavaScript 中,函数声明的提升优先于变量声明。这意味着函数会被完整地提升到作用域的顶部,并且在变量声明之前可以使用。但变量声明只会被提升声明,赋值操作仍然保留在原地。

函数声明是局部变量的声明

在题目三中,function foo() 实际上相当于声明了一个局部变量 foo,并将其初始化为一个函数对象。正因如此,当后续的 foo = 10 进行赋值时,修改的是这个局部变量,而不是全局的 foo

这说明函数声明不仅仅是定义一个可调用的代码块,它也会在当前作用域中创建一个变量,这个变量的名字与函数名相同。

变量遮蔽与作用域链

当函数内部声明一个与外部变量同名的变量时,这个内部变量会遮蔽外部的变量,这就是所谓的作用域链中的遮蔽现象。遮蔽意味着在当前作用域中,同名的外部变量变得不可访问,除非使用特定的方式(例如 this 或全局对象)访问全局变量。

💯小结

通过以上几个例子,我们深入理解了 JavaScript 中的作用域、变量提升函数声明与变量声明之间的关系。关键点包括:

  1. 函数参数与变量声明的优先级: 函数参数会优先于函数内部的同名 var 声明
  2. 函数声明是局部变量的声明: 在函数内部声明函数,相当于在当前作用域创建了一个局部变量,且初始值为函数对象
  3. 变量遮蔽: 函数内部的变量会遮蔽外部的同名变量,函数声明也会有类似的行为。
  4. 预解析的重要性: 理解 JavaScript预解析过程有助于理解代码的执行顺序,尤其是复杂的作用域和变量提升问题。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验