js预解析(变量提升)导致了什么问题?

预解析

  • JS代码在执行前,浏览器会对js代码进行扫描,默认的把所有带var和function声明的变量或者函数进行提前的声明,遵循先解析后使用的原则。
  • 变量提升的表现是,在变量或函数声明之前访问变量或调用函数而不会报错。
变量提升
函数提升

原因:

JavaScript引擎在代码执行前有一个解析的过程(预编译),创建执行上下文,初始化一些代码执行时需要用到的对象。 当访问一个变量时,会到当前执行上下文中的作用域链中去查找,而作用域链的首端指向的是当前执行上下文的变量对象,这个变量对象是执行上下文的一个属性, 它包含了函数的形参、所有的函数和变量声明,这个对象的是在代码解析的时候创建的。

首先要知道,JS在拿到一个变量或者一个函数的时候,会有两步操作,即解析执行

  1. 在解析阶段 JS会检查语法,并对函数进行预编译。解析的时候会先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来, 变量先赋值为undefined,函数先声明好可使用。在一个函数执行之前,也会创建一个函数执行上下文环境,跟全局执行上下文类似, 不过函数执行上下文会多出this、arguments和函数的参数。
  2. 在执行阶段,就是按照代码的顺序依次执行。

全局上下文:变量定义,函数声明 函数上下文:变量定义,函数声明,this,arguments

为何预解析

那为什么会进行变量提升呢?主要有以下两个原因:

  1. 提高性能
  2. 容错性更好

(1)提高性能

在JS代码执行之前,会进行语法检查和预编译,并且这一操作只进行一次。这么做就是为了提高性能,如果没有这一步,那么每次执行代码前都必须重新解析一遍该变量(函数),而这是没有必要的,因为变量(函数)的代码并不会改变,解析一遍就够了。

在解析的过程中,还会为函数生成预编译代码。在预编译时,会统计声明了哪些变量、创建了哪些函数,并对函数的代码进行压缩,去除注释、 不必要的空白等。这样做的好处就是每次执行函数时都可以直接为该函数分配栈空间(不需要再解析一遍去获取代码中声明了哪些变量,创建了哪些函数), 并且因为代码压缩的原因,代码执行也更快了。

(2)容错性更好

变量提升可以在一定程度上提高JS的容错性,看下面的代码:

javascript 复制代码
    a = 1
    var a
    console.log(a) // 1 如果没有变量提升,这段代码就会报错导致的问题
    
    var tmp = new Date();
    function fn(){
        console.log(tmp);
        if(false){
            var tmp = 'hello nanjiu';
        }
    }
    fn();  // undefined

在这个函数中,原本是要打印出外层的tmp变量,但是因为变量提升的问题,内层定义的tmp被提到函数内部的最顶部, 相当于覆盖了外层的tmp,所以打印结果为undefined。

ini 复制代码
    var tmp = 'hello nan jiu';
    for (var i = 0; i < tmp.length; i++) {
        console.log(tmp[i]);
    }
    console.log(i); // 13

由于遍历时定义的i会变量提升成为一个全局变量,在函数结束之后不会被销毁,所以打印出来13

总结:

  • 解析和预编译过程中的声明提升可以提高性能,让函数可以在执行时预先为变量分配栈空间
  • 声明提升还可以提高JS代码的容错性,使一些不规范的代码也可以正常执行
  • 函数是一等公民,当函数声明与变量声明冲突时,变量提升时函数优先级更高,会忽略同名的变量声明

全局预编译和局部预编译

预编译分为全局预编译和局部预编译:

  • 全局预编译‌:在页面加载完成时执行,主要创建全局对象(Global Object),将所有全局变量和函数声明添加到这个对象上‌
  • 局部预编译‌:在函数执行前执行,主要创建激活对象(Activation Object),将所有局部变量和函数声明添加到这个对象上‌

实际应用中的问题和解决方案

在实际开发中,预编译可能导致一些难以发现的错误,例如在变量初始化之前就使用它,可能会导致不可预测的行为。为了避免这些问题,可以采取以下措施:

  • 使用let和const代替var,因为let和const不会提升,这样可以减少因提升导致的错误‌1。
  • 在使用变量之前确保其已被正确初始化。
相关推荐
Carlos_sam4 分钟前
Openlayers:flat样式介绍
javascript
the_one6 分钟前
🚀「v-slide-in」+ 瀑布流实战指南:Vue 高级滑入动画一键实现,页面质感瞬间拉满!
前端·javascript·css
asing7 分钟前
之家中后台前端解决方案 - 支点2.0
前端·javascript
阳树阳树43 分钟前
Solidjs 响应式 & 编译原理初探
前端·javascript·面试
MurphyChen1 小时前
前端请求进化史 :从 Form 到 Server Actions 🚀
前端·javascript·面试
magic 2452 小时前
ES6变量声明:let、var、const全面解析
前端·javascript·ecmascript·es6
好_快2 小时前
Lodash源码阅读-dropWhile
前端·javascript·源码阅读
好_快2 小时前
Lodash源码阅读-dropRightWhile
前端·javascript·源码阅读
请叫我欧皇i3 小时前
vue2使用ezuikit-js播放萤石视频
开发语言·javascript·ecmascript
IT专家-大狗4 小时前
Google Chrome Canary版官方下载及安装教程【适用于开发者与进阶用户】
开发语言·javascript·chrome·ecmascript