《众妙之门——JavaScript与jQuery技术精粹》——2.6 JavaScript代码复查实例

简介:

本节书摘来自异步社区《众妙之门——JavaScript与jQuery技术精粹》一书中的第2章,第2.6节,作者: 【德】Smashing Magazine译者: 吴达茄 芮鹏飞 更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.6 JavaScript代码复查实例

最近一位开发人员让我对他的代码进行复查并提供改进建议。虽然我并不是代码复查专家(不要被我上面所说的忽悠),我在这里还是给出我提出的问题和解决方案。

问题1
问题:函数和对象没经过任何类型校验就作为参数传递给其他函数。

回复:类型校验是保证输入类型的必要步骤,如果没有进行检查,可能就有输入类型(字符串、日期、数组等)不确定的风险,这些可以轻易地毁掉你未经防御处理的应用程序。对于函数,至少应该进行以下处理:

1.测试以确保传递的变量真实存在;

2.进行typeof检查以阻止执行的输入为非有效函数。
screenshot

不幸的是,简单的typeof检查是不够的,正如Angus Croll在“Fixing the typeof operator ”中指出,在对包括函数在内的许多内容进行typeof检查时需要注意大量细节。

例如,对空返回对象进行typedef检查在技术上是错误的。实际上,对于除了函数之外的任何对象类型进行typedef检查时,都会返回对象而不区分它们是数组、日期、RegEx还是什么。

可以利用Object.prototype.toString来调用JavaScript内部对象的属性,即[Class],也就是对象的类属性。不幸的是,内置对象通常会覆盖Object.prototype.toString,但是可以对它们加上通用的toString函数:

Object.prototype.toString.call([1,2,3]); //"[object Array]"

你可能也会发现下面Angus的函数是比typeof更适合的选择,对对象、数组以及其他类型调用betterTypeOf()函数来看看会发生什么。
screenshot

这里,parseInt()函数被盲目地用来解析用户输入的整数值却没有指定基,这样会引起麻烦。

在"JavaScript:the Good Parts "中,Douglas Crockford指出parseInt()函数的调用是非常危险的。尽管你知道输入字符串变量会返回整数,也应该指定一个基作为第二个变量,否则会返回意想不到的输出,考虑下面的例子:
screenshot

你会对多少开发人员忽略第二个参数感到吃惊,但实际上这经常发生。记住使用者(如果允许自由输入数值)并不一定会根据标准的数值惯例来输入(因为他们太疯狂了!)。我见过020、29319.jpg20、;29337.jpg20以及其他许多输入方式,所以尽可能为各种方式的输入值进行解析,下列使用parseInt()函数的方式偶尔会更好:
screenshot

问题2
问题:在整个代码库上重复检查是否满足特定于浏览器的条件(例如:特性监测,检查支持的ES5特性等)。

回复:理想情况下,应保持代码库尽可能的“干燥”,有一些好的解决方案可以解决这个问题。例如,可以从加载时间配置模式(也称为加载时间和初始化时间分支)中获益。基本思想是仅测试条件一次(加载应用时)然后在后续检查中来调用这个结果。这种模式在JavaScript库文件中很常见,这些JavaScript库文件在加载时会自我配置,以针对具体浏览器进行优化。

这种模式可以这样实现:
screenshot

下面的例子演示了如何规范化得到XMLHttpRequest对象。
screenshot

有一个很著名的例子,Stoyan Stefanov运用这个来添加和删除跨浏览器的事件监听器,在他的《JavaScript Patterns》一书中有介绍。
screenshot

问题3
问题:定期扩展本机Object.prototype。

回复:扩展本机类型经常会出问题,很少有(如果有的话)著名的代码库敢于扩展Object.prototype类型。事实是并没有一定要扩展它的情况存在。除非是要破坏JavaScript代码中的对象散列表及增加命名冲突可能性,这种扩展的操作一般被认为是糟糕的,这种操作应该是最后选择项(这同扩展自定义对象属性大有不同)。

