JS的解析与执行过程

全局中的解析和执行过程

预处理:创建一个词法环境(LexicalEnvironment,在后面简写为LE),扫描JS中的用声明的方式声明的函数,用var定义的变量并将它们加到预处理阶段的词法环境中去。

一、全局环境中如何理解预处理

比如说下面的这段代码:

var a = ;//用var定义的变量,以赋值
var b;//用var定义的变量,未赋值
c = ;//未定义,直接赋值
function d(){//用声明的方式声明的函数
console.log('hello');
}
var e = function(){//函数表达式
console.log('world');
}

在预处理时它创建的词法作用域可以这样表示:

LE{        //此时的LE相当于window
a:undefined
b:undefined
没有c
d:对函数的一个引用
没有e
}

强调:1、预处理的函数必须是JS中用声明的方式声明的函数(不是函数表达式),看例子:

d();
e();
function d(){//用声明的方式声明的函数
console.log('hello');
}
var e = function(){//函数表达式
console.log('world');
}

输出结果分别是:hello;报错e is not a function
2、是用var定义的变量,看例子:

console.log(a);//undefined
console.log(b);//undefined
console.log(c);//报错:c is not defined
var a = ;
var b;
c = ;

二、命名冲突的处理

来看下面的代码:你觉得输出结果是什么?

console.log(f);
var f = ;
function f(){
console.log('foodoir');
}
console.log(f);
function f(){
console.log('foodoir');
}
var f = ;
console.log(f);
var f = ;
var f = ;
console.log(f);
function f(){
console.log('foodoir');
}
function f(){
console.log('hello world');
}

你可能跟我开始一样,觉得输出的是foodoir,这样你就错了,你应该继续看看前面讲的预处理的问题。第一段代码输出的结果应该是:function f(){console.log('foodoir');}。

看到第二段代码,你可能想都没想就回答结果是1,并且你还告诉原因说javascript里面的函数没有传统意义的重载。是的javascript里面的函数是没有重载,但是第二段代码的运行结果仍然是:function f(){console.log('foodoir');}。(原因后面作解释)

如果你还觉得第三段代码的结果是2或者是1,那么我建议你回到前面看看关于预处理的例子。第三段的结果为:undefined。

第四段代码的结果为function f(){console.log('hello world');}

原因:处理函数声明有冲突时,会覆盖;处理变量声明有冲突时,会忽略。在既有函数声明又有变量声明的时候,你可以跟我一样像CSS中的权重那样去理解,函数声明的权重总是高一些,所以最终结果往往是指向函数声明的引用。

三、全局函数的执行

来看下面的例子:

 console.log(a);
console.log(b);
console.log(c);
console.log(d);
var a = ;
b = ;
console.log(b);
function c(){
console.log('c');
} var d = function(){
console.log('d');
}
console.log(d);

1、我们先分析全局预处理的情况,结果如下:

LE{
a:undefined
没有b
c:对函数的一个引用
d:undefined
}

