javascript高级教程讲解.docx

上传人:b****8 文档编号:9320677 上传时间:2023-02-04 格式:DOCX 页数:113 大小:211.42KB
下载 相关 举报
javascript高级教程讲解.docx_第1页
第1页 / 共113页
javascript高级教程讲解.docx_第2页
第2页 / 共113页
javascript高级教程讲解.docx_第3页
第3页 / 共113页
javascript高级教程讲解.docx_第4页
第4页 / 共113页
javascript高级教程讲解.docx_第5页
第5页 / 共113页
点击查看更多>>
下载资源
资源描述

javascript高级教程讲解.docx

《javascript高级教程讲解.docx》由会员分享,可在线阅读,更多相关《javascript高级教程讲解.docx(113页珍藏版)》请在冰豆网上搜索。

javascript高级教程讲解.docx

javascript高级教程讲解

我们应该如何去了解JavaScript引擎的工作原理

1.什么是JavaScript解析引擎?

简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序。

比方说,当你写了 vara=1+1; 这样一段代码,JavaScript引擎做的事情就是看懂(解析)你这段代码,并且将a的值变为2。

学过编译原理的人都知道,对于静态语言来说(如Java、C++、C),处理上述这些事情的叫编译器(Compiler),相应地对于JavaScript这样的动态语言则叫解释器(Interpreter)。

这两者的区别用一句话来概括就是:

编译器是将源代码编译为另外一种代码(比如机器码,或者字节码),而解释器是直接解析并将代码运行结果输出。

比方说,firebug的console就是一个JavaScript的解释器。

但是,现在很难去界定说,JavaScript引擎它到底算是个解释器还是个编译器,因为,比如像V8(Chrome的JS引擎),它其实为了提高JS的运行性能,在运行之前会先将JS编译为本地的机器码(nativemachinecode),然后再去执行机器码(这样速度就快很多),相信大家对JIT(JustInTimeCompilation)一定不陌生吧。

我个人认为,不需要过分去强调JavaScript解析引擎到底是什么,了解它究竟做了什么事情我个人认为就可以了。

对于编译器或者解释器究竟是如何看懂代码的,翻出大学编译课的教材就可以了。

这里还要强调的就是,JavaScript引擎本身也是程序,代码编写而成。

比如V8就是用C/C++写的。

2.JavaScript解析引擎与ECMAScript是什么关系?

JavaScript引擎是一段程序,我们写的JavaScript代码也是程序,如何让程序去读懂程序呢?

这就需要定义规则。

比如,之前提到的vara=1+1;,它表示:

∙左边var代表了这是申明(declaration),它申明了a这个变量

∙右边的+表示要将1和1做加法

∙中间的等号表示了这是个赋值语句

∙最后的分号表示这句语句结束了

上述这些就是规则,有了它就等于有了衡量的标准,JavaScript引擎就可以根据这个标准去解析JavaScript代码了。

那么这里的ECMAScript就是定义了这些规则。

其中ECMAScript262这份文档,就是对JavaScript这门语言定义了一整套完整的标准。

其中包括:

∙var,if,else,break,continue等是JavaScript的关键词

∙abstract,int,long等是JavaScript保留词

∙怎么样算是数字、怎么样算是字符串等等

∙定义了操作符(+,-,>,<等)

∙定义了JavaScript的语法

∙定义了对表达式,语句等标准的处理算法,比如遇到==该如何处理

∙⋯⋯

标准的JavaScript引擎就会根据这套文档去实现,注意这里强调了标准,因为也有不按照标准来实现的,比如IE的JS引擎。

这也是为什么JavaScript会有兼容性的问题。

至于为什么IE的JS引擎不按照标准来实现,就要说到浏览器大战了,这里就不赘述了,自行Google之。

所以,简单的说,ECMAScript定义了语言的标准,JavaScript引擎根据它来实现,这就是两者的关系。

3.JavaScript解析引擎与浏览器又是什么关系?

简单地说,JavaScript引擎是浏览器的组成部分之一。

因为浏览器还要做很多别的事情,比如解析页面、渲染页面、Cookie管理、历史记录等等。

那么,既然是组成部分,因此一般情况下JavaScript引擎都是浏览器开发商自行开发的。