如果因为某种原因你需要结束扩展对象原型,确保该方法已经不存在并拟出文件使小组中其他成员知道为什么需要这样做,你可以使用以下代码作为指导:
screenshot

Juriy Zaytsev 有一篇关于“扩展本机和主机对象 ”的非常著名的帖子,可能你会感兴趣。

问题4
问题:有些代码严重阻塞页面,因为它在进行任何进一步操作之前都要等待进程完成或数据加载。

回复:页面阻塞导致用户使用体验差,有很多不损坏应用的解决方法。

一个解决方法是使用“延迟执行”(通过“许诺”和“将来”的概念)。“许诺”的基本思想是与其让某些调用占用资源,不如直接返回一个“将来”会实现的“许诺”。这样将允许编写可异步运行的非阻塞逻辑。常见的做法是在方程中引入一个调用,当请求完成时执行。

我曾经和Julian Aubourg写过一篇全面介绍这种方法的帖子,如果你对通过jQuery实现它感兴趣可以看看这篇帖子。当然也可以利用JavaScript实现。

微框架Q 提供了一个一般性的JS-兼容的“许诺”、“将来”实现方案,它相对而言比较全面,具体如下:
screenshot

如果你想找一些更基础的可通读程序,这里是Douglas Crockford关于“许诺”的实现方法:
screenshot
screenshot

问题5
问题:通常使用“= =”操作符测试某一属性的显式数值等式,但应该使用的是“= = =”操作符。

回复:正如你可能知道也可能不知道的,“= =”操作符在JavaScript中的使用非常自由,即使两个量的值是完全不同的类型也会认为它们相等。这是因为该操作符会优先进行强制类型转换而不是比较,“= = =”却是在两个类型不一样的情况下不会进行强制类型转换,因而会报错。

我之所以在特定类型比较(本例)时更多地推荐使用“= = =”操作符,是因为“= =”操作符有许多陷阱并被许多开发人员认为是不可靠的。

你可能想知道在抽象化的语言(如CoffeeScript)中,由于其不可靠性,“= =”操作符的使用率相对“= = =”完全处于下风。

与其听我片面之言,不如看看下面运用“= =”进行布尔相等性检查的例子,该例子运行会产生无法预期的结果。
screenshot

上面列表中许多结果等于true,因为JavaScript是一种弱类型化的语言:它尽量多地使用强制类型转换。如果你对上述表达式等于true的原因感兴趣,可以参阅《Annotated ES5指导》,其中的解释更为精彩。

回到复查上面来,如果100%确信进行比较的量不会被用户干扰,可以谨慎地使用“= =”操作符。一定记住,如果有非预期的输入,使用“= = =”操作符会更好。

问题6
问题:非缓存的数组长度被用于所有的for循环中是非常糟糕的,因为你在利用它遍历整个元素集合。

这里有个例子:
screenshot

回复:这种方法(我依然看到许多开发人员在使用)的问题在于该数组长度在每个循环的迭代中被不必要的重复访问。这会导致程序运行非常慢,尤其是用在HTMLCollection上时(在这种情况下,正如Nicholas C. Zakas在《 High-Performance JavaScript》一书中提到的,对长度进行缓存可以比反复访问它快上190倍)。以下是对数组长度进行缓存的一些方法。

screenshot

如果你想研究哪种方法表现最佳的话,使用jsPerf 对循环内外的数组捕捉、前缀增量使用、倒计时等进行测试以比较其性能优劣也是可行的。

问题7
问题:jQuery的$.each()函数用于遍历对象和数组,然而在某些情况下则使用for。

回复:在jQuery中,有两种方法可以无缝地遍历对象和数组。通用的$.each 可以遍历这两种类型,$.fn.each() 函数专门用于遍历jQuery对象(其中标准对象利用$()函数封装,你应该更倾向于使用后者)。低级别的$.each()函数执行效果比$.fn.each()函数好,标准的JavaScript for和while循环比这两个都要好,这是经jsPerf测试验证的。以下是一些运行情况也不错的循环:
screenshot

