0

0

JavaScript专题之一:变量提升与预编译

coldplay.xixi

coldplay.xixi

发布时间:2021-03-02 09:42:42

|

2081人浏览过

|

来源于CSDN

转载

JavaScript专题之一:变量提升与预编译

目录

  • 前言
  • 一、有趣的现象
  • 二、Js的预解析
  • 三、提升之间的优先级
  • 四、ES6
  • 写在最后

(相关免费学习推荐:javascript视频教程

前言

立即学习Java免费学习笔记(深入)”;

本篇文章是《JavaScript专项进阶系列》的第一篇文章,全系列大概会包含例如:

  • 防抖节流
  • 扁平化
  • 深浅拷贝
  • 数组去重
  • 排序

等等经典的专项知识点。取名为专项进阶是因为它们在很多场合的“出镜率高”很高,为了避免化身google内容搜索师,《JavaScript专项进阶系列》诞生了!!!

在这里插入图片描述

一、有趣的现象

按照大家的常识,JavaScript代码在执行是一定是自上而下的,你需要输出一个字符串,当然需要提前声明一个保存string类型的变量。如果深奥的道理我都能懂,于是我阅读了下面的代码。

1.1 我以为的开局
var str = '123';console.log(str); // 123

我们调换一下代码的位置在再看:

console.log(str); // undefinedvar str = '123';

我好像找到规律了!!!

当我看完了前两段代码并且进行了“深度思考”后,我好像找到规律了,那就是:在当前代码块后函数中,在变量声明和初始化之前使用变量,会拿不到正确的值。

JavaScript专题之一:变量提升与预编译

1.2 实际上是这样的

带着上面的“结论”我来到了这里

var val = '余光';(function(){
    console.log(val); // 余光})();

果然如此!,在变量声明和初始化之后耶稣也阻挡不了我拿到val的值,我说的!!!

当我看到下面一段代码时,我已经产生了动摇,此事必要蹊跷。

var val = '余光';(function(){
    console.log(val); // undefined
    var val = '测试';})();

Ps:如果大家立即执行函数存在疑问,不妨看看《JavaScript之深入理解立即调用函数表达式(IIFE)》吧~

这…我怂了,是什么原因导致这样的现象发生的呢?Js又是如果处理的呢?

JavaScript专题之一:变量提升与预编译

二、Js的预解析

在当前的作用域内,无论在哪里变量声明,在幕后,都会进行一次看不见的移动。

注意:仅声明被“移动”。即声明和赋值在某些时候被动分开了。而这次看不见的移动实际上就是Js在编译阶段的解析

来看一段《你知不知道的Js》中经典的例子:

name = '余光'; // 未添加关键字(未声明),name为全局变量,,即window.name = '余光'var name; // 再次声明name,此时name未进行初始化,它的值是undefined吗?console.log(name); // ?

结果是成功打印“余光”,这样看不见的移动就发生在Js预解析(编译)之中。

智川X-Agent
智川X-Agent

中科闻歌推出的一站式AI智能体开发平台

下载
2.1 核心:预解析

为了搞明白这个核心问题,我们需要回顾一下,引擎会在解释JavaScript代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。感兴趣的小伙伴可以阅读《JavaScript中的变量对象》和《从作用域到作用域链》这两篇文章哦~

因此,发生这样的事情,包括变量函数在内的所有声明都会在任何代码被执行前首先被处理。当你看到var a = 2;时,可能会认为这是一个声明。但JavaScript实际上会将其看成两个声明:var a;和a = 2;。

  • 第一个定义声明是在编译阶段进行的。
  • 第二个赋值声明会被留在原地等待执行阶段。

即代码是这样写的:

// 我们看到的代码:var name = '余光';

但Js会将它解析成:

// 声明(Declaration)var name; // 声明但未初始化,所以分配 undefined// 初始化(Initialization)name = '余光'; // 初始化(赋值)

所以本小结的一段代码应该这样分析:

var name; // 声明name提到作用域顶部,并被分配了一个undefinedname = '余光'; // 进行初始化操作console.log(name); // '余光'
2.2 注意:只有声明被提升了

只有声明会被提升,而赋值和其他代码逻辑会在执行到代码的位置时才会生效。所以会有下面的问题:

foo();function foo(){
    console.log(name); // undefined
    var name = '余光';}

函数被提升了,自然可以正常执行,但变量仅仅是声明被提升了。

2.3 每个作用域都会进行提升操作

还是上面的代码:

foo();function foo(){
    console.log(name); // undefined
    var name = '余光';}

实际它在编译时是这样的:

function foo(){
    var name; // 声明
    console.log(name); // undefined
    name = '余光'; // 初始化}foo(); // 函数执行

JavaScript专题之一:变量提升与预编译

三、提升之间的优先级

既然我们知道了变量函数会被提升,他们之间又是如何判断优先级的呢?

3.1 函数会被首先提升,然后才是变量

我们分析下面的代码:

foo();var foo; // 1function foo(){
    console.log('余光');}foo = function(){
    console.log('小李');}

本着函数优先提升的原则,他会被解析成这样:

function foo(){
    console.log('余光');}foo(); // 余光foo = function(){
    console.log('小李');}

注意,var foo 因为是一个重复声明,且优先级低于函数声明所以它被忽略掉了。

3.2 函数字面量不会进行函数提升

最直观的例子,就是在函数字面量前调用该函数:

foo();var foo = function(){
    console.log(1);}// TypeError: foo is not a function

这段程序中:

  1. 变量标识符foo被提升并分配给所在作用域(在这里是全局作用域),因此在执行foo()时不会导致ReferenceError(),而是会提示你 foo is not a function
  2. 然后就是执行foo,foo此时并没有赋值(注意变量被提升了)。由于对undefined值进行函数调用而导致非法操作,因此抛出TypeError异常。

四、ES6和小结

ES6新增了两个命令letconst,用来声明变量,有关它们完整的概念我会在《ES6基础系列》中总结,提起它们,是因为变量提升在它们身上不会存在

4.1 变量提升是可以规避的

let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

// var 的情况console.log(foo); // 输出undefinedvar foo = 2;// let 的情况console.log(bar); // 报错ReferenceErrorlet bar = 2;

上面代码中,变量foo用var命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。

在变量提升上,const和let一样,只在声明所在的块级作用域内有效,也不会变量提升

4.2 小结
  1. 变量提升:函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部,但变量的初始化不会提升;
  2. 函数提升:函数声明可以被看作是函数的整体被提升到了代码的顶部,但函数字面量表达式并不会引发函数提升;
  3. 函数提升优先与变量提升;
  4. let和const可以有效的规避变量提升

最后提炼一下:JavaScript引擎并不总是按照代码的顺序来进行解析。在编译阶段,无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理,这个过程被称为提升。声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升。

相关免费学习推荐:javascript(视频)

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

38

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

83

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

97

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

223

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

458

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

169

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

246

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

34

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号