比如:

IE9的Chakra、Firefox的TraceMonkey、Chrome的V8等等。

从而也看出,不同浏览器都采用了不同的JavaScript引擎。

因此,我们只能说要深入了解哪个JavaScript引擎。

4.深入了解其内部原理的途径有哪些?

搞清楚了前面三个问题,那这个问题就好回答了。

个人认为,主要途径有如下几种(依次由浅入深):

∙看讲JavaScript引擎工作原理的书

这种方式最方便,不过我个人了解到的这样的书几乎没有,但是DmitryA.Soshnikov博客上的文章真的是非常的赞。

∙看ECMAScript的标准文档

这种方式相对直接,原汁原味,因为引擎就是根据标准来实现的。

目前来说,可以看第五版和第三版,不过要看懂也是不容易的。

∙看JS引擎源代码

这种方式最直接,当然也最难了。

因为还牵涉到了如何实现词法分析器,语法分析器等等更加底层的东西了,而且并非所有的引擎代码都是开源的。

5.以上几种方式中第一种都很难看明白怎么办?

其实第一种方式中的文章,作者已经将文档中内容提炼出来,用通俗易懂的方式阐述出来了。

如果,看起来还觉得吃力,那说明还缺少两块的东西:

∙对JavaScript本身还理解的不够深入

如果你刚刚接触JavaScript,或者说以前甚至都没有接触过。

那一下子就想要去理解内部工作原理,的确是很吃力的。

首先应该多看看书,多实践实践,从知识和实践的方式来了解JavaScript预言特性。

这种情况下,你只需要了解现象。

比方说,(function(){})() 这样可以直接调用该匿名函数、用闭包可以解决循环中的延迟操作的变量值获取问题等等。

要了解这些,都是需要多汲取和实践的。

实践这里就不多说了,而知识汲取方面可以多看看书和博客。

这个层面的书就相对比较多了,《ProfessionalJavaScriptforWebDevelopers》就是本很好的书(中文版请自行寻找)。

∙缺乏相应的领域知识

当JavaScript也达到一定深度了,但是,还是看不大明白,或者没法很深入到内部去一探究竟。

那就意味着缺少对应的领域知识。

这里明显的就是编译原理相关的知识。

不过,其实对这块了解个大概基本看起来就没问题了。

要再继续深入,那需要对编译原理了解的很深入,比如说词法分析采用什么算法,一般怎么处理。

会有什么问题,如何解决,AST生成算法一般有哪几种等等。

那要看编译原理方面的书,也有基本经典的书,比如《Compilers:

Principles,Techniques,andTools》这本也是传说中的龙书,还有非常著名的《SICP》和《PLAI》。

不过其实根据个人经验,对于Dmitry的文章,要看懂它,只要你对JavaScript有一定深度的了解,同时你大学计算机的课程都能大致掌握了(尤其是操作系统),也就是说基础不错,理解起来应该没问题。

因为这些文章基本没有涉及底层编译相关的,只是在解释文档的内容,并且其中很多东西都是相通的,比如:

context的切换与CPU的进程切换、函数相关的的局部变量的栈存储、函数退出的操作等等都是一致的。

以上就是个人对这个问题的看法,除此之外,我觉得,学习任何技术都不能操之过急,要把基础打扎实了,这样学什么都会很快。

编写可维护的代码的重要性

软件bug的修复是昂贵的,并且随着时间的推移,这些bug的成本也会增加,尤其当这些bug潜伏并慢慢出现在已经发布的软件中时。

当你发现bug的时候就立即修复它是最好的,此时你代码要解决的问题在你脑中还是很清晰的。

否则,你转移到其他任务,忘了那个特定的代码,一段时间后再去查看这些代码就需要:

∙花时间学习和理解这个问题

∙花时间是了解应该解决的问题代码

还有问题,特别对于大的项目或是公司,修复bug的这位伙计不是写代码的那个人(且发现bug和修复bug的不是同一个人)。

因此,必须降低理解代码花费的时间,无论是一段时间前你自己写的代码还是团队中的其他成员写的代码。

这关系到底线(营业收入)和开发人员的幸福,因为我们更应该去开发新的激动人心的事物而不是花几小时几天的时间去维护遗留代码。

