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

    缘由 为什么要做AIROBOT?其实自从我知道智能家居这个领域之后,就一直想打造一个自己的智能家居控制平台,算是我的一个梦.最开始的项目还是在安居客当时工作的时候做的,项目地址:https://git ...

  2. LeetCode 705 Design HashSet 解题报告

    题目要求 Design a HashSet without using any built-in hash table libraries. To be specific, your design s ...

  3. mysql主从服务搭建

    一.安装mysql 检测当前centos是否安装了mysql:yum list installed | grep mysql yum list installed | grep mariadb    ...

  4. spring-boot mybatis配置

    接着我们的spring boot项目,spring boot如何使用mybatis访问数据库呢? 个人习惯使用mapper接口和xml配置sql,从pom.xml入手 1.1 添加依赖 <dep ...

  5. Tomcat配置技巧

    1. 配置系统管理(Admin Web Application) 大多数商业化的J2EE服务器都提供一个功能强大的管理界面,且大都采用易于理解的Web应用界面.Tomcat按照自己的方式,同样提供一个 ...

  6. 2018-2019-2 《网络对抗技术》Exp0 Kali安装 Week1 20165321

    安装kali 在vm里面新建虚拟机,选择典型 选择安装程序光盘镜像文件,系统出现无法检测此光盘镜像中的操作系统 虚拟机命名选择安装位置 给虚拟机分配的磁盘大小 点击自定义硬件,更改虚拟机硬件 选择Gr ...

  7. C++ 自定义订单号

    自定义订单号 #include<iostream> #include<stack> #include <time.h> #include <sys/timeb ...

  8. spring IOC 分析及实现

    什么是IOC Inversion of Control,控制反转,也成依赖倒置. 反转: 依赖对象的创建被反转,使用IOC之前,对象由自己创建,反转后,由IOC容器获取 IOC容器的工作: 负责创建, ...

  9. C语言实现迷宫小游戏

    代码如下,时间太晚,有空补注释: #include<stdio.h> #include<string.h> #include<time.h> #include< ...

  10. Linux 命令整理-ps

    ps 命令 ps -ef | grep tomcat ps -ef :以长格式(全格式)显示所有进程:“|” :是管道grep :检索tomcat :与字符tomcat有关的进程 ps[选项]-e:显 ...