上面一篇文章说了js的作用域链,这一节算是对上面的延申,有一个典型的例子,首先看原来的一段代码:

var name = "test";
function t() {
    var b = 1;
    if (b === 1) {
        var name = "test1";
    } else {
        var name = "test2";
    }
    console.log(name);
}
t();

这段代码的结果是test1

这个肯定是没有什么问题了,但是如果把代码作如下修改:

修改1:
var name = "test";
function t() {
    console.log(name);
    var name = "test1";
    console.log(name);
}
t();

修改2:
var name = "test";
function t() {
    console.log(name);
       /***********
    // var b = 1;
    // if (b === 1) {
    //     var name = "test1";
    // } else {
    //     var name = "test2";
    // }
    // console.log(name);
    ******************/
}
t();

这两个都是基于原来的代码修改,运行的结果分别是undefine,test1和test,噫!

这个是修改1后的输出:

这个是什么原因呢?原来在js运行前会有一个类似编译的过程------词法分析。首先看下记法分析的过程,词法分析主要分三个步骤:

1、先分析参数

2、再分析变量的声明

3、分析函数说明

具体的步骤:

0:函数的在运行的瞬间,生成一个活动对象(Active
Object)就是所谓的AO

1:分析参数

1-1:函数接收参数,添加到AO的属性上面,值全部都是undefine,如AO.age=undefine

1-2:接收实参,形成AO对应的属性值

2:分析变量声明,如var
age,

2-1:如果AO上还没有age属性,则添加AO
属性,值是undefine

2-2:如果AO
上面已经有了age属性,则不做任何操作。

3:分析函数的声明,如果funcion
foo(){},

3-1:
则把函数赋给AO.fooo,如果数据属性已经存在,则要被现在的新的值覆盖

下面开始分析上面修改1的执行的过程(自上崦下):

1、在window上面形成一个AO链,AO.name=undefine;

2、在函数的内部分析参数,函数没有参数,略过

3、在到变量声明阶段,在t函数的内部形成AO1,AO1.name=undfine;

4、分析函数声明,略过

执行过程:

1、首先是
在外层的AO.name=”test”

2、执行t函数,执行到console.log(name)时,函数内有name属性,就不会在到外面去寻找 执行输出undefine

3、执行赋值语句AO1.name=”test1”

4、输出当前的name值,”test1”;

这个问题解释清楚后,下面再分享几个代码:

function t(age) {
    console.log(age);
    var age = 99;
    console.log(age);
    function age() {

    }
    console.log(age);
}
t(5);  

这个问题看起来有点晕,但是事事都逃不过一个理,下面开始对这个进行词法分析

1-1、分析参数,有一个参数,形成一个AO.age=undefine;

1-2、接收参数AO.age=5;

2、分析变量声明,有一个var age,发现AO上面有一个AO.age,则不做任何处理

3、分析函数声明,有一个function age(){}声明, 则把原有的age覆盖成AO.age=function(){};

执行过程

1、执行第一个console.log(age)时,当前的AO.age是一个函数,所以输出的一个函数

2、这句var age=99;是对不AO.age的属性赋值,AO.age=99,所以在第二个输出的是99

3、同理第三个输出的是99,因为中间没有改变age值的语句了,

具体结果是,function age(){...},99,99,你算对了么?

如果把代码稍微再改下:

function t(age) {
    var age;
    console.log(age);
    age = "99";
    console.log(age);
    function age() {
    }
    console.log(age);
}
t(5);

你发现这个和上面运行的结果一致,虽然js代码是从上到下到下执行的,但从这个例子可以看出,在执行前还有一定规则的“编译”。

好了,现在词法分析说的差不多了,万变不离其宗,下面开始再说几个容易混淆的问题:

对上面的代码再修改

1、

function t(age) {
    var age;
    console.log(age);
    age = "99";
    console.log(age);
    function age() {
        console.log(age);
    }
    age();//这里不一样
    console.log(age);
}
t(5);

说明:这里的与前面的一样,但是这个唯一不同的是调用了age();呵呵,这个输出什么,这里的age根本不是一个函数了,所以肯定要报错了。不要被这个表现的现象忽悠了。

2、

function t(age) {
    var age;
     //age=99;这里被我注释掉了
    console.log(age);
    function age() {
        console.log(age);
    }
    age();
    console.log(age);
}
t(5);

现在想想答案会是什么?这里没有对age的赋值,说明age是一个函数,当这个函数执行的时候,age函数的内部没有age变量,根据作用域链到外层的函数去找,找到了一个age,但它还是一个函数,又再次输出函数,后面一个也是如此,所以结果是下面结果:

3、把函数声明函数变成用函数表达式

function t(age) {
    // var age;
    console.log(age);
    var age=function () {
        console.log(age);
    }
    age();
    console.log(age);
}
t(5);

这个结果应该是?先说下函数的表达式,如果把表达式拆开也就明白了,拆成如下的结果:var age;age=function(){},也就是函数表达是其实是一个变量声明和赋值的过程,而不是一个真正的函数声明!所以有了前面的基础,这个输出就很简单了,答案是5,function(){},function(){};

4、把匿名函数转成“函数声明的形式”