另一个相关软件开发生命的事实是,读代码花费的时间要比写来得多。

有时候,当你专注并深入思考某个问题的时候,你可以坐下来,一个下午写大量的代码。

你的代码很能很快就工作了,但是,随着应用的成熟,还会有很多其他的事情发生,这就要求你的进行进行审查,修改,和调整。

例如:

∙bug是暴露的

∙新功能被添加到应用程序

∙程序在新的环境下工作(例如,市场上出现新的浏览器)

∙代码改变用途

∙代码得完全从头重新,或移植到另一个架构上或者甚至使用另一种语言

由于这些变化,很少人力数小时写的代码最终演变成花数周来阅读这些代码。

这就是为什么创建可维护的代码对应用程序的成功至关重要。

可维护的代码意味着:

∙可读的

∙一致的

∙可预测的

∙看上去就像是同一个人写的

∙已记录

谨慎使用全局变量

全局对象的概念

JavaScript通过函数管理作用域。

在函数内部声明的变量只在这个函数内部可用,而在函数外面不可用。

另一方面,全局变量就是在任何函数外面声明的或是未声明直接简单使用的。

每个JavaScript环境有一个全局对象,当你在任意的函数外面使用this的时候可以访问到。

你创建的每一个全局变量都成了这个全局对象的属性。

在浏览器中,方便起见,该全局对象有个附加属性叫做window,此window(通常)指向该全局对象本身。

下面的代码片段显示了如何在浏览器环境中创建和访问的全局变量:

myglobal= "nowamagic";         //不推荐写法

console.log(myglobal);          //"hello"

console.log(window.myglobal);       //"hello"

console.log(window["myglobal"]);    //"hello"

console.log(this.myglobal);         //"hello"

谨慎使用全局变量

全局变量的问题在于,你的JavaScript应用程序和web页面上的所有代码都共享了这些全局变量,他们住在同一个全局命名空间,所以当程序的两个不同部分定义同名但不同作用的全局变量的时候,命名冲突在所难免。

web页面包含不是该页面开发者所写的代码也是比较常见的,例如:

第三方的JavaScript库、广告方的脚本代码、第三方用户跟踪和分析脚本代码、不同类型的小组件/标志/按钮。

比方说,该第三方脚本定义了一个全局变量,叫做result;接着,在你的函数中也定义一个名为result的全局变量。

其结果就是后面的变量覆盖前面的,第三方脚本就一下子嗝屁啦!

因此,要想和其他脚本成为好邻居的话,尽可能少的使用全局变量是很重要的。

一些减少全局变量的策略,例如命名空间模式或是函数立即自动执行,但是要想让全局变量少最重要的还是始终使用var来声明变量。

由于JavaScript的两个特征,不自觉地创建出全局变量是出乎意料的容易。

首先,你可以甚至不需要声明就可以使用变量;第二,JavaScript有隐含的全局概念,意味着你不声明的任何变量都会成为一个全局对象属性。

参考下面的代码:

function sum(x,y){

//不推荐写法:

隐式全局变量

result=x+y;

return result;

}

此段代码中的result没有声明。

代码照样运作正常,但在调用函数后你最后的结果就多一个全局命名空间,这可以是一个问题的根源。

经验法则是始终使用var声明变量,正如改进版的sum()函数所演示的:

function sum(x,y){

var result=x+y;

return result;

}

另一个创建隐式全局变量的反例就是使用任务链进行部分var声明。

下面的片段中,a是本地变量但是b确实全局变量,这可能不是你希望发生的:

//反例,勿使用

functionfoo(){

vara=b=0;

//...

}

此现象发生的原因在于这个从右到左的赋值,首先,是赋值表达式b=0,此情况下b是未声明的。

这个表达式的返回值是0,然后这个0就分配给了通过var定义的这个局部变量a。

换句话说,就好比你输入了:

var a=(b=0);

如果你已经准备好声明变量,使用链分配是比较好的做法,不会产生任何意料之外的全局变量,如:

functionfoo(){

vara,b;

//...a=b=0;//两个均局部变量

}

然而,另外一个避免全局变量的原因是可移植性。

