【读书笔记】读《编写可维护的JavaScript》 - 编程实践(第二部分)
本书的第二个部分总结了有关编程实践相关的内容,每一个章节都非常不错,捡取了其中5个章节的内容。对大家组织高维护性的代码具有辅导作用。
5个章节如下——
一.UI层的松耦合
二.避免使用全局变量
三.事件处理
四.将配置数据从代码中分离出来
五.抛出自定义错误
一、UI层的松耦合
如果两个组件耦合太紧,则说明一个组件和另一个组件直接相关,这样的话,如果修改一个组件的逻辑,那么另外一个组件的逻辑也需要修改。比如,有一个名为error的CSS类名,它是贯穿整个站点的,它被嵌入到HTML之中。如果有一天你觉得error的取名并不合适,想将它改为warning,你不仅需要修改CSS还要修改用到这个calssName的HTML。HTML和CSS紧耦合在一起。
当你能够做到修改一个组件而不需要更改其他的组件的时候,你就做到了松耦合。当一个大系统的每个组件的内容有了限制,就做到了松耦合。本质上讲,每个组件需要保持足够瘦身来确保松耦合。组件知道的越少,就越有利于形成整个系统。有一点要注意:在一起工作的组件无法达到“无耦合”。在所有系统中,组件之间总要共享一些信息来完成各自的工作。这很好理解,我们的目标是确保对一个组件的修改不会经常性地影响其他部分。
下面我用一个图来来简要解释HTML/CSS/JavaScript三者之间如何做到松耦合——
二、避免使用全局变量
全局变量所带来的危害在这里就不说了,直接说说如何避免使用全局变量。
1.单全局变量
“单全局变量”的意思是所创建的这个唯一全局对象名是独一无二的(不会和内置的API产生冲突),并将你所有的功能代码都挂载到这个全局对象上。因此每个可能的全局变量都成为你唯一全局对象的属性,从而不会创建多个全局变量。
1> YUI定义了唯一一个YUI全局对象
2> jQuery定义了两个全局对象,$和jQuery。只有在$被其他的类库使用了的情况下,为了避免冲突,应当使用jQuery
3> Dojo定义了一个dojo全局对象
4> Closure类库定义了一个goog全局对象
2.模块
如YUI模块及AMD模块(例子略)
3.零全局变量
有两种情况会使用这种情形:
1> 所有所需的脚本都会合并到一个文件
2> 代码短小且不提供任何接口
/*
* 这段立即执行的代码传入了window对象,因此这段代码不需要直接引用任何全局变量。
* 在这个函数内部,变量doc是指向document对象的引用,只要是函数代码中没有直接修改window或doc且所有变量都是用var关键字定义,
* 这段脚本则可以注入到页面中而不会产生任何全局变量。
*/
(function(win) { var doc = win.document; // 在这里定义其他的变量 // 其他相关代码 })(window);
三、事件处理
我们习惯了使用最直接的方式去定义事件,如
addListener(element, 'click', function() {
var popup = document.getElementBuId('popup');
popup.style.left = event.clientX + 'px';
popup.style.top = event.clientY + 'px';
popup.className = 'reveal';
});
规则一、隔离应用逻辑
我们将上面的代码进行分解,首先隔离应用逻辑,将应用逻辑从所有事件处理程序中抽离出来的做法,所带来的好处:
1.提高应用逻辑代码复用性,说不定什么时候其他地方就会触发同一段逻辑。
2.测试时需要直接触发功能代码,而不必通过模拟对元素的点击来触发。如果将应用逻辑放于事件处理程序中,唯一的测试方法是制造事件的触发。
// 拆分应用逻辑
var myApp = { handleClick: function(event) {
this.showPopup(event);
}, /*
分离了应用逻辑之后,对这段功能代码的调用可以在多点发生,则不需要一定依赖于某个特定事件的触发。
*/
showPopup: function(event) {
var popup = document.getElementBuId('popup');
popup.style.left = event.clientX + 'px';
popup.style.top = event.clientY + 'px';
popup.className = 'reveal';
} }; addListener(element, 'click', function(event) {
myApp.handleClick(event);
});
规则二、不要分发事件对象
上述代码存在一个问题,即event对象被无节制地分发。它从匿名的事件处理函数传入了myApp.handleClick(),然后又传入了myApp.showPopup()。event对象上包含很多和事件相关的额外信息,而这段代码只用到了其中的两个而已。应用逻辑不应当依赖于event对象来正确完成功能,原因如下:
1.方法接口没有标明那些数据是必要的。好的API一定是对于期望和依赖都是透明的。将event对象作为参数并不能告诉你event的那些属性是有用的,用来干什么。
2.因此,如果你想测试这个方法,你必须重新创造一个event对象并将它作为参数传入。最佳方法是让事件处理程序使用event对象来处理事件,然后拿到所有需要的数据传给业务逻辑。例如myApp.showPopup()方法只需要两个数据,x坐标和y坐标。
var myApp = { handleClick: function(event) {
this.showPopup(event.clientX, event.clientY);
}, /*
分离了应用逻辑之后,对这段功能代码的调用可以在多点发生,则不需要一定依赖于某个特定事件的触发。
*/
showPopup: function(x, y) {
var popup = document.getElementBuId('popup');
popup.style.left = x + 'px';
popup.style.top = y + 'px';
popup.className = 'reveal';
} }; addListener(element, 'click', function(event) {
myApp.handleClick(event);
});
这样就很清晰地看到myApp.showPopup()所期望的传入的参数,并且在测试或代码的任意位置都可以很轻易地直接调用这段逻辑。如,myApp.showPopup(10, 10);
当处理事件时,最好让事件处理程序成为接触到event对象的唯一的函数。事件处理程序应当在进入应用逻辑之前针对event对象执行任何必要的操作,包括阻止默认事件或阻止事件冒泡,都应当直接包含在事件处理程序中。这样很清晰地展现了事件处理程序和应用逻辑之间的分工。
var myApp = { handleClick: function(event) { // 假设事件支持DOM Level2
event.preventDefault();
event.stopPropagetion(); // 传入应用逻辑
this.showPopup(event.clientX, event.clientY);
}, /*
分离了应用逻辑之后,对这段功能代码的调用可以在多点发生,则不需要一定依赖于某个特定事件的触发。
*/
showPopup: function(x, y) {
var popup = document.getElementBuId('popup');
popup.style.left = x + 'px';
popup.style.top = y + 'px';
popup.className = 'reveal';
} }; addListener(element, 'click', function(event) {
myApp.handleClick(event);
});
四、将配置数据从代码中分离出来
配置数据,如
1.URL
2.需要展现给用户的字符串
3.重复的值
4.设置(比如每页的配置项)
5.任何可能发生变更的值
我们时刻要记住,配置数据是可以发生变更的,而且你不希望因为有人突然想修改页面中展示的信息,而导致你去修改JavaScript源码。将配置数据抽离出来意味着任何人都可以修改它们,而不会导致应用逻辑的错误。同样,我们可以将整个config对象放到单独的文件中,这样对配置数据的修改可以完全和使用这些数据的代码隔离开来。
var config = {
URL: {
save: 'module/save',
remove: 'module/remove',
update: 'module/update',
list: 'module/list'
},
MESSAGE: {
empty: '输入内容不可为空',
success: '编辑成功',
error: '服务器错误,请稍后再试'
},
PAGE: {
startIndex: 0, // 开始索引
limit: 20, // 每页显示X条数据
currentPageNum: 1 // 默认当前页
}
};
五、抛出自定义错误
1.抛出错误
针对所有浏览器,唯一不出差错的显示自定义的错误消息的方式就是用一个Error对象。
2.抛出错误的好处
抛出错误陈述发生的问题,能够马上去调试。就像给自己留下告诉自己为什么失败的便签。
function getDivs(element) {
if (element && element.getElementByTagName) {
return element.getElementByTagName('div');
} else {
// 推荐总是在错误消息中包含函数名称,以及函数失败的原因
throw new Error('getDivs(): Argument must be a DOM element');
}
}
3.何时抛出错误
如果一个函数只被已知的实体调用,错误检查很可能没有必要(这个案例是私有函数);如果不能提前确定函数会被调用的所有地方,你很可能需要一些错误检查。这就更有可能从抛出自己的错误中获益。
本书中提供了一些抛出错误很好的经验法则:
1> 一旦修复了一个很难调式的错误,尝试增加一两个自定义错误。当再次发生错误时,这将有助于更容易解决问题。
2> 如果正在编写代码,思考一下:“我希望[某些事情]不会发生,如果发生,我的代码会一团糟糕”。这时,如果“某些事情”发生,就抛出一个错误。
3> 如果正在编写的代码别人(不知道是谁)也会使用,思考一下他们使用的方式,在特定的情况下抛出错误。
4.throw和try-catch的区分
// 使用throw
throw new Error('123'); alert(1); // 不会执行
// 使用try-catch
try {
throw new Error('123');
} catch(e) {
alert(e.message);
// 在这里可以对错误进行处理,目的是从错误中回复
} alert(1); // 会执行
// 两者结合使用
function test() {
throw new Error('123'); alert(1); // 不会会执行
} function test2() {
try {
test();
} catch(e) {
// 对test调用抛出的错误进行处理
alert(e.message); //
// 错误恢复
}
alert(2); // 会执行
} test2();
总结:本篇随笔把这本书的第二个部分——编程实践,其中的核心部分抽取出来。这样,结合上一篇随笔(【读书笔记】读《编写可维护的JavaScript》 - 编程风格(第一部分)),完成对本书前两个部分的阅读总结。对第三个部分(自动化),也是最后一个部分,就不做总结了。
很不错的书,推荐博友们看看。细细品味一下。
最后,放入这本书的logo——
【读书笔记】读《编写可维护的JavaScript》 - 编程实践(第二部分)的更多相关文章
- <读书笔记>001-以解决问题为导向的python编程实践
以解决问题为导向的python编程实践 0.第0章:计算机科学 思考:计算机科学是否为计算机编程的简称? 编程的困难点:1.同时做2件事(编程语言的语法.语义+利用其解决问题) 2.什么是好程序(解 ...
- [已读]编写可维护的javascript
13年4月份出版,作者是大名鼎鼎的Zakas,他的另两本书<javascript高级程序设计>与<高性能javascript>你一定听过或者读过. 这本书重点讲了编码风格和编码 ...
- 《编写可维护的javascript》读书笔记(中)——编程实践
上篇读书笔记系列之:<编写可维护的javascript>读书笔记(上) 上篇说的是编程风格,记录的都是最重要的点,不讲废话,写的比较简洁,而本篇将加入一些实例,因为那样比较容易说明问题. ...
- 《编写可维护的javascript》读书笔记(上)
最近在读<编写可维护的javascript>这本书,为了加深记忆,简单做个笔记,同时也让没有读过的同学有一个大概的了解. 一.编程风格 程序是写给人读的,所以一个团队的编程风格要保持一致. ...
- 编写可维护的Javascript读书笔记
写在前面:之前硬着头皮参加了java方面的编程规范培训,收货良多,工作半年有余的时候,总算感觉到一丝丝Coding之美,以及造轮子的乐趣,以至于后面开发新功能的时候,在Coding style方面花了 ...
- 《编写可维护的JavaScript》 笔记
<编写可维护的JavaScript> 笔记 我的github iSAM2016 概述 本书的一开始介绍了大量的编码规范,并且给出了最佳和错误的范例,大部分在网上的编码规范看过,就不在赘述 ...
- 拯救一切强迫症 - 读《编写可维护的 JavaScript》(一)
拯救一切强迫症 - 读<编写可维护的 JavaScript>(一) 本文写于 2020 年 4 月 24 日 我在小学的时候就有接触过编程,所以读大一的时候 C 语言还算是轻车熟路.自然会 ...
- 编写可维护的JavaScript 收纳架
如果你看过Nicolas C.Zakas写过的任何作品,你必须承认他是个不折不扣的天才.也只有天才级的才能写出<JavaScript高级程序设计>让所有的前端攻城师人手一本.Nicolas ...
- 编写可维护的JavaScript代码(部分)
平时使用的时VS来进行代码的书写,VS会自动的将代码格式化,所有写了这么久的JS代码,也没有注意到这些点.看了<编写可维护的javascript代码>之后,做了些笔记. var resul ...
随机推荐
- Tomcat开机启动设置
omcat开机启动设置 1.修改/etc/rc.d/rc.local,使用vi /etc/rc.d/rc.local 命令2.在/etc/rc.d/rc.local文件最后添加下面两行脚本 expor ...
- [51nod] 1090 3个数和为0 暴力+二分
给出一个长度为N的无序数组,数组中的元素为整数,有正有负包括0,并互不相等.从中找出所有和 = 0的3个数的组合.如果没有这样的组合,输出No Solution.如果有多个,按照3个数中最小的数从小到 ...
- “全栈2019”Java第十四章:二进制、八进制、十六进制
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- JAVA输入随笔
做题时经常遇到输入的问题,很麻烦 写一点点自己对于输入的随笔,以备后查 这里都以整数为例,其他类型的话换成相应方法就行了 1.知道一共多少行,每一行只有一个整数 这是比较简单的输入,可以用Scanne ...
- css 清楚浮动三种方法
我们可以看到这样一个布局: <style> .left{ width: 200px; height: 200px; background-color: #00ee00; float: le ...
- Python全栈-magedu-2018-笔记12
第三章 - Python 内置数据结构 字典dict key-value键值对的数据的集合 可变的.无序的.key不重复 字典dict定义 初始化 d = dict() 或者 d = {} dict( ...
- Maximum call stack size exceeded
写vue时报了如下错误 Maximum call stack size exceeded 栈溢出,因为在调用函数时使用了递归调用,而且没有写跳出条件,导致了该错误
- python之类与对象(5)
6. 类的多继承与超继承 6.1 多继承 python的类还有一个特点,就是可以继承多个类.但是我们作为测试人员,在实际中很少用到这个多继承,这里不就详细(一直都不详细,hhhh)写了.上代码: #! ...
- 【Python】Excel数据处理
1.环境准备 > python2.7 > xlrd,xlwt模块下载与安装,前者用来读取excel文件,后者用来写入excel文件 2.实战案例 案例场景: > excel1中包含某 ...
- Git命令之回退篇 git revert git reset
Git command之回退篇 欲练回退 必先了解:HEAD.index.WorkingCopy HEAD: 当前所在的分支版本顶端的别名,也就是最新的一次commit. git commit 之后与 ...