我们都知道javascript是解释型语言,执行的特点呢是编译一行,执行一行。按照这个思路有时候我们在运行代码时会有一些令人费解的现象出现。下面我们一起来执行下面三段代码。

<script>
var a = 123;
console.log(a);
</script>

  

<script>
console.log(a);
</script>

  

<script>
console.log(a);
var a = 123;
</script>

运行上面三段代码可以得出结果分别为:  123. 和 a is not defined. 和 undefined.

按理说第三个代码应该也输出:a is not defined.但是并没有,这正是由于javascript在执行代码前的预编译产生的。

前面说的是变量的例子,下面我们看看函数执行时的效果。请看下面两段代码。

<script>
function test() {
document.write("a");
}
test();
</script>

  

<script>
test();
function test() {
document.write("a");
}
</script>

  

运行上面两段代码执行结果都为:a。 这也是预编译所产生的结果。

这又是为什么呢。下面我们带着疑问来了解一下预编译到底是什么。

全局变量、暗示全局变量和局部变量的概念

在这之前我们先来了解一下什么是:暗示全局变量。我们都知道变量有两种类型:全局变量和局部变量。在函数外部定义的就叫全局变量,在函数内部定义的就叫局部变量。

先看一段代码

<script>
var a = 1;
b = 2;           
function test() {
var c = d = 3; //变量的赋值是从右到左的,相当于:d = 3; var c = d;
console.log(c);
console.log(d);
}
test();
console.log(b);
console.log(d);
</script>

  

执行结果为:3  3  2  3.  

奇怪,d 不是局部变量吗,为什么可以在全局进行访问?  为什么 b = 2 这样也可以声明变量 ?

那么就只有一个可能,d 不是局部变量,而是暗示全局变量。

接下来我们熟悉一下两个重要的概念。

  1. imply global暗示全局变量:即任何变量,如果变量未声明就赋值,此变量为全局对象(window)所有。window就是全局的域。
  2. 任何声明的全局变量,皆为window的属性。所以访问一个全局变量可以console.log(a),也可以console.log(window.a).

所以出现上面的结果也就不难解释了,a 是全局变量。b d二者是暗示全局变量(即未声明就赋值的变量)。c 是局部变量。所以变量d可以在外部进行访问。以后我们只要记得暗示全局变量也是全局变量即可。

JS运行三部曲

下面,我们正式进入JS运行三部曲部分。

1、语法分析

2、预编译

3、解释执行

在执行代码前还有两个步骤,即语法分析和预编译。

在预编译之前,系统会对整个代码全部扫描一遍,看看有没有低级的语法错误,如少了括号或引号等等。解释执行就是从上到下依次执行函数代码。

下面我们重点来讲一下预编译。讲完预编译后相信大家就会知道之前的代码执行结果为什么是那样的啦。

我们分函数体内的预编译和全局预编译两个部分来讲解预编译。

函数体内的预编译

函数体内预编译四部曲    //发生在函数执行的前一刻。

  1. 创建AO对象。Activation object。即作用域,或叫执行期上下文。
  2. 找形参和变量声明,将变量和形参名作为AO的属性名,初始值为undefined.
  3. 将形参和实参相统一。
  4. 在函数体里找函数声明,值(函数声明)赋予AO对象。

注意:预编译过程只涉及变量或函数的声明,赋值语句在解释执行的阶段才进行。

我们来看一下这段代码具体解释一下函数体内预编译的详细过程,也可以自己先执行一遍,看一下结果与自己预期的是否一致。

<script>
function fn(a){        //定义一个函数fn()
console.log(a);
var a = 123;
console.log(a);
function a() {}    //函数的声明在预编译时已经被识别,所以在调用fn(1)时,忽略这条语句
console.log(a);
var b =function () {}  
console.log(b);
function d() {}
}
fn(1);
</script>

  1、创建一个AO对象。  AO {  }。

  2、找形参和变量声明,将变量和形参名作为AO的属性名,初始值为undefined。变量名和形参名一致的话只写一个就行。

    AO { a : undefined

        b : undefined

         }

  3、将实参和形参统一。

    AO {a : 1

      b : undefined

      }

  4、在函数体里找函数声明,值(函数声明)赋予AO对象。

    AO {a : function a() {}

      b : undefined

      d : function d() {}

      }

预编译结束后,才执行fn(1)函数。(执行过程为从上到下依次执行)

执行结果为:function a() {} 、123、123、function () {}.

全局预编译

全局预编译(整个script代码块执行前)

  1、创建一个GO对象。GO对象即window对象。

  2、找变量声明。初始值为undefined。注:全局预编译没有形参和实参

  3、找‘全局里的函数声明,函数声明赋值到全局里的GO。

我们来看一下下面这段代码来了解一下全局预编译。

<script>

        function b(a) {
console.log(a);
}
b(1);
console.log(a);
var a = 2;
console.log(a);
</script>

  1、创建一个GO对象。 GO { }

  2、找变量声明  GO {a : undefined}

  3、找函数声明  GO {a : undefined

             b : function b(a){...} }

从上到下执行整个代码。

执行到 b (1)时,先进行函数体的预编译,然后再执行 b (1)函数。

  1、创建一个AO对象   AO { }

  2、找形参和变量声明  AO {a : undefined}

  3、将实参和形参统一  AO {a : 1}

  4、在函数体里找函数声明,值(函数声明)赋予AO对象。没有函数体,则这一步忽略。