你可能会发现,Angus Croll的帖子"Rethinking JavaScript for Loops"是对这些建议的一个有趣的延伸。

考虑一个以数据为中心的应用程序,每一个对象或数组都包含大量数据,你应该考虑进行重构来使用以上方法。从拓展性角度说,你应该尽可能地剔除浪费的毫秒数,因为当页面上有数以千计的元素时,时间会累积到很大。

问题8
问题:JSON字符串在内存中以字符串级联的方式建立。

回复:可以通过更优的方式来实现。例如,为什么不使用可以接收JavaScript对象并返回与JSON格式等效的JSON.stringify()函数呢?对象可以按照需要尽可能的复杂或者深度嵌套,这样将会产生更加简单、有效的解决方法。
screenshot
screenshot

额外的调试小技巧,如果你想要使得终端控制台显示的JSON更为美观可读,可以使用stringify()函数的以下额外参数实现:
screenshot

问题9
问题:使用的命名空间模式在技术上是无效的。

回复:应用程序中其他部分使用的命名空间是正确的,而对其存在性的检查是无效的,现有:
screenshot

问题在于 !MyNamespace会报错:ReferenceError。因为MyNamespace变量之前未经声明。较好的模式是利用内部变量声明布尔类型的强制转换,如下:
screenshot

当然,可以通过其他许多方法来实现。如果你想阅读更多关于命名空间模式的内容(以及一些命名空间拓展的思路),可以参阅我最近写的"Essential JavaScript Namespacing Patterns"一文,Juriy Zaytsev也写过一篇关于命名空间模式非常全面的文章。

相关文章
|
2天前
|
资源调度 前端开发 JavaScript
Babel:JavaScript代码的编译利器
Babel:JavaScript代码的编译利器
|
2天前
|
JavaScript 前端开发 安全
抽象语法树(AST):理解JavaScript代码的抽象语法树
抽象语法树(AST):理解JavaScript代码的抽象语法树
|
2天前
|
JavaScript 前端开发 索引
jQuery学习教程,写更少的代码,做更多的事情(二)
jQuery学习教程,写更少的代码,做更多的事情(二)
|
2天前
|
XML 前端开发 JavaScript
jQuery与javascript的区别+案例 锋芒毕露
jQuery与javascript的区别+案例 锋芒毕露
|
2天前
|
JavaScript 索引
jQuery 实现 图片框切换【与原生 JS 对比】
jQuery 实现 图片框切换【与原生 JS 对比】
|
2天前
|
JavaScript
js中如何使用工厂方式和构造函数创建对象,web开发项目实例
js中如何使用工厂方式和构造函数创建对象,web开发项目实例
|
2天前
|
存储 JavaScript 前端开发
掌握值类型和引用类型,让你的JavaScript代码更上一层楼!
掌握值类型和引用类型,让你的JavaScript代码更上一层楼!
|
3天前
|
Web App开发 JavaScript 前端开发
解决Vue.js Devtools未检测到Vue实例的问题
解决Vue.js Devtools未检测到Vue实例的问题
|
3天前
|
存储 移动开发 JavaScript
jQuery 根据 css 类筛选 DOM 元素的代码
jQuery 根据 css 类筛选 DOM 元素的代码
17 1
|
4天前
|
JavaScript 前端开发 开发者
跟踪最新的JavaScript游戏开发技术趋势需要多方面的努力和参与
【5月更文挑战第14天】跟踪JavaScript游戏开发趋势:访问专业网站和博客(如Medium, GameDev.net),参加JSConf和GDC会议,订阅技术期刊,关注开源项目(如Phaser, Three.js),利用社交媒体(Twitter, Reddit)和在线论坛(Stack Overflow),学习新技术(如WebGL, WebAssembly)。通过这些方式保持对行业动态的敏锐度。
11 4