为何已经有个高质量通过了,但是看电影任务栏跳出来中的高质量没有跳出来完成??

您所在的位置:
& 资料区 & 正文
陈至立在考察北京高校奥运场馆建设时强调加强领导 精心组织确保按时高质量地完成高校奥运场馆建设任务
日17:53  
  新华网北京7月5日电国务委员、北京奥组委第一副主席陈至立6月30日和7月4日考察了北京高校奥运场馆建设工程,并召开会议听取有关高校的汇报。她强调,各有关部门和高校要进一步认真学习贯彻胡锦涛总书记在考察工程建设时的重要讲话精神,加强领导,精心组织,严格管理,确保按时高质量地完成高校奥运场馆建设任务。
  陈至立指出,将部分奥运场馆建在高校是中央的重要决策。建设好高校奥运场馆,是举办“有特色、高水平”奥运会的重要保障,对于推动高校体育运动的蓬勃发展和奥林匹克精神在高校的普及具有重要意义,对于提高我国竞技体育水平将发挥重要作用。
  陈至立强调,今年是2008年北京奥运会筹办工作的决战之年,高校奥运场馆建设也到了决战阶段。各有关高校要以高度的责任感继续扎扎实实做好奥运场馆建设工作。一要加强组织领导,强化质量管理,确保场馆按时高质量地交付使用;二要高度重视奥运场馆建设的安全问题,加强监督检查和应急管理,确保安全;三要高度重视场馆运行团队建设和培训,充分利用好运北京体育赛事锻炼队伍,磨合机制,把握规律,积累经验,确保达到奥运比赛要求;四要厉行节约,大力实践“绿色奥运、科技奥运、人文奥运”理念,提高场馆建设的节能、环保水平,同时要做好校园内部和周边环境的整治工作,以崭新的面貌迎接奥运;五要广泛动员高校师生积极关注奥运、支持奥运、参与奥运,在校园中营造“我参与、我奉献、我快乐”的浓厚氛围,促进高校精神文明建设。她要求有关部门加大对高校奥运场馆建设的支持力度,及时解决工程建设中遇到的实际问题。