此时,我们可以得到前四行代码得到的结果分别为:
  undefined
  报错
  function c(){console.log('c');
  undefined

2、当执行完预处理后,代码开始一步步被解析(将第二行报错的代码注释掉)

在第6行代码执行完,LE中的a的值变为1;

LE{
a:
没有b
c:对函数的一个引用
d:undefined
}

第7行代码执行完,LE中就有了b的值(且b的值为2,此时b的值直接变为全局变量);

LE{
a:
b:
c:对函数的一个引用
d:undefined
}

第10行代码执行完,

LE{
a:
b:
c:指向函数
d:undefined
}

第14行代码执行完,此时

LE{
a:
b:
c:指向函数
d:指向函数
}

关于b变为全局变量的例子,我们在控制台中输入window.b,可以得到b的结果为2。结果如图:

补充:运用词法的作用域,我们可以很好的解释一个带多个参数的函数只传递一个参数的例子。

function f(a,b){

}
f();

它的词法作用域可以这样解释:

LE{
a:
b:undefined
}

函数中的解析和执行过程

函数中的解析和执行过程的区别不是很大,但是函数中有个arguments我们需要注意一下,我们来看下面的例子:

function f(a,b){
alert(a);
alert(b); var b = ;
function a(){}
}
f(,);

我们先来分析函数的预处理,它和全局的预处理类似,它的词法结构如下:

LE{
b:
a:指向函数的引用
arguments:
}
//arguments,调用函数时实际调用的参数个数

再结合之前的那句话:处理函数声明有冲突时,会覆盖;处理变量声明时有冲突,会忽略。
故结果分别为:function a(){}和2

当传入的参数值有一个时:

function f(a,b){
alert(a);
alert(b); var b = ;
function a(){}
}
f();

这个时候的词法结构如下:

LE{
b:undefined
a:对函数的一个引用
arguments:
}

故结果分别为:function a(){}和undefined
还有一个需要注意的地方有:如果没有用var声明的变量,会变成最外部LE的成员,即全局变量

function a(){
function b(){
g = ;
}
b();
}
a();
console.log(g);//

控制台结果:

有了前面的基础,我们就可以对JS的作用域和作用域链进行深入的了解了。

关于JS作用域和作用域链

console.log(a);//undefined
console.log(b);//undefined
console.log(c);//c is not defined
console.log(d);//d is not defined var a = ;
if(false){
var b = ;
}else{
c = ;
}
function f(){
var d = ;
}

有了前面的基础我们很容易就可以得到前三个的结果,但是对于第四个却很是有疑问,这个时候,你就有必要看一看关于javascript作用域的相关知识了。
  在编程语言中,作用域一般可以分为四类:块级作用域、函数作用域、动态作用域、词法作用域(也称静态作用域)

块级作用域

在其它C类语言中,用大括号括起来的部分被称为作用域,但是在javascript并没有块级作用域,来看下面一个例子:

for(var i=;i<;i++){
//
}
console.log(i);

它的结果为3,原因:执行完for循环后,此时的i的值为3,在后面仍有效

函数作用域

没有纯粹的函数的作用域

动态作用域

来看下面的例子:

function f(){
alert(x);
}
function f1(){
var x = ;
f();
}
function f2(){
var x = ;
f();
}
f1();
f2();

如果说存在动态作用域,那么结果应该是分别为1、2,但是最终结果并不是我们想要的,它的结果为:x is not defined。所以javascript也没有动态作用域

词法作用域(也称静态作用域)

我们可以在函数最前面声明一个x=100

var x=;
function f(){
alert(x);
}
function f1(){
var x = ;
f();
}
function f2(){
var x = ;
f();
}
f1();
f2();

结果为分别弹出两次100。说明javascript的作用域为静态作用域 ,分析:

function f(){
alert(x);
}
// f [[scope]] == LE == window
//创建一个作用域对象f [[scope]],它等于创建它时候的词法环境LE(据前面的知识我们又可以知道此时的词法环境等于window) function f1(){
var x = ;
f();//真正执行的时候(一步一步往上找)LE ->f.[[scope]] == window
}

在词法解析阶段,就已经确定了相关的作用域。作用域还会形成一个相关的链条,我们称之为作用域链。来看下面的例子:

function f(){    //f.scope == window
var x = ;//f.LE == {x:100,g:函数} var g = function(){//g.scope = f.LE
alert(x);
}
g();//在执行g的时候,先找g.scope,没有的话再找f.LE,还没有的话找f.scope……一直往上找window
}
f();

最终结果为:100
来看一个经典的例子:

//定义全局变量color,对于全局都适用,即在任何地方都可以使用全局变量color
var color = "red"; function changeColor(){
//在changeColor()函数内部定义局部变量anotherColor,只在函数changeColor()里面有效
var anotherColor = "blue"; function swapColor(){
//在swapColor()函数内部定义局部变量tempColor,只在函数swapColor()里面有效
var tempColor = anotherColor;
anotherColor = color;
color = tempColor; //这里可以访问color、anotherColor和tempColor
console.log(color); //blue
console.log(anotherColor); //red
console.log(tempColor); //blue
} swapColor();
//这里只能访问color,不能访问anotherColor、tempColor
console.log(color); //blue
console.log(anotherColor); //anotherColor is not defined
console.log(tempColor); //tempColor is not defined
} changeColor();
//这里只能访问color
console.log(color); //blue
console.log(anotherColor); //anotherColor is not defined
console.log(tempColor); //tempColor is not defined

还需要注意的是:new Function的情况又不一样

var x= ;
function f(){
var x = ;
//g.[[scope]] == window
var g = new Function("","alert(x)");
g();
}
f();
//结果为:123

小结:

以f1{ f2{ x}}为例,想得到x,首先会在函数里面的词法环境里面去找,还没找到去父级函数的词法环境里面去找……一直到window对象里面去找。
这时候,问题来了。。。。

问题1:到这里看来如果有多个函数都想要一个变量,每次都要写一个好麻烦啊,我们有什么方法可以偷懒没?

方法:将变量设置为全局变量

问题2:不是说要减少全局用量的使用么?因为在我们做大项目的时候难免要引入多个JS库,变量间的命名可能会有冲突,且出错后不易查找,这个时候我们该怎么办呢?

方法:将变量设置在一个打的function中,比如下面这样:

function(){
var a = ;
var b = ;
function f(){
alert(a);
}
}

问题3:照你的这种方法我们在外面又访问不到了,怎么办?

方法:我们使用匿名函数的方法,示例如下:

(function(){
var a = ,
b = ;
function f(){
alert(a);
}
window.f = f;
})();
f();
//结果为:1

