今天在群里吹水时,有群友提出一个问题。我一看很简单,就立马给出了答案:因为存在变量提升,所以输出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
}()

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

  1. Mybatis参数预编译

    Mybatis参数预编译 一.数据库预编译介绍 1.数据库SQL语句编译特性: 数据库接受到sql语句之后,需要词法和语义解析,优化sql语句,制定执行计划.这需要花费一些时间.但是很多情况,我们的一 ...

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

    mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = ...

  3. mybatis之 # 与 $ 区别以及 sql 预编译

    mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = ...

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

    mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = ...

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

    预编译 预编译是ASP .Net Core的默认方式.在发布时,默认会将系统中的所有Razor视图进行预编译.编译好的视图DLL统一命名为 xxx.PrecompiledViews.dll 或者 xx ...

  6. 关于JavaScript预编译和执行顺序以及函数引用类型的思考

    昨晚在对项目中的一部分做模块化处理的时候,遇到了一个问题,一个重新定义的function对一个通用类中的function进行赋值覆盖的时候,失败了.问题抽象出来是这样的: <script > ...

  7. javascript的预编译和执行顺序

    原文:javascript的预编译和执行顺序 最近在复习javascript的事件处理时发现了一个问题,然后也是我来写javascript的预编译和执行顺序的问题 代码: 代码一<html> ...

  8. JavaScript的预编译和执行

    JavaScript引擎,不是逐条解释执行javascript代码,而是按照代码块一段段解释执行.所谓代码块就是使用<script>标签分隔的代码段. 整个代码块共有两个阶段,预编译阶段和 ...

  9. javascript引擎执行的过程的理解--语法分析和预编译阶段

    一.概述 js是一种非常灵活的语言,理解js引擎的执行过程对于我们学习js是非常有必要的.看了很多这方便文章,大多数是讲的是事件循环(event loop)或者变量提升的等,并没有全面分析其中的过程. ...

随机推荐

  1. NoSQL入门

    NoSQL(Not Only SQL)入门: *没有Fixed Schema *没有关系型数据储存在系统中 * 在大数据方面NoSQL有更好的表现 * 支持unstructured data - 不同 ...

  2. linux 对MTD分区nand flash的烧写和读取

    使用mtd-utils工具实现对flash的升级分区的烧写yaffs2 yaffs2的格式是根据所使用的nandflash来制作的,不同的nandflash,得到的yaffs2是不一样的,具体可以参考 ...

  3. spring 数据库多数据源路由

    项目中需要根据不同业务进行分库,首先是将业务不同业务映射到不同过的数据库( biz --> db,可能存在多对一情况), 查看springjdbc源码发现AbstractRoutingDataS ...

  4. python条件表达式:多项分支,双向分支

    # ### 多项分支 ''' if 条件表达式1: code1 code2 elif 条件表达式2: code3 code4 elif 条件表达式3: code5 code6 else: code7 ...

  5. Spark DateType cast 踩坑

    前言 在平时的 Spark 处理中常常会有把一个如 2012-12-12 这样的 date 类型转换成一个 long 的 Unix time 然后进行计算的需求.下面是一段示例代码: val sche ...

  6. 牛客(web 1)

    bootstrap(Web框架) 有关换行的知识: http://www.cnblogs.com/wqsbk/p/3493948.html 关于link加载问题: link是同时加载的,script标 ...

  7. 关于sql server profiler 监控工具的使用

    勾选以下属性: 记录这个数据库访问磁盘的次数:

  8. 拼多多(7pdd)微信跳转h5页面打开app跳转任意url关注技术weixin://dl/business/?ticket

    拼多多微信跳转接口利用了微信官方的weixin://dl/business/?ticket技术,此类接口可以在官方接口中找到,分析代码如下: <title>拼多多</title> ...

  9. cycle标签和random两种方式美化表格

    一:cycle标签实现给表格变色 1. <style>标签里写好需要的颜色 2. 在要变色的地方(行/列)加固定的语句,按照顺序依次执行 代码: <!DOCTYPE html> ...

  10. 集合 set

    集合 集合属于可变数据类型,但它的内容必须是不可变数据类型. 特点:无序   ,  不重复 有两种创建方式: set({1,2,3})和 {1,2,3} set1 = set({1,2,3,4}) s ...