还原真实,javascript之预编译 / 预解析

今天在群里吹水时,有群友提出一个问题。我一看很简单,就立马给出了答案:因为存在变量提升,所以输出undefined。本以为无人反驳,可确招来口诛笔伐。作为写实派的我,一贯以来坚持真实是我的使命,岂能容忍这等虚头巴脑的言论攻击。遂以此文记之,本在还原真实。奈何文笔拙劣,恐表述不当,误人子弟。若有不当之处,还请众佬及时斧正,以正视听。



就是下面这段代码:

var obj = {
  say: function () {

      console.log(obj)

  }()
}

// 输出 undefined

为什么是undefined?

这个问题看似简单,不值一提,可实际上是小孩没娘,说来话长!

尽管如此,我还是长话短说。很简单,因为在javascript执行之前,是存在一个“预编译”或者说是“预解析”这样的过程,在这个过程当中,进行了变量提升。

什么是变量提升?
变量提升就是将变量名提升到其所在作用域的顶部并赋值为undefined。与之相关的还有函数提升,不同的是,函数提升是将函数名及函数体全部提升到其所在作用域的顶部

与其说“预编译”,“预解析”可能更合理一点。为什么这样说?
因为在编程语言(如java,C#)当中,代码都是要先编译,后执行。而javascript作为脚本语言,不同于编程语言的一点是没有编译过程,但是它需要脚本解释器边解析边执行,而脚本解释器在解释执行代码前会先扫描一遍,这个过程就是“预解析”过程。

那么为什么会存在变量提升和函数提升呢?
在ES5当中,没有块级作用域的概念,只有全局作用域和函数作用域。声明变量可以用var关键字,也可以直接声明,只不过直接声明的变量是全局变量,而用var关键字声明的变量在全局作用域下就是全局变量,在函数体内就是局部变量。并且var关键字声明的变量在其所在的作用域下存在变量提升。

em... 那变量提升跟上面的代码输出的结果是undefined有什么关系呢? 好,请坐下,陈独秀同学。

我们来用代码还原一下真实过程:

预解析阶段:
生成全局对象,浏览器当中是window对象,node环境下是global对象
变量提升:
全局作用域下,变量obj提升到顶部,直接挂在window对象下,相当于window.obj = undefined;
say方法是一个匿名函数。匿名函数的函数体赋值给了变量say。所以变量say也会进行变量提升,它所在的作用域也是全局window下,相当于window.say = undefined;

函数提升:

function(){
  console.log(obj);  //obj = window.obj = undefined
}

执行阶段:
1,创建一个执行上下文(execution context),函数压栈,生成active object(活动对象)
1,执行/解释上下文中的function,为变量赋值
2,

参考:https://www.jianshu.com/p/edb2be5866eb

  • 需要记住的是:

    • 变量提升只提升变量名,函数提升会提升函数名及其函数体。
    • 变量提示优先级大于函数提升优先级。也就是说不管变量声明在前还是函数声明在前,都是先进行变量提升,再进行函数提升。

执行阶段:

function(){
  console.log(obj); //输出undefined
}()

原文地址:https://www.cnblogs.com/xiaohuangmao/p/10697725.html

时间: 2024-08-27 12:08:48

还原真实,javascript之预编译 / 预解析的相关文章

mybatis深入理解之 # 与 $ 区别以及 sql 预编译

mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = "ruhua"; 上述 sql 中,我们希望 name 后的参数 "ruhua" 是动态可变的,即不同的时刻根据不同的姓名来查询用户.在 sqlMap 的 xml 文件中使用如下的 sql 可以实现动态传递参数 name: select * from user whe

mybatis深入理解(一)之 # 与 $ 区别以及 sql 预编译

mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = "ruhua"; 上述 sql 中,我们希望 name 后的参数 "ruhua" 是动态可变的,即不同的时刻根据不同的姓名来查询用户.在 sqlMap 的 xml 文件中使用如下的 sql 可以实现动态传递参数 name: select * from user whe

.Net Core Razor 预编译,动态编译,混合编译

预编译 预编译是ASP .Net Core的默认方式.在发布时,默认会将系统中的所有Razor视图进行预编译.编译好的视图DLL统一命名为 xxx.PrecompiledViews.dll 或者 xxx.Views.dll 动态编译 将项目整个配置成动态编译很简单,添加一个配置项目MvcRazorCompileOnPublish,值为false即可 <PropertyGroup> <MvcRazorCompileOnPublish>false</MvcRazorCompile

JavaScript的预编译和执行

JavaScript引擎,不是逐条解释执行javascript代码,而是按照代码块一段段解释执行.所谓代码块就是使用<script>标签分隔的代码段. 整个代码块共有两个阶段,预编译阶段和执行阶段 一.编译阶段 对于常见编译型语言(例如:Java)来说,编译步骤分为:词法分析->语法分析->语义检查->代码优化和字节生成. 对于解释型语言(例如JavaScript)来说,通过词法分析和语法分析得到语法树后,就可以开始解释执行了. (1)词法分析是将字符流(char strea

JavaScript预编译

今天用了大量时间复习了作用域,预编译等等知识 看了很多博文,翻开了以前看过的书 发现当初觉得自己学的很明白,其实还是存在一些思维误区 今晚就整理了一下凌乱的思路 先整理一下预编译的知识吧,日后有时间再把作用域详细讲解一下 大家要明白,这个预编译和编译是不一样的 JavaScript是解释型语言, 既然是解释型语言,就是编译一行,执行一行 传统的编译会经历很多步骤,分词.解析.代码生成什么的 日后有时间再给大家科普 下面就给大家分享一下我所理解的JS预编译 JavaScript运行三部曲 页面加载

个人对JavaScript预编译的理解

什么是js的预编译 马上要找工作了,之前学习JS都是很基础的皮毛,作为当前最火热的语言,本人一定是要研究的,然而刚接触到预编译我就快疯了,对于一个脑子不好使的人来说真的太绕了,饶了好久也不知道有没有绕明白,所以先记载下来,以后发现有啥不对的再纠正. 首先,JS解释器在执行一段script脚本时,首先会进行预编译,将代码中声明的变量和函数进行处理,然后才会按代码顺序翻译执行,那么JS在预编译和执行阶段分别进行了些什么操作呢?网上有很多大神的帖子给了说明: 1. 在执行代码前会进行类似"预编译&qu

javascript的预编译和执行顺序

最近在复习javascript的事件处理时发现了一个问题,然后也是我来写javascript的预编译和执行顺序的问题 代码: 代码一<html> <head> <title>事件处理</title> <meta http-equiv="content-type" content="text/html;charset=utf-8"/> <script type='text/javascript'>

JavaScript预编译原理分析

今天用了大量时间复习了作用域.预编译等等知识 看了非常多博文,翻了翻曾经看过的书(好像好多书都没有讲预编译) 发现当初认为自己学的非常明确,事实上还是存在一些思维误区 (非常多博文具有误导性) 今晚就整理了一下凌乱的思路 先整理一下预编译的知识吧,日后有时间再把作用域具体解说一下 大家要明确.这个预编译和传统的编译是不一样的(能够理解js预编译为特殊的编译过程) JavaScript是解释型语言, 既然是解释型语言,就是编译一行.运行一行 传统的编译会经历非常多步骤,分词.解析.代码生成什么的

JavaScript作用域原理(二)——预编译

JavaScript是一种脚本语言, 它的执行过程, 是一种翻译执行的过程.并且JavaScript是有预编译过程的,在执行每一段脚本代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式). 一.变量执行之前,会被赋为undefined <p id="scope2" style="color:red"></p> function echo(p, html) { p.innerHTML += html + '&l