function t(age) {
    // var age;
    console.log(age);
    var age=function age () {
        console.log(age);
    }
    age();
    console.log(age);
}
t(5);

这个答案又是什么呢?是不是有点晕?这个结果和上面的结果是一个的,由这两个例子可以看出js的函数表达式只是一个赋值的过程。

好了说了这么多,今天的内容算是说完了, 希望能够让你从另一个全新的角度去考虑js的这门语言。后面会把js的原型链,js的继承链写出来,这三个链写完后,会把js中的this详细的总结下,js的部分有些“坑”说下,希望能够给你带来收获。

第十九篇 js高级知识---词法分析和AO 链的更多相关文章

  1. js高级知识---词法分析和AO 链

    转载自https://www.cnblogs.com/OceanHeaven/p/4957704.html 上面一篇文章说了js的作用域链,这一节算是对上面的延申,有一个典型的例子,首先看原来的一段代 ...

  2. 第十八篇 js高级知识---作用域链

    一直有想法去写写js方面的东西,我个人是最喜欢js这门语言,喜欢的他的自由和强大,虽然作为脚本语言有很多限制的地方,但也不失为一个好的语言,尤其是在H5出现之后.下面开始说说js的方面的东西,由于自己 ...

  3. 第20篇 js高级知识---深入原型链

    前面把js作用域和词法分析都说了下,今天把原型链说下,写这个文章费了点时间,因为这个东西有点抽象,想用语言表达出来不是很容易,我想写的文章不是简单的是官方的API的copy,而是对自己的知识探索和总结 ...

  4. 第九十九篇:JS闭包

    好家伙,总是要来的,去面对那些晦涩难懂的原理,它就在那里,等着我去搞定它 首先我要去补充一些最基本的概念, 1.什么是内存? 新华字典永远的神, 但这个解释显然不够   去看看百度百科: 内存: CP ...

  5. 第十九篇 .NET高级技术之C#中的线程(一)

    原文://http://www.cnblogs.com/miniwiki/archive/2010/06/18/1760540.html 文章系参考转载,英文原文网址请参考:http://www.al ...

  6. Egret入门学习日记 --- 第十九篇(书中 8.8~8.10 节 内容)

    第十九篇(书中 8.8~8.10 节 内容) 开始 8.8节. 重点: 1.类型推断. 2.类型强制转换,使其拥有代码提示功能. 3.除了TS自带的类型判断,Egret官方也提供了类型判断的方法. 操 ...

  7. JavaScript的高级知识---词法分析

    JavaScript的高级知识---词法分析 词法分析 词法分析方法: js运行前有一个类似编译的过程即词法分析,词法分析主要有三个步骤: 分析参数 再分析变量的声明 分析函数说明 函数在运行的瞬间, ...

  8. Python之路【第十九篇】:爬虫

    Python之路[第十九篇]:爬虫   网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用 ...

  9. Bootstrap入门(二十九)JS插件6:弹出框

    Bootstrap入门(二十九)JS插件6:弹出框 加入小覆盖的内容,像在iPad上,用于存放非主要信息 弹出框是依赖于工具提示插件的,那它也和工具提示是一样的,是需要初始化才能够使用的 首先我们引入 ...

随机推荐

  1. UVa 10400 - Game Show Math

    题目大意:给出n(n<100)个正整数和一个目标数,按照给出数的顺序,运用+.-.*./四则运算(不考虑优先级),判断能否得出所要的结果. 首先考虑的就是暴力枚举,不过时间复杂度为O(4n),会 ...

  2. 浅谈Java分页技术

    话不多言.我们要实现java分页技术,我们首先就需要定义四个变量,他们是: int  pageSize;//每页显示多少条记录 int pageNow;//希望现实第几页 int pageCount; ...

  3. .net 设置版本号信息

    1.AssemblyInfo.cs [assembly: AssemblyVersion("1.3.170116")] [assembly: AssemblyFileVersion ...

  4. 使用(Drawable)资源——StateListDrawable资源

    StateListDrawable用于组织多个Drawable对象.当使用StateListDrawable作为目标组件的背景.前景图片时,StateListDrawable对象所显示的Drawabl ...

  5. Delphi 常用函数记录

    //判断是否是数字 function IsNumeric(sDestStr: string): Boolean; //简写多余汉字 function SimplifyWord(sWord: strin ...

  6. Git 的是使用入门

    Coding 代码管理快速入门 当项目创建好了之后,我们该如何上传代码到 coding 上呢? Coding 网站使用“ Git 仓库”(类似 github )来管理代码. 其操作原理在于:利用 gi ...

  7. zoom:1-hasLayout

    在现代浏览器,如果子元素float,则父元素不会自动被撑开 #nofloatbox { border: 1px solid #FF0000; background: #CCC; width:200px ...

  8. HTML 颜色名

    目前所有浏览器都支持以下颜色名. 141个颜色名称是在HTML和CSS颜色规范定义的(17标准颜色,再加124).下表列出了所有颜色的值,包括十六进制值.  提示: 17标准颜色:黑色,蓝色,水,紫红 ...

  9. jQuery css,position,offset,scrollTop,scrollLeft用法

    jQuery css,position,offset,scrollTop,scrollLeft用法: <%@ page language="java" import=&quo ...

  10. Canvas get/putImageData

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...