有关部委、北京市和北京奥组委负责人,以及相关高校领导参加了考察和工作汇报会。(新华网
更多关于“”的新闻
( 16:34:07)
( 16:13:40)
( 11:14:36)
( 10:29:07)
( 21:39:30)
( 21:37:28)
( 21:21:53)
( 20:59:30)
( 20:41:44)
( 20:20:42)
同时更新资讯所属地
暂无更新,休息一会儿
白羊座(03.21-04.19)
金牛座(04.20-05.20)
双子座(05.21-06.21)
巨蟹座(06.22-07.22)
狮子座(07.23-08.22)
处女座(08.23-09.22)
天秤座(09.23-10.23)
天蝎座(10.24-11.22)
射手座(11.23-12.21)
摩羯座(12.22-01.19)
水瓶座(01.20-02.18)
双鱼座(02.19-03.20)
今日运势:
本日可多参与公众事务,将自己的意见与兴趣结合,提供同好们做参考,让欢乐的气氛添加一些趣味性...
24小时热帖
Copyright & 1998 - 2017 Tencent. All Rights ReservedHTTP Error 404. The requested resource is not found.登录以解锁更多InfoQ新功能
获取更新并接收通知
给您喜爱的内容点赞
关注您喜爱的编辑与同行
966,690 九月 独立访问用户
语言 & 开发
架构 & 设计
文化 & 方法
您目前处于:
高质量代码——书评与采访
高质量代码——书评与采访
Srini Penchikala
13&他的粉丝
0&他的粉丝
日. 估计阅读时间:
:Facebook、Snapchat、Tumblr等背后的核心技术
相关厂商内容
相关赞助商
InfoQ:首先能否请您为读者们对&高质量代码&作一个定义呢?因为对不同的读者来说,它可能意味着不同的东西。那么究竟什么是高质量的代码呢?
Vance:如果要用一句话来概括的话,我认为高质量代码具有以下特性,它们按重要性的优先顺序排列:高质量代码能够完成既定的任务、没有bug,并且实现良好。它不仅能够应对今天的需求,并且在明天甚至是明年依然能够胜任;它能够满足业务和用户的需求;它应该是没有bug的,并且应当尽量与当前这个不完美的世界保持独立,而如果它不得不和某个不完美的外部世界相交互,也能够优雅地进行处理;它应该具有良好的设计与实现,即使在多年之后也能够轻松地修订、改动或增强其行为。传统上每过若干年之后,某些软件中的老代码的修改成本就会超过重写它的成本了,而高质量的代码则有希望打破这个怪圈。
我假设你已经了解你的代码需要完成的功能是什么,我将试图为你解决的问题是怎样将这个需求如实地转变为代码。尤其重要的是,我将确保开发者了解可测试的代码是怎样的,并让他们了解他们可以从测试代码的实现模式中熟练掌握这一技巧。
InfoQ:您能够深入谈论一下&代码的意图&吗?如果某个IDE工具能够提供一种管理代码意图的特性,它又应该是怎样工作的?
Vance:你所指的是我在本书第2章介绍部分所引用的某个虚构的&意图检查器&(Intention Checker),对吧?
很多时候,我们在完成了代码编写之后,就简单地判断它是否按照我们想要的方式工作。但其实我们并非完全有意图地编写我们的代码。或者这段代码虽然并不算实现的非常差,但它确实存在bug,而且很可能包含了许多不必要的元素。而有意图的编写代码是指你完全理解编写每一条语句的原因,并且保证每一条语句都是为了满足某个目的而编写的。
在整本书中,我对于这一点始终非常关注,因为一个测试就是一个意图的语句表达。对这一点的认知将有助于提高你的测试功力,并且编写出满足需求的代码。
正因如此,我本人是测试驱动开发的忠实粉丝。因为这门技术不是针对测试本身,而是通过编写一个能够表达你意图的测试来驱动你的设计。
说到工具方面,如果我真有答案的话,那我想整个软件工程业界应该也都知道这个答案了吧。将意图转换为代码是编程过程的真正核心。业务曾试用过各种手段来实现这一点,包括需求、可追踪性、图形化编程、软件工厂、形式逻辑、生成框架以及其它种种方式,但始终没有很好地做到表现语义。我们在慢慢地开发越来越高级别的构件,它使我们越来越远离本质的部分。但为了编写软件所必须进行的分解的级别依然远远高于多数人所能够理解的粒度,这一过程依然是高度专业性,而且极易出错的。
InfoQ:对于应用程序中的错误处理代码,您有些什么心得体会吗?
Vance:你的软件除了正确地完成了基本的功能之外,它是否进行了合适的错误处理可以说是对软件品质的最大的决定因素了。我听到过一种说法,当人们考虑是否购买或使用某样东西的时候,1 条差评的分量往往能够顶得上4到10条好评。
那么为什么错误处理这一步骤往往会放到最后才去考虑,为什么在设计时没有注意它,并且对它的测试覆盖率也是最低的呢?为什么在许多用户可见的错误中,会显示一些隐晦的或无用的信息呢?其中一部分原因是在于没有有意识地对错误用例进行处理或者测试。
从代码角度来说,像Java这样的语言即使能够声明异常,它也不会强制你必须声明所有的异常。开发者越来越倾向于仅仅使用Java中的unchecked异常类型,它允许你忽略各种显式的意图。更不用说还有许多语言不支持对错误的声明。并且许多类库和框架也没有很好地将各种错误文档化,这意味着即使你打算在自己的软件中很好地处理它,最终结果也取决于你能在多大程度上对这些工具进行反向工程。
InfoQ:您在书中谈论了多种不同的测试方面,例如状态测试与行为测试。您是否能够深入地讲述一下这些内容,并谈谈开发者应该如何利用这些不同的测试技术吗?
Vance:状态测试关注的是你的软件行为会造成数据产生怎样的变化。这方面的经典例子包括:完全由输入参数的值所决定的返回值;以及在执行对象上的某个方法后对对象属性值的改变。
行为测试则关注在软件执行时调用了哪些方法,例如调用了其它对象的方法,或是调用了某个服务。举例来说,如果某个方法的唯一目的就是以正确的顺序及正确的参数调用其它方法,那么你就需要验证这些方法调用是否按照期望进行执行了。
某些方法或许能够完全归类于状态测试或行为测试中的一种,但多数方法与两者都有相关。对于这些方法来说,如果仅仅按照一种方法测试,不仅有局限性,甚至可能会有所损害。你不一定能够访问所有状态,而且仅仅关注状态或者会使你遗漏某些关键的行为。而过于关注行为的风险是你的测试有可能会与实现细节相耦合,并且为了支持这种风格的验证,你或许会对软件进行过度设计。
如果想在两方面都获得最好的效果,那么你需要有意图地指引你的测试,并且让你使用的测试技巧能够最好地表达出你的意图,而且让你的测试与具体实现的耦合降至最低。这两者之间的差别应该能够进一步强化你的设计意图,而不是非此即彼的相互排斥。
InfoQ:在创建足够的测试方法与过分追求覆盖率之间如何进行平衡?
Vance:对这个问题可以从多种角度来回答。一种衡量角度是你是否编写了冗余的测试,另一种角度则是测试反馈周期影响时间有多长,例如是否由于你编写了过多的测试而使你的测试集运行起来不够快了。还有一种角度依赖于测试的级别,例如单元/隔离测试、集成测试、API测试和系统测试
我想你的问题是我所关注的那个角度,它也是关注软件测试的各种讨论中的核心部分,也就是使你的投入保持协调。其实这种说法的另一种含义,即是说对这个问题没有明确的答案,取决于实际情况。
如果你遵循测试驱动开发的方式,那么这个问题的答案与你仅仅追求测试覆盖率又有所不同,至少在开发的首个阶段来说是不同的。测试驱动开发更接近于一种软件开发的途径,而不是软件测试的方法。这种高度纪律性的开发方式驱使在编写实际的代码之前,用某个测试表现出你的意图,随后不断改进你的软件,通过这种方式创建出高质量的代码。
从测试覆盖率的角度来说,它主要是与风险评估相关。假设你来自一家创业公司,正在努力寻找自己的商业模式。如果你目前正在编写的软件在下一周就有可能被废弃,那你还愿意投入大量精力去确保这个软件毫无漏洞吗?另一方面,你能否在一定程度上保证你的软件质量,至少不要因为缺乏测试而让那些bug吓跑了你的潜在客户呢?如果你已经认识到你的软件缺乏足够的测试,而你也确定了你的商务模式,在这种情况下,你是否会按照更具有纪律性的方式去重写软件,填补缺失的代码覆盖率,以达到一种可接受的程度?或者是放任不管,而寄希望于软件本身的质量已经足够好了呢?此时,个人和公司对风险的容忍度将指导你做出决策。
再举一个例子,如果你接手了一个完全没有测试的软件项目,但该项目已经几乎不需要改动了,或是很快就将被取代了,那又怎么样呢?你很可能不会为这个系统编写完整的测试。不过,某些关键的函数或较高层次的改动依然需要测试。
典型的软件系统往往支持着一个正在运行中的商务模型,而且这个模型在未来几年内也需要继续运行,这样的系统就需要测试。有趣的是,我发现单元测试只有在语句覆盖率达到至少50%以上才能值会投入成本,至少要到70%至80%的语句覆盖率才能够体现出明显的好处。我曾经为一些具有高可靠性和高安全性的软件系统做过测试方面的培训,这些系统做到了100%的语句、分支和条件覆盖率这几个里程碑,它们对于测试系统故障确实起到了极大的作用。
有些观点在本书中并没有提到,其中之一就是追求高测试覆盖率并没有通常所想象的那么困难。实际上,本书曾考虑过以&高覆盖率的单元测试&命名。对许多来说,困难主要在于对可测性机制的理解。
InfoQ:您在本书最后一章讨论了软件考古学(Software Archaeology),能否请您讨论一下这方面内容,以及它对保证软件质量起到了怎样的帮助吗?
Vance:如果你没有任何测试或文档,那你不得不对你的意图进行反向工程,以得到待测试的系统(software under test)。有时你会发现,你对软件的产出感到力不从心,这让你感觉很受挫。有些时候,这是由于你的技能、洞察力或注意力的局限所造成的。但通常来说,真正的问题出自于退化的代码、不断变化的语境、无效的行为和莫名奇妙的临时方案等方面。
在你所提到的那一部分软件功能中,我无法找到某个异常是从哪里产生的。那段代码会根据异常信息做出一些处理,按照文档中的方法,它在这里使用了一些hack手段。我可以通过注入一个在表面上看来满足其预期模式的异常对象来完成测试方法,但这样一来,我所测试的就不是软件的意图,而是它具体的实现方式了。
经过仔细研究后才知道,原来这种奇怪的处理方式来自于一个早已被重构过的方法的老版本,异常处理代码也已经重新实现了。但问题在于老版本的代码依然留在系统中,结果就导致了无用的并且令人困惑的代码,而且难以进行测试。这些都属于软件质量方面的反模式,我们需要使用一些考古学的方法将其清除出去。
对代码从始至今的演化过程进行研究,在我们需要填补测试方法时,有助于使代码保持精准、整洁和易于测试。
Stephen还提到了以下内容,他认识测试的局限性往往是由糟糕的设计造成的,而不是由可测试性本身造成的。
&我最喜欢的一句名言来自于亨利福特(福特公司创始人):&无论你认为你行或者你不行,你都是对的!&。这句话对于测试来说同样成立。如同我在书中第13章所描述的,即使是一种最困难的测试,即重现某个竞态条件,也能够以大量的列举情境来驯服。在测试方面的困难更像是一种坏味道,它暗示着被测试的代码出现了问题,而不是可测试性本身的问题。&
Stephen Vance在过去的20年中几乎扮演过软件开发过程中每一个相关的角色。他曾为多个不同的产业机构解决过虚拟现实、工业机器人、互联网基础结构、企业商务和软件即服务等方面的问题。他经常在全球范围内对软件开发流程和配置管理方面提供顾问服务、举办培训和进行演讲。他现在在波士顿担任精益/敏捷软件开发的教练
查看英文原文:
Author Contacted
语言 & 开发
45 他的粉丝
架构 & 设计
205 他的粉丝
0 他的粉丝
4 他的粉丝
1 他的粉丝
5 他的粉丝
Book Review
0 他的粉丝
0 他的粉丝
12 他的粉丝
测试驱动开发
0 他的粉丝
1 他的粉丝
8 他的粉丝
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
赞助商链接
InfoQ每周精要
订阅InfoQ每周精要,加入拥有25万多名资深开发者的庞大技术社区。
架构 & 设计
文化 & 方法
<及所有内容,版权所有 &#169;
C4Media Inc.
服务器由 提供, 我们最信赖的ISP伙伴。
北京创新网媒广告有限公司
京ICP备号-7
找回密码....
InfoQ账号使用的E-mail
关注你最喜爱的话题和作者
快速浏览网站内你所感兴趣话题的精选内容。
内容自由定制
选择想要阅读的主题和喜爱的作者定制自己的新闻源。
设置通知机制以获取内容更新对您而言是否重要
注意:如果要修改您的邮箱,我们将会发送确认邮件到您原来的邮箱。
使用现有的公司名称
修改公司名称为:
公司性质:
使用现有的公司性质
修改公司性质为:
使用现有的公司规模
修改公司规模为:
使用现在的国家
使用现在的省份
Subscribe to our newsletter?
Subscribe to our industry email notices?
我们发现您在使用ad blocker。
我们理解您使用ad blocker的初衷,但为了保证InfoQ能够继续以免费方式为您服务,我们需要您的支持。InfoQ绝不会在未经您许可的情况下将您的数据提供给第三方。我们仅将其用于向读者发送相关广告内容。请您将InfoQ添加至白名单,感谢您的理解与支持。高质量JavaScript代码书写基本要点 - 简书
高质量JavaScript代码书写基本要点
英文原文:原文作者:译者:原文地址:
才华横溢的,在他写的由出版的新书(JavaScript模式)中,我想要是为我们的读者贡献其摘要,那会是件很美妙的事情。具体一点就是编写高质量JavaScript的一些要素,例如避免全局变量,使用单变量声明,在循环中预缓存length(长度),遵循代码阅读,以及更多。
此摘要也包括一些与代码不太相关的习惯,但对整体代码的创建息息相关,包括撰写API文档、执行同行评审以及运行JSLint。这些习惯和最佳做法可以帮助你写出更好的,更易于理解和维护的代码,这些代码在几个月或是几年之后再回过头看看也是会觉得很自豪的。
书写可维护的代码(Writing Maintainable Code )
软件bug的修复是昂贵的,并且随着时间的推移,这些bug的成本也会增加,尤其当这些bug潜伏并慢慢出现在已经发布的软件中时。当你发现bug的时候就立即修复它是最好的,此时你代码要解决的问题,在你脑中还是很清晰的。否则,你转移到其他任务,忘了那个特定的代码,一段时间后再去查看这些代码就需要:
花时间学习和理解这个问题
花时间是了解应该解决的问题代码
还有问题,特别对于大的项目或是公司,修复bug的这位伙计不是写代码的那个人(且发现bug和修复bug的不是同一个人)。因此,必须降低理解代码花费的时间,无论是一段时间前你自己写的代码,还是团队中的其他成员写的代码。这关系到底线(营业收入)和开发人员的幸福,因为我们更应该去开发新的激动人心的事物,而不是花几小时几天的时间去维护遗留代码。
另一个相关软件开发生命的事实是,读代码花费的时间要比写来得多。有时候,当你专注并深入思考某个问题的时候,你可以坐下来,一个下午写大量的代码。
你的代码很能很快就工作了。但是,随着应用的成熟,还会有很多其他的事情发生,这就要求你得进行审查,修改和调整。例如:
bug是暴露的
新功能被添加到应用程序
程序在新的环境下工作(例如,市场上出现新想浏览器)
代码改变用途
代码得完全从头重新移植到另一个架构上,或者甚至使用另一种语言
由于这些变化,很少人力数小时写的代码,最终演变成花数周来阅读这些代码。这就是为什么创建可维护的代码,对应用程序的成功至关重要。
可维护的代码意味着:
看上去就像是同一个人写的
最小全局变量(Minimizing Globals)
JavaScript通过函数管理作用域。在函数内部声明的变量,只在这个函数内部,函数外面不可用。另一方面,全局变量就是在任何函数外面声明的,或是未声明直接简单使用的。
每个JavaScript环境有一个全局对象,当你在任意的函数外面使用this的时候可以访问到。你创建的每一个全部变量都成了这个全局对象的属性。在浏览器中,方便起见,该全局对象有个附加属性叫做window,此window(通常)指向该全局对象本身。下面的代码片段显示了如何在浏览器环境中创建和访问的全局变量:
myglobal = "hello"; // 不推荐写法
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 +
此段代码中的result没有声明。代码照样运作正常,但在调用函数后你最后的结果就多一个全局命名空间,这可以是一个问题的根源。
经验法则是始终使用var声明变量,正如改进版的sum()函数所演示的:
function sum(x, y) {
var result = x +
另一个创建隐式全局变量的反例就是使用任务链进行部分var声明。下面的片段中,a是本地变量,但是b确实全局变量,这可能不是你希望发生的:
// 反例,勿使用
function foo() {
var a = b = 0;
此现象发生的原因在于这个从右到左的赋值,首先,是赋值表达式b = 0,此情况下b是未声明的。这个表达式的返回值是0,然后这个0就分配给了通过var定义的这个局部变量a。换句话说,就好比你输入了:
var a = (b = 0);
如果你已经准备好声明变量,使用链分配是比较好的做法,不会产生任何意料之外的全局变量,如:
function foo() {
// ... a = b = 0; // 两个均局部变量
然而,另外一个避免全局变量的原因是可移植性。如果想让你的代码在不同的环境下(主机下)运行,使用全局变量如履薄冰,因为你会无意中覆盖你最初环境下不存在的主机对象(所以,你原以为名称可以放心大胆地使用,实际上对于有些情况并不适用)。
忘记var的副作用(Side Effects When Forgetting var)
隐式全局变量和明确定义的全局变量间有些小的差异,就是通过delete操作符让变量未定义的能力。
通过var创建的全局变量(任何函数之外的程序中创建)是不能被删除的。
无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的。
这表明,在技术上,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性。属性是可以通过delete操作符删除的,而变量是不能的:
// 定义三个全局变量
var global_var = 1;
global_novar = 2; // 反面教材
(function () {
global_fromfunc = 3; // 反面教材
// 试图删除
delete global_ // false
delete global_ // true
delete global_ // true
// 测试该删除
typeof global_ // "number"
typeof global_ // "undefined"
typeof global_ // "undefined"
在ES5严格模式下,未声明的变量(如在前面的代码片段中的两个反面教材)工作时会抛出一个错误。
访问全局对象(Access to the Global Object)
在浏览器中,全局对象可以通过window属性在代码的任何位置访问(除非你做了些比较出格的事情,像是声明了一个名为window的局部变量)。但是在其他环境下,这个方便的属性可能被叫做其他什么东西(甚至在程序中不可用)。如果你需要在没有硬编码的window标识符下访问全局对象,你可以在任何层级的函数作用域中做如下操作:
var global = (function () {
这种方法可以随时获得全局对象,因为其在函数中被当做函数调用了(不是通过new构造),this总是指向全局对象。实际上这个病不适用于ECMAScript 5严格模式,所以,在严格模式下时,你必须采取不同的形式。例如,你正在开发一个JavaScript库,你可以将你的代码包裹在一个即时函数中,然后从全局作用域中,传递一个引用指向this作为你即时函数的参数。
单var形式(Single var Pattern)
在函数顶部使用单var语句是比较有用的一种形式,其好处在于:
提供了一个单一的地方去寻找功能所需要的所有局部变量
防止变量在定义之前使用的逻辑错误
帮助你记住声明的全局变量,因此较少了全局变量
//zxx:此处我自己是有点晕乎的…
少代码(类型啊传值啊单线完成)
单var形式长得就像下面这个样子:
function func() {
var a = 1,
sum = a + b,
myobject = {},
// function body...
您可以使用一个var语句声明多个变量,并以逗号分隔。像这种初始化变量同时初始化值的做法是很好的。这样子可以防止逻辑错误(所有未初始化但声明的变量的初始值是undefined)和增加代码的可读性。在你看到代码后,你可以根据初始化的值,知道这些变量大致的用途。例如,是要当作对象呢,还是当作整数来使。
你也可以在声明的时候做一些实际的工作。例如,前面代码中的sum = a + b这个情况。另外一个例子就是当你使用DOM(文档对象模型)引用时,你可以使用单一的var把DOM引用一起指定为局部变量,就如下面代码所示的:
function updateElement() {
var el = document.getElementById("result"),
style = el.
// 使用el和style干点其他什么事...
预解析:var散布的问题(Hoisting: A Problem with Scattered vars)
JavaScript中,你可以在函数的任何位置声明多个var语句,并且它们就好像是在函数顶部声明一样发挥作用,这种行为称为hoisting(悬置/置顶解析/预解析)。当你使用了一个变量,然后不久在函数中又重新声明的话,就可能产生逻辑错误。对于JavaScript,只要你的变量是在同一个作用域中(同一函数),它都被当做是声明的,即使是它在var声明前使用的时候。看下面这个例子:
myname = "global"; // 全局变量
function func() {
alert(myname); // "undefined"
var myname = "local";
alert(myname); // "local"
在这个例子中,你可能会以为第一个alert弹出的是”global”,第二个弹出”loacl”。这种期许是可以理解的,因为在第一个alert的时候,myname未声明,此时函数肯定很自然而然地看全局变量myname。但是,实际上并不是这么工作的。第一个alert会弹出”undefined”,是因为myname被当做了函数的局部变量(尽管是之后声明的),所有的变量声明当被悬置到函数的顶部了。因此,为了避免这种混乱,最好是预先声明你想使用的全部变量。
上面的代码片段执行的行为,可能就像下面这样:
myname = "global"; // global variable
function func() {
// 等同于 -& var myname =
alert(myname); // "undefined"
myname = "local";
alert(myname); // "local"}
关于JavaScript的置顶解析,我上周专门翻译了篇文章,您有兴趣可以看看:“”。
为了完整,我们再提一提执行层面稍微复杂点的东西。代码处理分两个阶段:第一阶段是变量,函数声明,以及正常格式的参数创建,这是一个解析和进入上下文的阶段。第二个阶段是代码执行,函数表达式和不合格的标识符(为声明的变量)被创建。但是,出于实用的目的,我们就采用了”hoisting”这个概念,这种ECMAScript标准中并未定义,通常用来描述行为。
for循环(for Loops)
在for循环中,你可以循环取得数组或是数组类似对象的值,譬如arguments和HTMLCollection对象。通常的循环形式如下:
// 次佳的循环
for (var i = 0; i & myarray. 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 (var i = 0, max = myarray. i & i++) {
// 使用myarray[i]做点什么
这样,在这个循环过程中,你只检索了一次长度值。
在所有浏览器下,循环获取内容时缓存HTMLCollections的长度是更快的,2倍(Safari3)到190倍(IE7)之间。//zxx:此数据貌似很老,仅供参考
注意到,当你明确想要修改循环中集合的时候(例如,添加更多的DOM元素),你可能更喜欢长度更新而不是常量。
伴随着单var形式,你可以把变量从循环中提出来,就像下面这样:
function looper() {
var i = 0,
myarray = [];
for (i = 0, max = myarray. i & i++) {
// 使用myarray[i]做点什么
这种形式具有一致性的好处,因为你坚持了单一var形式。不足在于当重构代码的时候,复制和粘贴整个循环有点困难。例如,你从一个函数复制了一个循环到另一个函数,你不得不去确定你能够把i和max引入新的函数(如果在这里没有用的话,很有可能你要从原函数中把它们删掉)。
最后,一个需要对循环进行调整的是使用下面表达式之一来替换i++。
JSLint提示您这样做,原因是++和–-促进了“过分棘手(excessive trickiness)”。//zxx:这里比较难翻译,我想本意应该是让代码变得更加的棘手
如果你直接无视它,JSLint的plusplus选项会是false(默认是default)。
还有两种变化的形式,其又有了些微改进,因为:
少了一个变量(无max)
向下数到0,通常更快,因为和0做比较要比和数组长度或是其他不是0的东西作比较更有效率
第一种变化的形式:
var i, myarray = [];
for (i = myarray. i–-;) {
// 使用myarray[i]做点什么
第二种使用while循环:
var myarray = [],
i = myarray.
while (i–-) {
// 使用myarray[i]做点什么
这些小的改进只体现在性能上,此外JSLint会对使用i–-加以抱怨。
for-in循环(for-in Loops)
for-in循环应该用在非数组对象的遍历上,使用for-in进行循环也被称为“枚举”。
从技术上将,你可以使用for-in循环数组(因为JavaScript中数组也是对象),但这是不推荐的。因为如果数组对象已被自定义的功能增强,就可能发生逻辑错误。另外,在for-in中,属性列表的顺序(序列)是不能保证的。所以最好数组使用正常的for循环,对象使用for-in循环。
有个很重要的hasOwnProperty()方法,当遍历对象属性的时候可以过滤掉从原型链上下来的属性。
思考下面一段代码:
var man = {
// 在代码的某个地方
// 一个方法添加给了所有对象
if (typeof Object.prototype.clone === "undefined") {
Object.prototype.clone = function () {};
在这个例子中,我们有一个使用对象字面量定义的名叫man的对象。在man定义完成后的某个地方,在对象原型上增加了一个很有用的名叫clone()的方法。此原型链是实时的,这就意味着所有的对象自动可以访问新的方法。为了避免枚举man的时候出现clone()方法,你需要应用hasOwnProperty()方法过滤原型属性。如果不做过滤,会导致clone()函数显示出来,在大多数情况下这是不希望出现的。
// for-in 循环
for (var i in man) {
if (man.hasOwnProperty(i)) { // 过滤
console.log(i, ":", man[i]);
/* 控制台显示结果
// 反面例子:
// for-in loop without checking hasOwnProperty()
for (var i in man) {
console.log(i, ":", man[i]);
控制台显示结果
clone: function()
另外一种使用hasOwnProperty()的形式是取消Object.prototype上的方法。像是:
for (var i in man) {
if (Object.prototype.hasOwnProperty.call(man, i)) { // 过滤
console.log(i, ":", man[i]);
其好处在于在man对象重新定义hasOwnProperty情况下避免命名冲突。也避免了长属性查找对象的所有方法,你可以使用局部变量“缓存”它。
var i, hasOwn = Object.prototype.hasOwnP
for (i in man) {
if (hasOwn.call(man, i)) { // 过滤
console.log(i, ":", man[i]);
严格来说,不使用hasOwnProperty()并不是一个错误。根据任务以及你对代码的自信程度,你可以跳过它以提高些许的循环速度。但是当你对当前对象内容(和其原型链)不确定的时候,添加hasOwnProperty()更加保险些。
格式化的变化(通不过JSLint)会直接忽略掉花括号,把if语句放到同一行上。其优点在于循环语句读起来就像一个完整的想法(每个元素都有一个自己的属性”X”,使用”X”干点什么):
// 警告: 通不过JSLint检测
var i, hasOwn = Object.prototype.hasOwnP
for (i in man) if (hasOwn.call(man, i)) { // 过滤
console.log(i, ":", man[i]);
(不)扩展内置原型((Not) Augmenting Built-in Prototypes)
扩增构造函数的prototype属性是个很强大的增加功能的方法,但有时候它太强大了。
增加内置的构造函数原型(如Object(), Array(), 或Function())挺诱人的,但是这严重降低了可维护性,因为它让你的代码变得难以预测。使用你代码的其他开发人员很可能更期望使用内置的JavaScript方法来持续不断地工作,而不是你另加的方法。
另外,属性添加到原型中,可能会导致不使用hasOwnProperty属性时在循环中显示出来,这会造成混乱。
因此,不增加内置原型是最好的。你可以指定一个规则,仅当下面的条件均满足时例外:
可以预期将来的ECMAScript版本,或是JavaScript实现将一直将此功能当作内置方法来实现。例如,你可以添加ECMAScript 5中描述的方法,一直到各个浏览器都迎头赶上。这种情况下,你只是提前定义了有用的方法。
如果您检查您的自定义属性或方法已不存在——也许已经在代码的其他地方实现,或已经是你支持的浏览器JavaScript引擎部分。
你清楚地文档记录并和团队交流了变化。
如果这三个条件得到满足,你可以给原型进行自定义的添加,形式如下:
if (typeof Object.protoype.myMethod !== "function") {
Object.protoype.myMethod = function () {
// 实现...
switch形式(switch Pattern)
你可以通过类似下面形式的switch语句,增强可读性和健壮性:
var inspect_me = 0,
result = '';
switch (inspect_me) {
result = "zero";
result = "one";
result = "unknown";
这个简单的例子中所遵循的风格约定如下:
每个case和switch对齐(花括号缩进规则除外)
每个case中代码缩进
每个case以break清除结束
避免贯穿(故意忽略break)。如果你非常确信贯穿是最好的方法,务必记录此情况,因为对于有些阅读人而言,它们可能看起来是错误的。
以default结束switch:确保总有健全的结果,即使无情况匹配。
避免隐式类型转换(Avoiding Implied Typecasting)
JavaScript的变量在比较的时候会隐式类型转换。这就是为什么一些诸如:false == 0或“” == 0返回的结果是true。为避免引起混乱的隐含类型转换,在你比较值和表达式类型的时候始终使用===和!==操作符。
var zero = 0;
if (zero === false) {
// 不执行,因为zero为0, 而不是false
// 反面示例
if (zero == false) {
// 执行了...
还有另外一种思想观点认为==就足够了===是多余的。例如,当你使用typeof你就知道它会返回一个字符串,所以没有使用严格相等的理由。然而,JSLint要求严格相等,它使代码看上去更有一致性,可以降低代码阅读时的精力消耗。(“==是故意的还是一个疏漏?”)
避免(Avoiding) eval()
如果你现在的代码中使用了eval(),记住该咒语“eval()是魔鬼”。此方法接受任意的字符串,并当作JavaScript代码来处理。当有问题的代码是事先知道的(不是运行时确定的),没有理由使用eval()。如果代码是在运行时动态生成,有一个更好的方式不使用eval而达到同样的目标。例如,用方括号表示法来访问动态属性,会更好更简单:
// 反面示例
var property = "name";
alert(eval("obj." + property));
var property = "name";
alert(obj[property]);
使用eval()也带来了安全隐患,因为被执行的代码(例如从网络来)可能已被篡改。这是个很常见的反面教材,当处理Ajax请求得到的JSON 相应的时候。在这些情况下,最好使用JavaScript内置方法来解析JSON相应,以确保安全和有效。若浏览器不支持JSON.parse(),你可以使用来自JSON.org的库。
同样重要的是要记住,给setInterval(), setTimeout()和Function()构造函数传递字符串,大部分情况下,与使用eval()是类似的,因此要避免。在幕后,JavaScript仍需要评估和执行你给程序传递的字符串:
// 反面示例
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
使用新的Function()构造就类似于eval(),应小心接近。这可能是一个强大的构造,但往往被误用。如果你绝对必须使用eval(),你可以考虑使用new Function()代替。有一个小的潜在好处,因为在新Function()中作代码评估是在局部函数作用域中运行,所以代码中任何被评估的通过var定义的变量都不会自动变成全局变量。另一种方法来阻止自动全局变量是封装eval()调用到一个即时函数中。
考虑下面这个例子,这里仅un作为全局变量污染了命名空间。
console.log(typeof un);
// "undefined"
console.log(typeof deux); // "undefined"
console.log(typeof trois); // "undefined"
var jsstring = "var un = 1; console.log(un);";
eval(jsstring); // logs "1"
jsstring = "var deux = 2; console.log(deux);";
new Function(jsstring)(); // logs "2"
jsstring = "var trois = 3; console.log(trois);";
(function () {
eval(jsstring);
}()); // logs "3"
console.log(typeof un); // number
console.log(typeof deux); // "undefined"
console.log(typeof trois); // "undefined"
另一间eval()和Function构造不同的是,eval()可以干扰作用域链,而Function()更安分守己些。不管你在哪里执行Function(),它只看到全局作用域。所以其能很好的避免本地变量污染。在下面这个例子中,eval()可以访问和修改它外部作用域中的变量,这是Function做不来的(注意到使用Function和new Function是相同的)。
(function () {
var local = 1;
eval("local = 3; console.log(local)"); // logs "3"
console.log(local); // logs "3"
(function () {
var local = 1;
Function("console.log(typeof local);")(); // logs undefined
parseInt()下的数值转换(Number Conversions with parseInt())
使用parseInt()你可以从字符串中获取数值,该方法接受另一个基数参数,这经常省略,但不应该。当字符串以”0″开头的时候就有可能会出问题,例如,部分时间进入表单域,在ECMAScript 3中,开头为”0″的字符串被当做8进制处理了,但这已在ECMAScript 5中改变了。为了避免矛盾和意外的结果,总是指定基数参数。
var month = "06",
year = "09";
month = parseInt(month, 10);
year = parseInt(year, 10);
此例中,如果你忽略了基数参数,如parseInt(year),返回的值将是0,因为“09”被当做8进制(好比执行 parseInt( year, 8 )),而09在8进制中不是个有效数字。
替换方法是将字符串转换成数字,包括:
+"08" // 结果是 8
Number("08") // 8
这些通常快于parseInt(),因为parseInt()方法,顾名思意,不是简单地解析与转换。但是,如果你想输入例如“08 hello”,parseInt()将返回数字,而其它以NaN告终。
编码规范(Coding Conventions)
建立和遵循编码规范是很重要的,这让你的代码保持一致性,可预测,更易于阅读和理解。一个新的开发者加入这个团队可以通读规范,理解其它团队成员书写的代码,更快上手干活。
许多激烈的争论发生会议上或是邮件列表上,问题往往针对某些代码规范的特定方面(例如代码缩进,是Tab制表符键还是space空格键)。如果你组织中建议采用规范的,准备好面对各种反对的,或是听起来不同但很强烈的观点。要记住,建立和坚定不移地遵循规范要比纠结于规范的细节重要的多。
缩进(Indentation)
代码没有缩进基本上就不能读了。唯一糟糕的事情就是不一致的缩进,因为它看上去像是遵循了规范,但是可能一路上伴随着混乱和惊奇。重要的是规范地使用缩进。
一些开发人员更喜欢用tab制表符缩进,因为任何人都可以调整他们的编辑器以自己喜欢的空格数来显示Tab。有些人喜欢空格——通常四个,这都无所谓,只要团队每个人都遵循同一个规范就好了。这本书,例如,使用四个空格缩进,这也是JSLint中默认的缩进。
什么应该缩进呢?规则很简单——花括号里面的东西。这就意味着函数体,循环 (do, while, for, for-in),if,switch,以及对象字面量中的对象属性。下面的代码就是使用缩进的示例:
function outer(a, b) {
var c = 1,
if (a & b) {
inner = function () {
inner = function () {
花括号{}(Curly Braces)
花括号(亦称大括号,下同)应总被使用,即使在它们为可选的时候。技术上将,在in或是for中如果语句仅一条,花括号是不需要的,但是你还是应该总是使用它们,这会让代码更有持续性和易于更新。
想象下你有一个只有一条语句的for循环,你可以忽略花括号,而没有解析的错误。
// 糟糕的实例
for (var i = 0; i & 10; i += 1)
但是,如果,后来,主体循环部分又增加了行代码?
// 糟糕的实例
for (var i = 0; i & 10; i += 1)
alert(i + " is " + (i % 2 ? "odd" : "even"));
第二个alert已经在循环之外,缩进可能欺骗了你。为了长远打算,最好总是使用花括号,即时值一行代码:
// 好的实例
for (var i = 0; i & 10; i += 1) {
if条件类似:
if (true) {
左花括号的位置(Opening Brace Location)
开发人员对于左大括号的位置有着不同的偏好——在同一行或是下一行。
if (true) {
alert("It's TRUE!");
alert("It's TRUE!");
这个实例中,仁者见仁智者见智,但也有个案,括号位置不同会有不同的行为表现。这是因为分号插入机制(semicolon insertion mechanism)——JavaScript是不挑剔的,当你选择不使用分号结束一行代码时JavaScript会自己帮你补上。这种行为可能会导致麻烦,如当你返回对象字面量,而左括号却在下一行的时候:
// 警告: 意外的返回值
function func() {
// 下面代码不执行
name : "Batman"
如果你希望函数返回一个含有name属性的对象,你会惊讶。由于隐含分号,函数返回undefined。前面的代码等价于:
// 警告: 意外的返回值
function func() {
// 下面代码不执行
name : "Batman"
总之,总是使用花括号,并始终把在与之前的语句放在同一行:
function func() {
name : "Batman"
关于分号注:就像使用花括号,你应该总是使用分号,即使他们可由JavaScript解析器隐式创建。这不仅促进更科学和更严格的代码,而且有助于解决存有疑惑的地方,就如前面的例子显示。
空格(White Space)
空格的使用同样有助于改善代码的可读性和一致性。在写英文句子的时候,在逗号和句号后面会使用间隔。在JavaScript中,你可以按照同样的逻辑在列表模样表达式(相当于逗号)和结束语句(相对于完成了“想法”)后面添加间隔。
适合使用空格的地方包括:
for循环分号分开后的的部分:如for (var i = 0; i & 10; i += 1) {...}
for循环中初始化的多变量(i和max):for (var i = 0, max = 10; i & i += 1) {...}
分隔数组项的逗号的后面:var a = [1, 2, 3];
对象属性逗号的后面以及分隔属性名和属性值的冒号的后面:var o = {a: 1, b: 2};
限定函数参数:myFunc(a, b, c)
函数声明的花括号的前面:function myFunc() {}
匿名函数表达式function的后面:var myFunc = function () {};
使用空格分开所有的操作符和操作对象是另一个不错的使用,这意味着在+, -, *, =, &, &, &=, &=, ===, !==, &&, ||, +=等前后都需要空格。
// 宽松一致的间距
// 使代码更易读
// 使得更加“透气”
var d = 0,
a = b + 1;
if (a && b && c) {
// 反面例子
// 缺失或间距不一
// 使代码变得疑惑
var d = 0,
a = b + 1;
if (a && b && c) {
//zxx:我就琢磨着这正面和反面例子不长得一样吗...原文就是如此,我也不好擅自改动。
最后需要注意的一个空格——花括号间距。最好使用空格:
函数、if-else语句、循环、对象字面量的左花括号的前面({)
else或while之间的右花括号(})
空格使用的一点不足就是增加了文件的大小,但是压缩无此问题。
有一个经常被忽略的代码可读性方面是垂直空格的使用。你可以使用空行来分隔代码单元,就像是文学作品中使用段落分隔一样。
命名规范(Naming Conventions)
另一种方法,让你的代码更具可预测性和可维护性是采用命名规范。这就意味着你需要用同一种形式,给你的变量和函数命名。
下面是建议的一些命名规范,你可以原样采用,也可以根据自己的喜好作调整。同样,遵循规范要比规范是什么更重要。
以大写字母写构造函数(Capitalizing Constructors)
JavaScript并没有类,但有new调用的构造函数:
var adam = new Person();
因为构造函数仍仅仅是函数,仅看函数名就可以帮助告诉你这应该是一个构造函数还是一个正常的函数。
命名构造函数时,首字母大写具有暗示作用,使用小写命名的函数和方法不应该使用new调用:
function MyConstructor() {...}
function myFunction() {...}
分隔单词(Separating Words)
当你的变量或是函数名有多个单词的时候,最好单词的分离遵循统一的规范,有一个常见的做法被称作“驼峰(Camel)命名法”,就是单词小写,每个单词的首字母大写。
对于构造函数,可以使用大驼峰式命名法(upper camel case),如MyConstructor()。对于函数和方法名称,你可以使用小驼峰式命名法(lower camel case),像是myFunction(), calculateArea()和getFirstName()。
要是变量不是函数呢?开发者通常使用小驼峰式命名法,但还有另外一种做法,就是所有单词小写以下划线连接:例如,first_name, favorite_bands, 和old_company_name,这种标记法帮你直观地区分函数和其他标识——原型和对象。
ECMAScript的属性和方法均使用Camel标记法,尽管多字的属性名称是罕见的(正则表达式对象的lastIndex和ignoreCase属性)。
其它命名形式(Other Naming Patterns)
有时,开发人员使用命名规范来弥补或替代语言特性。
例如,JavaScript中没有定义常量的方法(尽管有些内置的像Number, MAX_VALUE),所以开发者都采用全部单词大写的规范,来命名这个程序生命周期中都不会改变的变量,如:
// 珍贵常数,只可远观
var PI = 3.14,
MAX_WIDTH = 800;
还有另外一个完全大写的惯例:全局变量名字全部大写。全部大写命名全局变量可以加强减小全局变量数量的实践,同时让它们易于区分。
另外一种使用规范来模拟功能的是私有成员。虽然可以在JavaScript中实现真正的私有,但是开发者发现仅仅使用一个下划线前缀来表示一个私有属性或方法会更容易些。考虑下面的例子:
var person = {
getName: function () {
return this._getFirst() + ' ' + this._getLast();
_getFirst: function () {
_getLast: function () {
在此例中,getName()就表示公共方法,部分稳定的API。而_getFirst()和_getLast()则表明了私有。它们仍然是正常的公共方法。但是使用下划线前缀来警告person对象的使用者,这些方法在下一个版本中时不能保证工作的,是不能直接使用的。注意,JSLint有些不鸟下划线前缀,除非你设置了noman选项为:false。
下面是一些常见的_private规范:
使用尾下划线表示私有,如name_和getElements_()
使用一个下划线前缀表_protected(保护)属性,两个下划线前缀表示__private (私有)属性
Firefox中一些内置的变量属性不属于该语言的技术部分,使用两个前下划线和两个后下划线表示,如:__proto__和__parent__。
注释(Writing Comments)
你必须注释你的代码,即使不会有其他人向你一样接触它。通常,当你深入研究一个问题,你会很清楚的知道这个代码是干嘛用的,但是,当你一周之后再回来看的时候,想必也要耗掉不少脑细胞去搞明白到底怎么工作的。
很显然,注释不能走极端:每个单独变量或是单独一行。但是,你通常应该记录所有的函数,它们的参数和返回值,或是任何不寻常的技术和方法。要想到注释可以给你代码未来的阅读者以诸多提示;阅读者需要的是(不要读太多的东西)仅注释和函数属性名来理解你的代码。例如,当你有五六行程序执行特定的任务,如果你提供了一行代码目的,以及为什么在这里的描述的话,阅读者就可以直接跳过这段细节。没有硬性规定注释代码比,代码的某些部分(如正则表达式)可能注释要比代码多。
最重要的习惯,然而也是最难遵守的,就是保持注释的及时更新,因为过时的注释比没有注释更加的误导人。
关于作者(About the Author)
Stoyan Stefanov是Yahoo!web开发人员,多个O'Reilly书籍的作者、投稿者和技术评审。他经常在会议和他的博客上发表web开发主题的演讲。Stoyan还是smush.it图片优化工具的创造者,YUI贡献者,雅虎性能优化工具YSlow 2.0的架构设计师。
编程学习分享者,专注前端。
微博与微信:【IT程序狮】

我要回帖

更多关于 dnf一键完成任务 的文章

 

随机推荐