如果你想你的代码在不同的环境下(主机下)运行,使用全局变量如履薄冰,因为你会无意中覆盖你最初环境下不存在的主机对象(所以你原以为名称可以放心大胆地使用,实际上对于有些情况并不适用)。

var预解析与副作用

var的副作用

隐式全局变量和明确定义的全局变量间有些小的差异,就是通过delete操作符让变量未定义的能力。

∙通过var创建的全局变量(任何函数之外的程序中创建)是不能被删除的。

∙无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的。

这表明,在技术上,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性。

属性是可以通过delete操作符删除的,而变量是不能的:

//定义三个全局变量

varglobal_var=1;

global_novar=2;//反面教材

(function(){

global_fromfunc=3;//反面教材

}());

//试图删除

deleteglobal_var;//false

deleteglobal_novar;//true

deleteglobal_fromfunc;//true

//测试该删除

typeofglobal_var;//"number"

typeofglobal_novar;//"undefined"

typeofglobal_fromfunc;//"undefined"

在ES5严格模式下,未声明的变量(如在前面的代码片段中的两个反面教材)工作时会抛出一个错误。

单var形式

在函数顶部使用单var语句是比较有用的一种形式,其好处在于:

∙提供了一个单一的地方去寻找功能所需要的所有局部变量

∙防止变量在定义之前使用的逻辑错误

∙少代码(类型啊传值啊单线完成)

单var形式长得就像下面这个样子:

functionfunc(){

vara=1,

b=2,

sum=a+b,

myobject={},

i,

j;

//functionbody...

}

您可以使用一个var语句声明多个变量,并以逗号分隔。

像这种初始化变量同时初始化值的做法是很好的。

这样子可以防止逻辑错误(所有未初始化但声明的变量的初始值是undefined)和增加代码的可读性。

在你看到代码后,你可以根据初始化的值知道这些变量大致的用途,例如是要当作对象呢还是当作整数来使。

你也可以在声明的时候做一些实际的工作,例如前面代码中的sum=a+b这个情况,另外一个例子就是当你使用DOM(文档对象模型)引用时,你可以使用单一的var把DOM引用一起指定为局部变量,就如下面代码所示的:

functionupdateElement(){

varel=document.getElementById("result"),

style=el.style;

//使用el和style干点其他什么事...

}

var变量预解析

JavaScript中,你可以在函数的任何位置声明多个var语句,并且它们就好像是在函数顶部声明一样发挥作用,这种行为称为hoisting(悬置/置顶解析/预解析)。

当你使用了一个变量,然后不久在函数中又重新声明的话,就可能产生逻辑错误。

对于JavaScript,只要你的变量是在同一个作用域中(同一函数),它都被当做是声明的,即使是它在var声明前使用的时候。

看下面这个例子:

//反例

myname="global";//全局变量

functionfunc(){

alert(myname);//"undefined"

varmyname="local";

alert(myname);//"local"

}

func();

在这个例子中,你可能会以为第一个alert弹出的是”global”,第二个弹出”loacl”。

这种期许是可以理解的,因为在第一个alert的时候,myname未声明,此时函数肯定很自然而然地看全局变量myname,但是,实际上并不是这么工作的。

第一个alert会弹出”undefined”是因为myname被当做了函数的局部变量(尽管是之后声明的),所有的变量声明当被悬置到函数的顶部了。

因此,为了避免这种混乱,最好是预先声明你想使用的全部变量。

上面的代码片段执行的行为可能就像下面这样:

myname="global";//globalvariable

functionfunc(){

varmyname;//等同于->varmyname=undefined;

alert(myname);//"undefined"

myname="local";

alert(myname);//"local"

}

func();

为了完整,我们再提一提执行层面的稍微复杂点的东西。

代码处理分两个阶段,第一阶段是变量、函数声明、以及正常格式的参数创建,这是一个解析和进入上下文的阶段。

第二个阶段是代码执行、函数表达式和不合格的标识符(为声明的变量)被创建。

但是,出于实用的目的,我们就采用了”hoisting”这个概念,这种ECMAScript标准中并未定义,通常用来描述行为。

访问全局对象

在浏览器中,全局对象可以通过window属性在代码的任何位置访问(除非你做了些比较出格的事情,像是声明了一个名为window的局部变量)。