预编译完成后执行 b(1)函数。

最后显示的结果为:1  undefined  2.

总结:JS运行过程

全局预编译(脚本代码块script执行前)

  1. 查找全局变量声明(包括隐式全局变量声明,省略var声明),变量名作全局对象的属性,值为undefined  查找函数声明,函数名作为全局对象的属性,值为函数引用
  2. 查找函数声明,函数名作为全局对象的属性,值为函数引用。

从上往下依次执行代码,遇到函数体时,进行函数体预编译。

函数体预编译(函数执行前)

  1. 创建AO对象(Active Object)
  2. 查找函数形参及函数内变量声明,形参名及变量名作为AO对象的属性,值为undefined
  3. 实参形参相统一,实参值赋给形参
  4. 查找函数声明,函数名作为AO对象的属性,值为函数引用

函数体预编译后,继续往下执行代码。

JS预编译详解的更多相关文章

  1. JavaScript预编译详解

    一.js运行三部曲: 1.语法分析(通篇扫描看有没有语法错误) 2.预编译 3.解释执行 二.预编译前奏 1.imply global 暗示全局变量:任何变量如果未经声明就赋值,此变量为全局对象所有 ...

  2. C中预编译详解

    预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器.可见预处理过程先于编译器对源代码进行处理.在C 语言中,并没有任何内在的机制来完成如下一些功能:在编译时包含其他源文件.定义宏.根 ...

  3. 《Node.js开发实战详解》学习笔记

    <Node.js开发实战详解>学习笔记 ——持续更新中 一.NodeJS设计模式 1 . 单例模式 顾名思义,单例就是保证一个类只有一个实例,实现的方法是,先判断实例是否存在,如果存在则直 ...

  4. Js apply 方法 详解

    Js apply方法详解 我在一开始看到JavaScript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

  5. ES6,ES2105核心功能一览,js新特性详解

    ES6,ES2105核心功能一览,js新特性详解 过去几年 JavaScript 发生了很大的变化.ES6(ECMAScript 6.ES2105)是 JavaScript 语言的新标准,2015 年 ...

  6. js预编译

    先来做三个测试 eg1: var a; a = 1; function a() {}; console.log(a); eg2: var a; function a() {}; console.log ...

  7. 2010_3_1最新 完整 FFMPEG 编译详解

    在网上看了很多编译详解,都很零散.经过自己的编译,解决一些BUG,在此分享自己的一些经验... 话不多说了!直接上贴. 第一步:准备编译平台. 需要 一个 MinGW 和 一个 MSYS 安装包 以及 ...

  8. Handlebars.js 预编译(转)

    Handlebars.js 官网上对预编译1是这样说的: 你需要安装 Node.js 你需要在全局环境中,通过 Npm 安装 handlebars 包 然后你就可以通过命令预编译你的 handleba ...

  9. Js apply()使用详解

    Js apply方法详解 我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

随机推荐

  1. vs 2017局域网内调试

    之前调试代码都是在本地启动服务,以  localhost:端口号   的形式调试,今天发现也是可以用ip地址的形式来调用接口,这种方式可以支持内网内Client端调用接口,实现调试的功能,具体方法如下 ...

  2. .net core in Docker 部署方案(随笔)

    前一段时间由于项目需要 .net core 在docker下的部署,途中也遇到很多坑,看了各同行的博客觉得多多少少还是有些问题,原本不想写此篇文章,由于好友最近公司也需要部署,硬是要求,于是花了些时间 ...

  3. SKU:唯一标识填什么

    策略 随意填写 只要别和别人重复就好 ,不过重复你也创建不了. 最好填与APP信息相关的,比如直接填写bundle ID 上去...跟套装ID保持一致. 你新建应用的时候都还没有APP ID 你怎么填 ...

  4. Guideline 2.1 - Information Needed需要补充录制视频

    1.被拒回文 Guideline 2.1 - Information Needed We have started the review of your app, but we are not abl ...

  5. 互斥锁与join

    三 互斥锁与join 使用join可以将并发变成串行,互斥锁的原理也是将并发变成穿行,那我们直接使用join就可以了啊,为何还要互斥锁,说到这里我赶紧试了一下 #把文件db.txt的内容重置为:{&q ...

  6. Codeforces Round #545 (Div. 2)D(KMP,最长公共前后缀,贪心)

    #include<bits/stdc++.h>using namespace std;const int N=1000007;char s1[N],s2[N];int len1,len2; ...

  7. git配置本地环境(phpstudy/tortoisegit/git等)

    1.下载安装phpstudy 2.下载安装git 下载地址:https://git-scm.com/downloads 3.下载安装tortoisegit,电脑64位就下载这个,如图: 4.下载安装“ ...

  8. php代码审计2全局变量和超全局变量

    全局变量:就是在函数外面定义的变量,不能在函数中直接使用,因为它的作用域不会到函数内部,所以在函数内部使用的时候尝尝看到类似global $a; 超全局变量:在所有脚本都有效,所以,在函数可以直接使用 ...

  9. oracle调试存储过程不进去

    右击存储过程,选中Add debug information即可

  10. HTML5新增的表单元素有哪些?

    表单控:color ,  calendar  ,  date ,  datetime, datetime-local,  time, mouth , week, email, url , search ...