`
runfriends
  • 浏览: 228885 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

javascript里住着魔鬼

 
阅读更多

 

这一次还是说一些小问题。这些“小”问题在工程规模小的情况下不算什么事,不过在大规模的软件工程中却是非常不好的实践。同时个人认为这些问题也不符合良好的代码规范。既然是小问题,为什么还说它们是魔鬼?魔鬼都存在于细微处,至于它们会导致什么严重问题,且看我一一道来。

要说的第一个问题是全局变量。在js代码规范里,全局变量是受到严格限制的,甚至是禁止使用的。从各种著名的js库的代码风格中就可见一二。不论jQueryExt,还是yui它们只定义了一个全局对象,就是它们的名字。Dojo对于全局对象也有严格控制,它们分别是dojodojoxdijit,这三个全局对象划分了dojo的核心api、尚不稳定的api、以及各类富客户端组件。

对于被严格限制的全局对象,并利用局部变量、闭包、成员属性、回调函数作为组件、模块、功能之间的交互手段显而易见的好处就是,配合类似java的包特性创建用点访问的链式调用,不同的组件、模块、功能在不同的包里定义,各方互不干扰,每个组件、模块、功能能且只能操作自己组件内部定义的变量,将不虞因全局变量重名带来的各种意料不到的后果。

另外一个各个不同的组件都在各自包内定义,只对外开放有限的接口;而不像全部使用全局变量、全局函数一样,什么都能被组件外部访问到,这样做可以最大程度的降低在运行时其它组件对本组件的影响,而一个组件发生了变化也会把对其它组件影响降低到最低,因为暴露出来的接口非常少,如果一次修改对依赖它的另外某个组件产生了影响,会花费更少的时间去发现它,并修正它。暴露的接口越少维护和开发成本也会越低,需要的人更少,同样多的人就能做更多的事。

也许有人会问如果模块之间交互时需要大量的参数,而且在不同情况下可能会需要不同的参数该怎么办呢?难道全局变量不是最简单的办法吗?全局变量乍看起来似乎是一个简单的方法,但是它的简单不是简明、简易,而是简单粗暴。而且在这个过程中仍然难以避免与其它模块的重名问题。这些问题可能暂时不会暴发,不过随着工程规模越来越大,暴发问题的可能性也会越来越大。另一方面,使用全局变量,并不能降低模块间的耦合性,反而会因为各个模块都要依赖全局对象而在模块间产生了黏性,它们严格依赖全局对象才能运行,对于js代码的组织、重构、升级都不得不考虑全局对象可能会对组件产生的影响,而且作为组件开发者有时候恐怕难以确认从全局对象得到的值究竟是不是当前这一次调用得到的还是之前的某一次调用得到的,为了减少出错的可能性,在组件用完退出的时候还要做很多全局清理工作;而没有人能保证执行这些清理工作的函数会不会被开发人员调用,当然组件开发人员可以在组件自己的页面触发unload事件时执行清理。这样做又为组件开发人员带来了更大的代码量。代码量越大,工作量就越大,给将来维护代码、变更接口同样要维护清理函数,会给从开发到维护、从测试到运维带来更多困难。将来参数、接口、需求发生变更,这些代码都要跟着变更,是一个恐怖的工作。而且大多数时候,需要修改的代码量不多,但是要修改许多处代码,要修改的内容过于分散、零碎,容易遗漏。足见这么做的问题就是维护困难、内聚低,也没有降低耦合性。

那么有没有简单易行,又能降低耦合,提高内聚,还能集中管理代码的方法呢?

答案显而易见。如今已经有各种不同的设计模式可以用来解决这样的问题。比如可以使用oop,配合ioc实现js的对象注入。可以创建自定义事件模型,把组件间的交互定义为事件,通过回调函数完成交互工作。可以使用观察者模式,被依赖的组件作为观察者,依赖其它组件的组件作为被观察者,当交互发生时,被观察者通知观察者,观察者执行功能并返回被观察者需要的结果。观察者模式或自定义事件,可以由一个单例对象统一管理所有的自定义事件或观察者和被观察者,所有组件交互数据也都通过这个单例对象传播,一方面既杜绝了全局对象可能引起的问题,另一方面在单例对象中并不保留任何业务数据和对象的引用,它只是起到中转站的作用也就省去了清理数据的工作。另外大多数工作都交给这个单例对象完成了,开发人员也就有更多时间去关注业务实现,二者的实现方式差不多,也不是本文的重点,不再赘述。上述三种方法都不是最简单和耦合最低的。下面介绍一种最简单和耦合最低的模式——发布/订阅模式。

名字可能大家都已耳熟能详了。这里只介绍下它在组件交互中的意义和原理,如果大家对它的具体实现有兴趣,将来有机会我可能还会写一篇专门介绍在不借助第三方库的前提下如何实现发布/订阅模式。

发布/订阅模式与观察者模式、事件模型有相似之处,它也有三个对象——发布者(publisher)、订阅者(subscriber)、主题(topic)——参与整个处理过程。我们同样需要一个单例对象(我们暂且叫它pubsub)管理所有的主题以及每个订阅者的处理方式。

发布者可以是任何js对象,它首先用一个字符串向pubsub注册。这个字符串就是主题名,如果已经有同名主题存在就抛出异常(也可以认为相同的主题可以由不同的发布者发布,这样就不必抛出异常。但在这种情况下就必须为主题定义主题发布规范,不同的主题可以有不同的规范、格式,相同的主题必须遵守相同的规范、格式),这样就能在最短的时间内解决重名问题,为了避免不同模块的开发人员注册同名的主题,最好为主题定义一个命名规范,用类似定义java包名和类名的方式定义主题名。随后发布者就可以发布自己的主题了(pub(topic)),主题可以是一个数字、一个字符串、一个正则表达式、甚至可以是一个函数或一个复杂的对象。发布者只负责发布主题,不必关心有没有订阅者或订阅者如何处理,以及处理后该做什么。一旦主题发布了就跟发布者没有关系了。

任意对象也可以是订阅者,一个对象既可以是订阅者也可以是发布者,甚至一个对象可以订阅自己发布的主题。只要订阅者与发布者知道相同的主题名就可以实现二者之间的交互。订阅者通知pubsub自己要订阅一个主题同时向pubsub传递一个对象或回调函数(视具体实现而定),如果一个主题尚未被注册或发布就抛出异常(严格的发布订阅模式要求抛出异常,不过个人认为这里可以灵活处理,视具体要求不同我们可以订阅尚不存在的主题,因为我们确定知道这样的主题一定会被注册并发布,而且我们的模块需要这些主题)。

最后任意对象都可以作为主题内容,只要内容遵守主题的约定、规范、格式就行。一个主题被发布了,pubsub机制调用订阅者处理对象,将主题作为参数传入订阅者处理对象。

这就是发布/订阅模式的整个过程。是不是很简单?善加利用它,就能杜绝全局变量对windowtop对象的污染。同时又能杜绝因重名引起的模块交互问题。当然最后不得不提的一点是,如果一个对象不再需要某个主题最好从pubsub对象取消订阅,一个对象不再发布主题时就从pubsub对象注销(在这里,如果有多个发布者发布同一主题可以不必实现注销功能)。不取消/注销的话不会对程序功能造成影响,不过会占用内存,成为垃圾,影响性能。

第二个问题,就是重复代码太多。复制已有代码稍加修改就完成一个新功能,这似乎是一个效率很高的工作。但是随着项目进行,这些代码将会成为维护人员的梦魇。最突出的问题就是需求一变、接口一变,这些逻辑相似又不完全一样的代码就要全都改一遍,可能这些代码分散在多处,容易遗漏。还是那句话:代码量越大,维护工作量越大,工作量越大,越容易出错。所以即使是为了自己,将来修改更容易,也绝对不要这么做。可能有人会想将来上线了就不是我维护了,那时候我在哪还不知道呢。不过在你离开以前,任何需求、接口的改变还是你要负责的,所以这种想法不可以有。即使说将来要走,结构简要、逻辑清晰、层次分明简洁的代码显然更受欢迎,交接更容易。这么做也更容易锻炼自己的技术水平,即使你不要成为技术大拿,曾经力求简明、干净、清晰的代码也会对从事其它工作更有益处。代码风格清新、干净、简洁,做其它事情也会有同样的风格。谁不喜欢这样的人。也不用担心由于自己的代码一目了然,一看就懂,会对自己有不利影响;相反所有人都会因为看到了一目了然的代码,而去喜欢这些代码的作者。你甚至会逐渐形成一种氛围,进而创建一个微型的以你的代码风格为基础的生态环境。将来这个环境中的人可能会发生变化,但是它已经有了灵魂,它会产生更大的影响,在你不在的地方发挥作用,影响公司内越来越多的人。很伟大吧。所以坚持两个原则——凡是被使用至少两次的代码都尽量抽象出新的方法;一个方法代码行尽量少,尽量保持一个屏幕就能显示完整整个方法的定义。

最后的问题,就是=====!=!==的区别,还有parseInteval、‘+’连接字符串等问题。

关于js的相等性比较,==!=会发生自动类型转换,所以有时候可能会引起不易察觉的错误。就像在java里我们都建议重写equals进行对象的相等性比较一样,在js里都建议使用===!==

parseInt会把字符串找到的第一个数字子串转化成Number类型,所以’123’’abc123’调用parseInt的结果一样都是123。这种特性的意义我只发现在需要把’1px’这样的字符串转化成数字时有用,其它时候只可能带来混乱。不过个人还是觉的为了代码风格统一还是不要用parseInt(‘1px’)这样的代码。

eval函数的用处不多说,既然这篇文章是要讲魔鬼的,就只说下潜藏在eval里的魔鬼。Js代码被加载完成后,js引擎会把js代码文本解析成js语法树然后基于这棵语法树完成我们希望js去完成的工作。解析语法树的过程一般只在加载完成时执行一次。而evalnew Function使js引擎再次执行,把传入的实参再次解析成一棵语法树。这种方式,产生了在java中使用’+’连接sql/hql的类似结果(关于这个问题请参考我在上一期发表的文章)。还有一个问题是当传入的实参来自不受信站点时会产生安全问题,当然这种问题在我们这里不再赘述。另外传说ecma组织将要在标准中去掉eval。有时候我们还是不得不使用evalnew Function的,比如将json串转化成js对象。

至于使用‘+’连接字符串,除ie以外其它浏览器都对这个过程进行了优化,没有性能问题。唯独ie需要特殊对待,使用‘+’连接字符串在ie中同样有性能问题。我们可以使用[‘a’,’b’,’c’].join(‘’)连接一个字符串,join用接收的参数分割数组中的每个元素,并连接成一个字符串。前面的那个join调用构造一个值是’abc’的字符串。如果一个字符串太长,可以在一个字符串中断行,每行尾以‘\’结束。

0
0
分享到:
评论

相关推荐

    head first JavaScript源码

    JavaScript是一种广泛应用于网页和网络应用的脚本语言,尤其在前端开发中扮演着至关重要的角色。下面我们将详细探讨JavaScript的基础知识、核心概念以及它在实际应用中的重要性。 首先,JavaScript的基础知识包括...

    深入理解JavaScript系列

    本书是一本全面、深入介绍JavaScript语言的学习指南。本书共分四个部分,第1部分帮助读者快速入手,掌握基本的JavaScript编程要点;第2部分介绍JavaScript的发展和技术背景;第3部分深入探索JavaScript,介绍了语法...

    JavaScript语言精粹完整版

    JavaScript是一种弱类型的语言,这意味着变量在声明时不需要指定类型,而类型是在运行时自动确定的。它支持基本数据类型如数字(Number)、字符串(String)、布尔值(Boolean)、空值(null)和未定义值(undefined),以及...

    JavaScript文档

    本资源包包含了多个关于JavaScript的重要参考资料,包括“JavaScript权威指南(英文版).chm”、“javascript5.5.chm”、“css2gb.chm”以及“JavaScript对象与数组参考大全 .chm”。 首先,我们来看“JavaScript权威...

    JavaScript函数(源代码)

    JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)...

    VS2015安装证书,JavaScript_ProjectSystem.msi,JavaScript_LanguageService.msi

    在这个场景中,我们关注的是VS2015的安装过程中涉及到的证书问题以及两个特定的组件:JavaScript_ProjectSystem.msi和JavaScript_LanguageService.msi。 首先,关于“VS2015安装证书”,这通常是指安装过程中需要...

    JavaScript学习指南 高清 PDF

    JavaScript,一种广泛应用于Web开发的脚本语言,是前端开发的核心技术之一。这份"JavaScript学习指南"高清PDF,无疑为想要深入理解JavaScript的初学者或有一定基础的开发者提供了宝贵的资源。下面,我们将深入探讨...

    JavaScript课堂习题答案

    JavaScript课堂习题答案JavaScript课堂习题答案JavaScript课堂习题答案JavaScript课堂习题答案JavaScript课堂习题答案JavaScript课堂习题答案JavaScript课堂习题答案JavaScript课堂习题答案JavaScript课堂习题答案...

    javaScript api

    JavaScript API 是一种重要的编程资源,它为开发者提供了与JavaScript语言交互的接口和工具。这个离线版的JavaScript API 指南,以CHM(Compiled Help Manual)格式提供,是开发者学习和查阅JavaScript相关功能和...

    head first javascript 中文版.pdf

    函数在JavaScript中扮演着重要角色,它们可以作为参数传递,也可以作为返回值。此外,JavaScript支持函数表达式和匿名函数,这使得函数可以更加灵活地使用。闭包是JavaScript中的一个高级概念,它允许函数访问并操作...

    javascript试题(附答案)

    JavaScript是一种广泛应用于网页和网络应用的编程语言,尤其在客户端脚本方面扮演着核心角色。这份"javascript试题(附答案)"是为初学者设计的,旨在帮助他们更好地理解和掌握JavaScript的基础知识。 一、变量与数据...

    JavaScript全栈教程 (廖雪峰) 完整版PDF

    在Web世界里,只有JavaScript能跨平台、跨浏览器驱动网 页,与用户交互。 《JavaScript全栈教程》是小白的零基础JavaScript全栈教程。从基础入门在讲到node使用实践,感兴趣的可以下载学习,离线文件非常方面。

    [JavaScript权威指南(第6版)]

    《JavaScript权威指南(第6版)》主要讲述的内容涵盖JavaScript语言本身,以及Web浏览器所实现的JavaScript API。本书第6版涵盖了HTML5和ECMAScript 5,很多章节完全重写,增加了当今Web开发的最佳实践的内容,新增...

    现代JavaScript教程

    现代JavaScript教程是一个全面的学习资源,旨在帮助开发者深入理解JavaScript这一重要的编程语言。JavaScript,作为Web开发的核心技术之一,被广泛用于构建交互式的网页和应用程序。本教程覆盖了从基础概念到高级...

    深入理解JavaScript系列(.chm)

    深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 深入理解JavaScript系列(2):揭秘命名函数表达式 深入理解JavaScript系列(3):全面解析Module模式 深入理解JavaScript系列(4):立即调用...

    Javascript代码美化工具

    JS Beauty 是一款 Javascript 美化工具,它具有美化,净化,压缩和解压缩 Javascript 代码等功能: 1. 美化:将混乱的 Javascript 代码格式化为优美的带缩进的格式,适合阅读和修改。 2. 净化:去掉 Javascript ...

    javascript

    JavaScript是一种广泛用于网页开发的脚本语言,它是实现Web前端交互功能的核心技术之一。在给定的文件内容中,我们可以提取到关于JavaScript的多个知识点: 1. JavaScript的变量类型:JavaScript中主要有几种基本的...

    JavaScript征途

    《JavaScript征途》是一本学习JavaScript语言的权威书籍,在遵循语言学习的特殊规律基础上精心选材,力争做到统筹、有序,在结构上体现系统性和完整性。同时还重点挖掘JavaScript基于对象的开发精髓及函数式编程两个...

    JavaScript犀牛书电子版

    在JavaScript的世界里,你需要掌握以下关键知识点: 1. **基础语法**:JavaScript是一种基于原型的动态类型语言,这意味着变量在声明时不需要指定数据类型,而是根据赋值自动确定。了解变量、常量、数据类型(如...

    JavaScript基础教程(pdf版)

    本教程旨在为初学者提供一个全面的JavaScript基础知识学习平台,帮助理解并掌握这种强大的脚本语言。 《JavaScript基础教程》首先会介绍JavaScript的历史背景和基本语法,包括变量、数据类型、操作符、流程控制...

Global site tag (gtag.js) - Google Analytics