JS的解析与执行过程的更多相关文章

  1. JS的解析与执行过程—全局预处理阶段之全局词法环境对象

    问题:有如下代码 var a = 1; function pop() { alert(a); var a = 5; } pop();//执行结果,弹出undefined 这段代码的执行结果为undef ...

  2. JS的解析与执行过程—函数预处理

    声明:之所以分为全局预处理与函数预处理,只是为了理解方便,其实在实际运行中二者是不分先后的. 函数预处理阶段与全局预处理的差别: 函数每调用一次,就会产生一个LexicalEnviroment对象,在 ...

  3. JS的解析与执行过程—全局预处理阶段之命名冲突的处理策略

    有如下代码: <body> <script> alert(f); function f() { console.log("fff"); } var f = ...

  4. js全局的解析与执行过程

    先看下面实例的执行结果: alert(a);//undefined alert(b);//报错 alert(f);//输出f函数字符串 alert(g);//undefined var a = 1; ...

  5. JS引擎线程的执行过程的三个阶段(二)

    继续JS引擎线程的执行过程的三个阶段(一) 内容, 如下: 三. 执行阶段 1. 网页的线程 永远只有JS引擎线程在执行JS脚本程序,其他三个线程只负责将满足触发条件的处理函数推进事件队列,等待JS引 ...

  6. JS引擎线程的执行过程的三个阶段(一)

    浏览器首先按顺序加载由<script>标签分割的js代码块,加载js代码块完毕后,立刻进入以下三个阶段,然后再按顺序查找下一个代码块,再继续执行以下三个阶段,无论是外部脚本文件(不异步加载 ...

  7. js函数的解析与执行过程

    function f(a,b,c){ alert(a);//函数字符串 alert(b); var b = 5; function a(){ } } f(1,2); //预处理 lexicalEnvi ...

  8. js的解析--预处理(三)

    js的解析与执行过程  分全局  {预处理阶段和执行阶段}  函数{预处理函数和执行阶段}   1/创建词法环境(环境上下文) LexicalEnvironment   === window { } ...

  9. JS-预解析(提升)与代码执行过程

    1,预解析的过程. 2,代码的执行过程. 预解析:程序在执行过程,会先将代码读取到内存中检查,会将所有的声明在此处进行标记,所谓的标记就是让js解析器知道这个名字,后面在使用这个名字的时候,不会出现未 ...

随机推荐

  1. 【四】搭建Markdown的编辑器

    本系列有五篇:分别是 [一]Ubuntu14.04+Jekyll+Github Pages搭建静态博客:主要是安装方面 [二]jekyll 的使用 :主要是jekyll的配置 [三]Markdown+ ...

  2. 一个微软的DDD架构图

  3. 我的Git手册

    本文肯定不是Git的最佳的教程,它只是本人的Git操作手册,我将从一些实际问题出发,让熟悉SVN用户顺利过度到Git来(当然包括我自己了),其中会加入一些个人感受或看法,相信会对大家有些启发.另外,全 ...

  4. 浅析MSIL中间语言——基础篇

    一.开篇 研究MSIL纯属于个人喜好,说在前面MSIL应用于开发的地方很少,但是很大程度上能够帮着我们理解底层的原理,这是我了解MSIL的主要原因.托管代码表示应用程序的方法的功能,它们以微软的中间语 ...

  5. 【VC++技术杂谈007】使用GDI+进行图片格式转换

    本文主要介绍如何使用GDI+对图片进行格式转换,可以转换的图片格式为bmp.jpg.png. 1.加载GDI+库 GDI+是GDI图形库的一个增强版本,提供了一系列Visual C++ API.为了使 ...

  6. Senparc.Weixin.MP SDK 微信公众平台开发教程(十八):Web代理功能

    在Senparc.Weixin.dll v4.5.7版本开始,我们提供了Web代理功能,以方便在受限制的局域网内的应用可以顺利调用接口. 有关的修改都在Senparc.Weixin/Utilities ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (44) ------ 第八章 POCO之POCO中使用值对象和对象变更通知

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 8-4  POCO中使用值对象(Complex Type--也叫复合类型)属性 问题 ...

  8. 敏捷是什么?PMO是什么?

    敏捷组织中PMO应遵循的准则 敏捷改变了人们的工作方式,不仅仅是开发部门,而且还包括其它的部门,例如HR.财务以及PMO等.在大多数组织中,PMO是一个控制体.它指导项目团队的规范.模板以及流程.目前 ...

  9. Spring学习记录(七)---表达式语言-SpEL

    SpEL---Spring Expression Language:是一个支持运行时查询和操作对象图表达式语言.使用#{...}作为定界符,为bean属性动态赋值提供了便利. ①对于普通的赋值,用Sp ...

  10. 【夔堂】:程序血泪史之——有一种垃圾语言叫做JavaScript

    "Prototype"机制是个半成品OOP,有些文章说这玩意当初为了"简单(编写).容易(学习)"而发明的,但web前端技术发展到今天我们看到,JS显然是需要O ...