但是在其他环境下,这个方便的属性可能被叫做其他什么东西(甚至在程序中不可用)。

如果你需要在没有硬编码的window标识符下访问全局对象,你可以在任何层级的函数作用域中做如下操作:

varglobal=(function(){

returnthis;

}());

这种方法可以随时获得全局对象,因为其在函数中被当做函数调用了(不是通过new构造),this总是指向全局对象。

实际上这个病不适用于ECMAScript5严格模式,所以,在严格模式下时,你必须采取不同的形式。

例如,你正在开发一个JavaScript库,你可以将你的代码包裹在一个即时函数中,然后从全局作用域中,传递一个引用指向this作为你即时函数的参数。

JavaScript探秘:

for循环

在for循环中,你可以循环取得数组或是数组类似对象的值,譬如arguments和HTMLCollection对象。

通常的循环形式如下:

//次佳的循环

for(vari=0;i

//使用myarray[i]做点什么

}

这种形式的循环的不足在于每次循环的时候数组的长度都要去获取下。

这回降低你的代码,尤其当myarray不是数组,而是一个HTMLCollection对象的时候。

HTMLCollections指的是DOM方法返回的对象,例如:

document.getElementsByName()

document.getElementsByClassName()

document.getElementsByTagName()

还有其他一些HTMLCollections,这些是在DOM标准之前引进并且现在还在使用的。

有:

document.images:

页面上所有的图片元素

document.links:

所有a标签元素

document.forms:

所有表单

document.forms[0].elements:

页面上第一个表单中的所有域

集合的麻烦在于它们实时查询基本文档(HTML页面)。

这意味着每次你访问任何集合的长度,你要实时查询DOM,而DOM操作一般都是比较昂贵的。

这就是为什么当你循环获取值时,缓存数组(或集合)的长度是比较好的形式,正如下面代码显示的:

for(vari=0,max=myarray.length;i

//使用myarray[i]做点什么

}

这样,在这个循环过程中,你只检索了一次长度值。

注意到,当你明确想要修改循环中的集合的时候(例如,添加更多的DOM元素),你可能更喜欢长度更新而不是常量。

伴随着单var形式,你可以把变量从循环中提出来,就像下面这样:

functionlooper(){

vari=0,

max,

myarray=[];

//...

for(i=0,max=myarray.length;i

//使用myarray[i]做点什么

}

}

这种形式具有一致性的好处,因为你坚持了单一var形式。

不足在于当重构代码的时候,复制和粘贴整个循环有点困难。

例如,你从一个函数复制了一个循环到另一个函数,你不得不去确定你能够把i和max引入新的函数(如果在这里没有用的话,很有可能你要从原函数中把它们删掉)。

最后一个需要对循环进行调整的是使用下面表达式之一来替换i++。

i=i+1

i+=1

JSLint提示您这样做,原因是++和–-促进了“过分棘手(excessivetrickiness)”。

如果你直接无视它,JSLint的plusplus选项会是false(默认是default)。

还有两种变化的形式,其又有了些微改进,因为:

∙少了一个变量(无max)

∙向下数到0,通常更快,因为和0做比较要比和数组长度或是其他不是0的东西作比较更有效率

//第一种变化的形式:

vari,myarray=[];

for(i=myarray.length;i–-;){

//使用myarray[i]做点什么

}

//第二种使用while循环:

varmyarray=[],

i=myarray.length;

while(i–-){

//使用myarray[i]做点什么

}

这些小的改进只体现在性能上,此外JSLint会对使用i–-加以抱怨。

for-in循环(for-in_Loops)

for-in循环应该用在非数组对象的遍历上,使用for-in进行循环也被称为“枚举”。

从技术上将,你可以使用for-in循环数组(因为JavaScript中数组也是对象),但这是不推荐的。

因为如果数组对象已被自定义的功能增强,就可能发生逻辑错误。

另外,在for-in中,属性列表的顺序(序列)是不能保证的。

所以最好数组使用正常的for循环,对象使用for-in循环。

有个很重要的hasOwnProperty()方法,当遍历对象属性的时候可以过滤掉原型链上的属性。

思考下面一段代码:

//对象

varman={

hands:

2,

legs:

2,

heads:

1

};

//在代码的某个地方,一个方法添加给了所

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1