Tuesday, January 26, 2010

TCP握手协议

更多精彩请到 http://www.139ya.com

TCP握手协议

在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据,在上述过程中,还有一些重要的概念:

未连接队列:在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(syn=j)开设一个条目,该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于Syn_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。
Backlog参数:表示未连接队列的最大容纳数目。

SYN-ACK 重传次数 服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同。

半连接存活时间:是指半连接队列的条目存活的最长时间,也即服务从收到SYN包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和。有时我们也称半连接存活时间为Timeout时间、SYN_RECV存活时间。

Monday, January 11, 2010

linux vi(vim)常用命令汇总

更多精彩请到 http://www.139ya.com


首先介绍一下 vim vi 是 unix/linux 下极为普遍的一种文本编辑器, 大部分机器上都有 vi 有各种变种, 在不同的机器上常用不同的变种软件 其中 vim 比较好用也用得比较广泛 vim 是 Vi IMproved 的缩写, 表示更好的 vi 本文就说些其中比较有用, 比较常用的命令, 若能熟练运用这些命令, 那么会发现编辑文件很大帮助,尤其是做开发的人员,能快速的提高开发效率

说明:
以下的例子中 xxx 表示在命令模式下输入 xxx 并回车
以下的例子中 :xxx 表示在扩展模式下输入 xxx 并回车
小括号中的命令表示相关命令
在编辑模式或可视模式下输入的命令会另外注明

1 查找

/xxx(?xxx) 表示在整篇文档中搜索匹配xxx的字符串, / 表示向下查找, ? 表示
向上查找其中xxx可以是正规表达式,关于正规式就不多说了
一般来说是区分大小写的, 要想不区分大小写, 那得先输入
:set ignorecase
查找到以后, 再输入 n 查找下一个匹配处, 输入 N 反方向查找

*(#) 当光标停留在某个单词上时, 输入这条命令表示查找与该单词匹配的
下(上)一个单词 同样, 再输入 n 查找下一个匹配处, 输入 N 反方
向查找

g*(g#) 此命令与上条命令相似, 只不过它不完全匹配光标所在处的单词, 而
是匹配包含该单词的所有字符串

gd 本命令查找与光标所在单词相匹配的单词, 并将光标停留在文档的非
注释段中第一次出现这个单词的地方

% 本命令查找与光标所在处相匹配的反括号, 包括 () [] { }

f(F)x 本命令表示在光标所在行进行查找, 查找光标右(左)方第一个x字符
找到后:
输入 ; 表示继续往下找
输入 , 表示反方向查找

2 快速移动光标
在 vi 中, 移动光标和编辑是两件事, 正因为区分开来, 所以可以很方便的进行光标定
位和编辑 因此能更快一点移动光标是很有用的

w(e) 移动光标到下一个单词
b 移动光标到上一个单词

0 移动光标到本行最开头
^ 移动光标到本行最开头的字符处
$ 移动光标到本行结尾处

H 移动光标到屏幕的首行
M 移动光标到屏幕的中间一行
L 移动光标到屏幕的尾行
gg 移动光标到文档首行
G 移动光标到文档尾行
c-f (即 ctrl 键与 f 键一同按下) 本命令即 page down
c-b (即 ctrl 键与 b 键一同按下, 后同) 本命令即 page up

'' 此命令相当有用, 它移动光标到上一个标记处, 比如用 gd, * 等查
找到某个单词后, 再输入此命令则回到上次停留的位置

' 此命令相当好使, 它移动光标到上一次的修改行

` 此命令相当强大, 它移动光标到上一次的修改点

3 拷贝, 删除与粘贴
在 vi 中 y 表示拷贝, d 表示删除, p 表示粘贴 其中拷贝与删除是与光标移动命令
结合的, 看几个例子就能够明白了

yw 表示拷贝从当前光标到光标所在单词结尾的内容
dw 表示删除从当前光标到光标所在单词结尾的内容
y0 表示拷贝从当前光标到光标所在行首的内容
d0 表示删除从当前光标到光标所在行首的内容
y$ 表示拷贝从当前光标到光标所在行尾的内容
d$ 表示删除从当前光标到光标所在行尾的内容
yfa 表示拷贝从当前光标到光标后面的第一个a字符之间的内容
dfa 表示删除从当前光标到光标后面的第一个a字符之间的内容

特殊地:
yy 表示拷贝光标所在行
dd 表示删除光标所在行
D 表示删除从当前光标到光标所在行尾的内容

关于拷贝, 删除和粘贴的复杂用法与寄存器有关, 可以自行查询

4 数字与命令
在 vi 中数字与命令结合往往表示重复进行此命令, 若在扩展模式的开头出现则表示行
号定位 如:

5fx 表示查找光标后第5个x字符

5w(e) 移动光标到下五个单词

5yy 表示拷贝光标以下 5 行
5dd 表示删除光标以下 5 行

y2fa 表示拷贝从当前光标到光标后面的第二个a字符之间的内容

:12,24y 表示拷贝第12行到第24行之间的内容
:12,y 表示拷贝第12行到光标所在行之间的内容
:,24y 表示拷贝光标所在行到第24行之间的内容 删除类似

5 快速输入字符
在 vi 中, 不要求你输入每一个字符, 可以有很多种方法快速输入一些字符
使用 linux/unix 的同学一定有一个经验, 在命令行下输入命令时敲入头几个字符再按
TAB 系统就会自动将剩下的字符补齐, 假如有多个匹配则会打印出来 这就是著名的命令
补齐(其实windows中也有文件名补齐功能) vi 中有许多的字符串补齐命令, 非常方便

c-p(c-n) 在编辑模式中, 输入几个字符后再输入此命令则 vi 开始向上(下)搜
索开头与其匹配的单词并补齐, 不断输入此命令则循环查找 此命令
会在所有在这个 vim 程序中打开的文件中进行匹配

c-x-l 在编辑模式中, 此命令快速补齐整行内容, 但是仅在本窗口中出现的
文档中进行匹配

c-x-f 在编辑模式中, 这个命令表示补齐文件名 如输入:
/usr/local/tom 后再输入此命令则它会自动匹配出:
/usr/local/tomcat/

abbr 即缩写 这是一个宏操作, 可以在编辑模式中用一个缩写代替另一个
字符串 比如编写java文件的常常输入 Systemoutprintln, 这很
是麻烦, 所以应该用缩写来减少敲字 可以这么做:
:abbr sprt Systemoutprintln
以后在输入sprt后再输入其他非字母符号, 它就会自动扩展为System
outprintln

6 替换
替换是 vi 的强项, 因为可以用正规表达式来匹配字符串以下提供几个例子

:s/aa/bb/g 将光标所在行出现的所有包含 aa 的字符串中的 aa 替换为 bb
:s/\/bb/g 将光标所在行出现的所有 aa 替换为 bb, 仅替换 aa 这个单词
:%s/aa/bb/g 将文档中出现的所有包含 aa 的字符串中的 aa 替换为 bb
:12,23s/aa/bb/g 将从12行到23行中出现的所有包含 aa 的字符串中的 aa 替换为 bb
:12,23s/^/#/ 将从12行到23行的行首加入 # 字符
:%s= *$== 将所有行尾多余的空格删除
:g/^\s*$/d 将所有不包含字符(空格也不包含)的空行删除

Monday, January 4, 2010

Strlcpy 和 strlcat—— 一致的、安全的字符串拷贝和串接函数

更多精彩请到 http://www.139ya.com

英文原文: http://www.gratisoft.us/todd/papers/strlcpy.html

英文作者: Todd C. Miller, Theo de Raadt

译者:林海枫

译本地址:http://blog.csdn.net/linyt/archive/2009/07/27/4383328.aspx


Strlcpy strlcat—— 一致的、安全的字符串拷贝和串接函数

Todd C. Miller

University of Colorado, Boulder

Theo de Raadt

OpenBSD project

概述

随着流行的缓冲区溢出攻击的增加,越来越多程序员开始使用带有大小,即有长度限制的字符串函数,如 strncpy() strncat() 。尽管这种趋势令人十分鼓舞,但通常的标准 C 字符串函数并不是专为此而设计的。本文介绍另一种直观的,一致的,天生安全的字符串拷贝 API

当函数 strncpy() strncat() 作为 strcpy() strcat() 的安全版本来使用时,仍然存在一些安全隐患。首先,这两函数以不同的,非直观的方式来处理 NUL 结束符和长度参数,即使有经验的程序员也会混淆。其次,发生字符串截断时,也不容易检查。最后, strncpy() 函数使用 0 来填充剩余的目标字符串空间,以招致性能下降。在所有这些问题之中,由长度参数引起的混淆以及与 NUL 结束符相关的问题最严重。在审核 OpenBSD 源代码树的潜在安全漏洞时,我们发现 strncpy() strncat() 猖獗误用的情况。尽管并非所有的误用都会导致可被利用的安全漏洞,但清楚地表明使用 strncpy() strncat() 来实施安全的字符串操作这一准则已普遍受到误解。两个替代函数 strlcpy() strlcat() 被提议通过提出一个字符串拷贝安全的 API 来解决这些问题(参阅图 1 函数原型)。这两函数保证产生包含 NUL 的字符串,以长度即字符串按占用字节的数量作为入口参数,并且提供简便的方式来检查是否有字符串截断。两者均不会清零未使用的目标空间。

引言

1996 年年中,笔者和 OpenBSD 项目的其它成员一起担任审核 OpenBSD 源代码树的工作,以寻找安全问题,并强调缓冲区溢出问题。缓冲区溢出问题 [1] 最近在论坛上如 BugTraq [2] 获得广泛的关注,并且也被广泛利用。我们发现大量的溢出是由于使用 sprintf() strcpy() strcat() 而造成无长度界限的字符串拷贝,在循环里操纵字符串时没有显式检查字符串长度也是元凶之一。除此之外,我们也发现在很多场合下,程序员已使用 strncpy() strncat() 进行安全的字符串操纵,但未能领会这些 API 的精妙之处。

因此在审核代码时,我们发现不仅有必要去检查是否使用不安全的函数,如 strcpy() strcat() ,同时也要检查是是否有函数 strncpy() strcat() 的不正确使用。检查是否正确使用并非总是显而易见,特别是使用“静态”变量或使用由 calloc() 分配的缓冲区时,这些缓冲区总是预先就填满了 NUL 结束符。我们得到一个结论:需要十分安全的函数来替代 strncpy() strncat() ,从根本上简化程序员的工作,同时也使代码审核变得更容易。

size_t strlcpy(char *dst, const char *src, size_t size);
size_t strlcat(char *dst, const char *src, size_t size);

1 strlcpy() strlcat() ANSI C 原型

普遍的误解

最普遍的误解莫过于认为函数 strncpy() 总是产生以 NUL 结束的目标字符串。然而只有当源字符串的长度小于 size 参数时,这一论断才为真。当拷贝任意长的用户输入到固定大小的缓冲区,问题就出现了。这种情况下,使用 strncpy() 最安全的方法是先将目标字符串的大小减 1 ,再传递给 strncpy size 参数,然后手工给目标字符串加上 NUL 结束符。这样可以保证目标字符串总是以 NUL 结尾的。严格地说,如果字符串是“静态”变量或者由 calloc() 分配的变量,完全没有必要手工给字符串加上 NUL 结束符。因为这些字符串在分配时已经清零了。然而,依赖这一特性通常会给后来维护代码的人造成混乱。

另一个误解认为把代码中的 strcpy() strcat() 换成 strncpy() strncat() 所引起的性能下降微不足道。对于 strncat() 来说, 确实如此 。但对于 strncpy() 来说则不是这样,因为它会把那些未用来存储字符串的字节清零。当目标字符串的大小远远大于源字符的长度时,这会导致为数不少 [**] 的性能下降。 Strncpy() 的行为因 CPU 架构和它的实现而异,因此它所带来的性能下降也因它的行为而不同。

使用 strncat() 最普遍的错误是使用不正确的 size 参数。确实要保证 strncat() 使目标字符串包含 NULL 结束符,参数 size 决不能把 NULL 字符的空间计算在内。最重要的是,参数 size 不是目标字符串本身的大小,而是为字符串预留的空间的数量。由于参数 size 几乎总一个计算量,而非一个已知的常量,因此经常被错误地计算。

Strlcpy() strlcat() 是如何简化编程的?

Strlcpy() strlcat() 函数提供一个一致的,绝无 义的 API ,帮助程序员编写更安全的防弹代码。首先,同时也是最重的, strlcpy() strlcat() 两者保证所有的目标字符串都以 NUL 字符结尾,只要提供的 size 参数为非零。其次,两个函数都把 size 参数作为整个目标字符的大小。大多情况下,它的值很容易在编译时通过使用 sizeof 运算符来计算。最后, strlcpy() strlcat() 均不给目标字符串清零未使用的字节(而是使用 NUL 来表示字符串的结束)。

Strlcpy() strlcat() 函数返回他们尝试创建的字符串的长度。对于 strlcpy() 来说,就是源字符串的长度;而对 strlcat() 来说,就是目标字符串的长度(串接前的长度)加上源字符串的长度。对于检查是否发生字符截断,程序员只需要验证回返值是否不小于 size 参数。因此,就算发生截断,存储整个字符串所需的字节数现已知道,程序员可以分配一个更大的空间,接着重新拷贝字符串(如果需要的话)。返回值在语义上与 snprintf() 的返回值类似, snprintf() BSD 实现并由即将来临的 C9X 标准规范化(请注意,非并当前所有的 snprintf 实现都遵循 C9X )。如果没有发生截断,程序员现在也获知了结果字符串的长度。由于通常的实践是使用 strncpy() strncat() 来构建字符串,然后使用 strlen() 来获得结果字符串的长度,因此( strlcpy() strlcat() )这一返回值语义非常有用。有了 strlcpy() strlcat() 后,就不再需要最后一步的 strlen() 来获得字符串的长度了。

示例 1a 是有潜在缓冲区溢出的代码段( HOME 环境变量由用户所控制,可为任意长)。


strcpy(path, homedir);
strcat(path, "/");
strcat(path, ".foorc");
len = strlen(path);

示例 1a: 使用 strcpy() strcat() 的代码段

示例 1b 是同样功能的代码段,不过换成了 安全 地使用 strncpy() strncat()( 请注意我们不得已手工给目标字符串设置 NUL 字符 )

strncpy(path, homedir,sizeof(path) - 1);
path[sizeof(path) - 1] = '\ 0';
strncat(path, "/",sizeof(path) - strlen(path) - 1);
strncat(path, ".foorc",sizeof(path) - strlen(path) - 1);
len = strlen(path);

示例 1b: 转换成使用 strncpy() strncat()

示例 1c 是使用 strlcpy()/strlcat()API 平凡 版本。它的优点是与示例 1a 一样简洁,但不需要利用新 API 的返回值。

strlcpy(path, homedir, sizeof(path));
strlcat(path, "/", sizeof(path));
strlcat(path, ".foorc", sizeof(path));
len = strlen(path);

示例 1c: 使用 strlcpy()/strlcat() 的平凡版本

由于示例 1c 是如此的容易阅读和理解,故对它添加额外的检查显得格外简单。示例 1d 里检查返回值以确定是否有足够的空间来储存源字符串。如果没有足够空间,返回一个错误。虽然程序比以前有轻微的复杂,但更具鲁棒性,同时避免最后一步的 strlen() 调用。


len = strlcpy(path, homedir,sizeof(path));
if (len >= sizeof(path))
return (ENAMETOOLONG);
len = strlcat(path, "/",sizeof(path));
if (len >= sizeof(path))
return (ENAMETOOLONG);
len = strlcat(path, ".foorc",sizeof(path));
if (len >= sizeof(path))
return (ENAMETOOLONG);


示列 1d 检测是否截断

设计决策

在考虑 strlcpy() strlcat() 应具有什么语义的时候,涌现出各种各样的想法。原先的想法是使 strlcpy() strlcat() 的语义和 strncpy() strncat() 的相同,唯一例外是 他们总是确保目标字符串以 NUL 结尾。然而,回顾 strncat() 的普遍使用情况(和误用),我们深信 strlcat() size 参数应该是整个字符串空间的大小,而不仅是剩下来未分配的字符数。起决定初返回值为拷贝字符的数目,???。很快我们决定返回值和 snprintf() 的具有相同的语义是这一个更好的选择,因为这样给予程序员最大的弹性去做截断检查和截断恢复。

性能

程序员现已开始避免使用 strncpy() 函数,原因是当目标缓冲区远远大于源字符串的长度时,该函数的性能欠佳。例如 apache 开发小组 [6] 以调用内部函数来取代 strncpy() ,并公布了性能上的提升 [7] 。同样地, ncurses [8] 软件包最近删除了所有的 strncpy() 函数调用,结果 tic 工具的运行速度提高了四倍。我们谨希望,将来更多的程序员使用 strlcpy() 提供的接口,而非使用经定制的接口。

为获得在最糟糕情况下, strncpy() strlcpy() 差别的感性认识,我们运行一个测试程序,拷贝字符串“ this is just a test”1000 次到大小为 1024 字节的缓冲区。这对于 strncpy() 来说有点不公平,由于使用较短的字符串和较大的缓冲区, strncpy() 必须为缓冲区大部分空间填充上 NUL 字符。然而在实践中,使用的缓冲区通常远远大于用户预期的输入。例如,路径名缓冲区的长度为 MAXPATHLEN(1024 字节 ) ,但大多数文件名远远小于这一长度。表 1 中的平均运行时间是在使用 25Mhz 68040CPU 的机器 HP9000/425t OpenBSD 2.5 操作系统下和使用 166Mhz alpha CPU 的机器 DEC AXPPCI166 OpenBSD 2.5 操作系统下产生的结果。各种情况使用相同的 C 函数版本,时间为 time 工具报告结果的“ real time” 部分。

CPU 架构

函数

时间 (秒)

M 68k

Strcpy

0.137

M 68k

Strncpy

0.464

M 68k

Strlcpy

0.14

A lpha

Strcpy

0.018

A lpha

Strncpy

0.10

A lpha

Strlcpy

0.02

Table 1 : Performance timings in seconds

1 :性能测时结果(秒)

从表 1 可以看到, strncpy() 的计时结果远差于 strncpy() strlcpy() 的结果。这可能不仅仅是因为填补 NUL 字符带来的开销,而且是因为 CPU 的数据缓存被长长的零串有效地刷新。

Strlcpy() strlcat() 所不能及之处

尽管 strlcpy() strlcat() 善长于处理大小固定的缓冲区,但仍然不能完全取代 strncpy() strncat() 。在某些情况下,必须操纵那些并非真正 C 字符串的缓冲区(例如 struct utmp 中的字符串)。然而,我们认为这些“伪字符串”不应该使用在新的代码中,因为它们容易被误用,并且从我们的经验来说,这是 bug 的普遍源头。此外, strlcpy() strlcat() 函数并不尝试“修复” C 中的字符串处理。相反它们设计的初衷就是适合 C 字符的标准架构。如果要使用支持动态分配,任意大小缓冲区的字符串函数,可以使用 mib 软件 [9] 里的” astring” 包。

谁应该使用 strlcpy() strlcat()?

Strlcpy() strlcat() 函数首先出现在 OpenBSD 2.4 中。最近两函数被同意纳入 Solaris 的新版中。第三方包也开始使用这一 API 。例如, rsync [5] 软件包现在使用 strlcpy() ,如果 OS 不支持该函数则提供自己的版本。我们希望其它操作系统和应用程序以后会使用 strlcpy() strlcat() ,而且希望经过若干时间会得到标准的接受。

下一步将是什么?

OpenBSD 项目中,我们计划使用 strlcpy() strlcat() 替换每个 strncpy() strncat() ,这是明智之举。即使 OpenBSD 中使用新 API 来编写新的代码,仍然有大量的代码在我们原先的安全审核过程中转换成 strncpy() strncat() 。至今,我们继续在现有代码中发现由于错误使用 strncpy() strncat() 而造成的 bug 。把旧代码更改为使用 strlcpy() strlcat() ,应该能(??)一些程序提速,并且能 (?) 为一些程序揭开 bug

可从何处获得源代码?

Strlcpy() strcat() 的源代码可以免费获得,并遵循作为 OpenBSD 操作系统一部分的 BSD 协议。你同样可通过匿名 ftp ftp.openbsd.org /pub/OpenBSD/src/lib/libc/string 目录下载代码和它的手册。 strlcpy() strlcat() 的源代码分别在文件 strlcpy.c strlcat.c 中。文档(使用 tmac.doc troff 宏)可从 strlcpy.3 中找到。

作者信息

1993 年, Todd C. Miller 接管 sudo 软件包的维护工作,并从此参加免费软件社区。他作为活跃的开发者加入 OpenBSD 项目。 Todd 1997 年获得姗姗来迟的科罗拉多州大学计算机科学专业学士学位。可以使用邮件地址 Todd.Miller@cs.colorado.edu 与他联系。

Theo de Raadt 1990 年起加入免费 Unix 操作系统。他早期的开发工作包括移植 Minix sun3/50 amiga ,以及移植 PDP-11 BSD 2.9 68030 计算机。作为 NetBSD 项目的创始人之一, Theo 的工作内容为维护和改进很多系统部件,包括 sparc 端口和免费的 YP 实现,这一实现被大多数免费系统使用。 Theo 1995 年建立 OpenBSD 项目,项目集中(??)在安全,集成加密系统和代码正确性等方面。 Theo 全职工作于提升 OpenBSD 项目。可通过邮件地址 deraadt@openbsd.org 与他联系。

参考资料

[1] Aleph One. ``Smashing The Stack For Fun And Profit.'' Phrack Magazine Volume Seven, Issue Forty-Nine.

[2] BugTraq Mailing List Archives. http://www.geek-girl.com/bugtraq/. This web page contains searchable archives of the BugTraq mailing list.

[3] Brian W. Kernighan, Dennis M. Ritchie. The C Programming Language, Second Edition. Prentice Hall, PTR, 1988.

[4] International Standards Organization. ``C9X FCD, Programming languages \*- C'' http://wwwold.dkuug.dk/jtc1/sc22/open/n2794/ This web page contains the current draft of the upcoming C9X standard.

[5] Andrew Tridgell, Paul Mackerras. The rsync algorithm. http://rsync.samba.org/rsync/tech_report/. This web page contains a technical report describing the rsync program.

[6] The Apache Group. The Apache Web Server. http://www.apache.org. This web page contains information on the Apache web server.

[7] The Apache Group. New features in Apache version 1.3. http://www.apache.org/docs/new_features_1_3.html. This web page contains new features in version 1.3 of the Apache web server.

[8] The Ncurses (new curses) home page. http://www.clark.net/pub/dickey/ncurses/. This web page contains Ncurses information and distributions.

[9] Forrest J. Cavalier III. ``Libmib allocated string functions.'' http://www.mibsoftware.com/libmib/astring/. This web page contains a description and implementation of a set of string functions that dynamically allocate memory as necessary.


CVE

更多精彩请到 http://www.139ya.com

CVE® International in scope and free for public use, CVE is a dictionary of publicly known information security vulnerabilities and exposures.

http://cve.mitre.org/

dojo website

更多精彩请到 http://www.139ya.com

http://dojocampus.org/explorer/

http://dojocampus.org/

檢查 Run Time Memory Error 的工具

更多精彩请到 http://www.139ya.com

檢查 Run Time Memory Error 的工具

目 前市面上有不少專門檢查run time memory error 的工具,例如Purify、Insure++、Bounds Checker 以及Valgrind 等等;其中,Purify、Insure++與Valgrind,是筆者曾經實際使用過的產品;然而,就這三項工具的功能完備性來說,筆者主觀地認為 Purify 是小勝幾籌;再者,就使用者介面的簡易明瞭性來說,筆者也是主觀地較偏好 Purify。因此,後續的章節內容與其範例程式之偵測與說明,都將以 Purify 這項工具為主軸。

Purify 的歷史

Purify 的產品雛形,是由一位名叫 Reed Hastings 的工程師所獨力研發而成。憑藉著Purify 的產品雛形,Reed Hastings 於1991 年在美國加州創立了 Pure Software 公司;創業後的四年之間,公司的年營業額每年皆呈倍數成長,亮麗的表現使得 Pure Software 公司獲得 Morgan Stanley 的青睞,並在 Morgan Stanley 的輔導之下,順利地於1995 年在 NASDAQ 掛牌上市。1997 年,Rational Software 公司以七億五千萬美金併購了 Pure Software 公司;而Rational Software 公司則於2003 年,被IBM 以二十億美金所併購。

Pure Software 的創辦人 ── Reed Hastings,在大學時期的主修科目是數學,並曾經得到系上的 Smyth Prize。他在1983 年大學畢業之後,隨即加入由美國總統 Kennedy 所創立的和平志工隊 (Peace Corps Volunteer),到非洲的 Swaziland 教授兩年的數學課程;之後,他回到校園攻讀 Computer Science,並於1988 年取得 Stanford University 電腦科學系的碩士學位。

Purify 產品目前已被更名為 PurifyPlus,而且也推出了 Linux 平台的版本;本書在範例程式當中所使用的 Purif
yPlus,則是由 IBM 網站上所下載的 trial version。

備註:最近幾年很紅的線上租片公司 Netflix,就是由 Reed Hastings 所創辦的。



Uninitialized Memory Read (UMR)

所 謂「Uninitialized Memory Read」,是指某個變數尚未給定初始值之前(此時的變數內容值很可能是個亂數值),程式就去讀取它的內容值。除了少數的情況之外,UMR 通常不會直接造成 coredump 或 segmentation fault,但它有可能會造成不正確的運算結果,抑或間接地引發後續的 coredump 或 segmentation fault。


The Operation Beyond Array Bounds

不 論何種 data type 的 array,其 array element 的個數都是有限的。第一個 array element 的 starting address,是 array 的 lower bound;而最後一個 array element 的 ending address,則是 array 的 upper bound;因此,對 array 的任何運作,都必須侷限在 lower bound 與 upper bound 的範疇之內,才是合法有效的運作;如果存取的運作超出這個範疇,則程式很可能會因此發生 coredump 或 segmentation fault;因為存的運作(write operation),有可能破壞了其他資料結構的內容值;而取的運作(read operation),則是從錯誤的地方去讀取了某個值,而這個值很可能會造成後續的程式無法順利地運作。

Stack Bound Read/Write (SBR/SBW)

所謂「Stack Bound Read/Write」,是指程式對array object 的存取運作,超越了array object 的有效範疇,而且該 array object 的 storage,是從 stack frame 配置而來。

Array Bound Read/Write (ABR/ABW)

所 謂「Array Bound Read」或「Array Bound Write」,是指程式對 array object 的存取運作,超出其有效的範疇,而且 array object 的 storage,是從 data segment 或 heap area 配置而來。

Null Pointer Read/Write (NPR/NPW)


所謂「Null Pointer Read」或「Null Pointer Write」,是指某個 pointer 指向 NULL,亦即該 pointer 沒有指向任何的 object,但程式卻未能檢測其值是否為 NULL,即逕行透過它來執行存取的運作。

Freeing Mismatched Memory (FMM)

所 謂「Freeing Mismatched Memory」,是指「配置記憶體的函式」與「釋放記憶體的函式」不一致;例如,程式呼叫 calloc 函式,要到了一塊記憶體,使用完之後卻呼叫 delete 去歸還記憶體。當程式帶有 FMM 的錯誤時,是不太容易察覺的;因為 compiler 無法找出 FMM 的錯誤,而且 FMM 的錯誤通常不會直接造成 coredump;此時,PurifyPlus 即可上場救援,透過它來找出 FMM 的錯誤。對於可以取得或釋放記憶體資源的相關函式,PurifyPlus 能夠檢測出兩者是否一致的搭配組合為:

"new" versus "delete"
"new []" versus "delete []"
"malloc" versus "free"
"calloc" versus "free"
"realloc" versus "free"
"XtMalloc" versus "XtFree" (X Windows APIs)

Free Memory Read/Write (FMR/FMW)

所 謂「Free Memory Read」或「Free Memory Write」,是指之前取得的記憶體資源,已經歸還給系統,但因程式的疏失或邏輯上的錯誤,使得某個 pointer 還依然指向該記憶體資源,並陸續地透過該 pointer 對其執行讀取或寫入的運作。

Dangling Pointer 與FMR/FMW

所 謂「Dangling Pointer」,是指某個 pointer 在一開始時,是指向一個活生生的 object,之後這個 object 可能因為衰老而壽終正寢,抑或遭逢意外而不幸身亡,但該 pointer 卻渾然不知,還是繼續對這個已經不存在的 object 執行讀取或寫入的運作,並因此引發FMR 或FMW 的錯誤。

之前所看到的 fmr_fmw.c 程式,由於主要目的是在做示範,故其所犯之錯誤看來是十分愚蠢;但在實際的軟體研發過程中,由dangling pointer 所引發的 FMR 與 FMW,是很有可能發生的。舉例來說,現在一般的軟體架構,大都採取 layered design 的設計;也就是說,有些 software components 是隸屬於底層,假設叫做 kernel layer components,簡稱 kernel layer;而有些則是隸屬於上層,假設叫做 application layer components,簡稱 application layer;當 kernel layer 遇到某個 CreateMyObj event,並因此配置了許多data type 叫做 MyObj 的 objects;之後、application layer 遇到 GetMyObjHdl event,因此application layer 向kernel layer 要求,希望能取得由 kernel layer 所配置的所有資料型態為 MyObj 的object 之address (即handle),並透過 array of pointer 或 pointer pool,在 application layer 記下這些 handles;當 kernel layer 遇到DestroyMyObj event,並因此將所有資料型態為 MyObj 的 objects 歸還給作業系統,但卻在歸還之後,忘了通知 application layer,它已經將所有的 objects 都釋放掉了;此時,application layer 認為它所保持的handles,依然指向實質存在的 objects,但因實際上都已經不存在,所以這些 handles 也就變成了dangling pointers,因此後續的讀或寫之運作,將會引發 FMR 或 FMW 的錯誤。

這種由 layered design 所引起的 dangling pointer,可以透過 callback mechanism 來解決;亦即 application layer 必須向 kernel layer 註冊 DestroyMyObj event 以及 callback function;而 kernel layer 在遇到 DestroyMyObj event 時,一定要透過 callback function 去通知 application layer,讓 application layer 有機會對 array of pointer 或 pointer pool 的內容,執行適當的清除運作。

Free Unallocated Memory (FUM)

所謂「Free Unallocated Memory」,是指程式所指定要歸還的記憶體資源之位址,之前已經被歸還過了,抑或從來就不曾被配置過;因此,該次的歸還運作是多此一舉。

Memory Leak (MLK)

所謂「Memory Leak」,是指程式沒有將記憶體資源做妥善的利用,以致於造成閒置或浪費的情形;而這種情形就如同沒有將水龍頭關緊,使得涓滴的自來水不斷地被浪費掉,因此將之稱為 memory leak。

筆 者將memory leak 的來源分為兩種:一、lost memory block;二、undeallocated memory block。其中,lost memory block 是指程式把某塊記憶體資源給搞丟了;而 undeallocated memory block 則是指程式已經不再使用某塊記憶體資源,但仍然把持著該記憶體資源的使用權利 (有可能是「忘記」或是「不願意」釋放記憶體資源)。

Lost Memory Block

所 謂「Lost Memory Block」,是指原本指向某 2 MB memory block 的 pointer,被程式更動後指向另外一塊 4 MB memory block,導致沒有任何的 pointer 指向之前的 2 MB memory block;此時,這塊 2 MB memory block 就成了被搞丟、被遺忘的記憶體資源。

若以日常生活的例子來比喻 lost memory block,它就像是自己買了一把螺絲起子,但在使用過後,由於沒有保持物歸原處的習慣,用完就隨手丟、隨處放,導致最後忘了螺絲起子放在哪裡,而且怎麼 找就是找不到;通常,在遍尋不可得的情況下,只好到修繕材料店再買一把螺絲起子;但如此一來,就造成了金錢與資源的浪費。

Undeallocated Memory Block

所謂「Undeallocated Memory Block」,是指程式已經不再使用某塊記憶體資源,但仍然把持著該記憶體資源的使用權利,使得這塊記憶體資源持續地處於閒置的狀態,直到 process 結束。

造成 undeallocated memory block 的原因有兩種:一、忘記歸還記憶體資源;二、故意佔著不放。通常,真正的原因是「忘記」,很少有程式故意佔著記憶體資源而不使用。

軟體測試的基本觀念

更多精彩请到 http://www.139ya.com

軟體測試的基本觀念

軟 體測試的議題,一般而言都可寫成專書來做論述,但在此節中,筆者僅就常見的以及本書所觸及的議題來做扼要的講述,並偏重於實作程式時的測試技術,所以沒有 對所有的議題做完整的論述;因此,想對軟體測試議題做通盤瞭解的讀者,則不妨上 amazon 網站鍵入 software testing 做搜尋,相信一定可以找到不少的專書著作。

Black Box Testing versus White Box Testing

在 說明正題之前,我們不妨來看個生活化的案例 ── Black Box Users versusWhite Box Users。什麼是black box users?大部分的人都是 black box users,也就是在購買家電或資訊產品時,只在乎它能完成什麼樣的功能以及如何使用,至於產品本身是如何完成該功能,使用者一點都不在乎;white box users 則是典型的好奇寶寶,這群使用者對於產品是如何完成各項功能,有著非常強烈的好奇心,而為了一窺箇中堂奧,凡是買回來的家電或資訊產品,幾乎都會遭到拆 解,以滿足其內心的好奇渴望。

看完 black box users 與white box users 的說明,相信在讀者的心中,對於什麼是 black box testing 與 white box testing,已經大致有譜了。所謂「Black Box Testing」,是指只知道受測元件的介面與功能等規格(例如它應當執行哪些功能,以及「什麼樣的 input 該得到什麼樣的 output」等等),但完全不瞭解或是完全不在乎受測元件是如何完成這些功能;在這樣的環境與條件下所執行的測試,就是 black box testing。

就「Test Case Design」而言,因為 black box test case 完全不涉及受測元件的內部架構與實作的細節,所以它的可重複使用性很高,而且不需要等到受測元件完成之後才能設計 test case。

所 謂「White Box Testing」,是指根據受測元件的規格、以及其內部的架構、實作的細節與設計意圖(design intention),而所執行的測試。white box testing 的優點是:除了可驗證實作細節的設計意圖之外,亦能操控受測元件的內部機制,使得測試者可以進一步地以更有效率的方式來測試受測元件;例如藉由操控內部的 某些機制,可以更方便地產生某些特殊的 input pattern,或是讓受測元件以更短的時間進入某種狀態等等。

就「Test Case Design」而言,white box test case 必須挑戰內部架構與實作細節的設計意圖,才能極大化其驗證的效果,所以設計 test case 的人最好不要找實作細節的執行者,以避免「當局者迷」所帶來的缺點與盲點;此外,由於 white box test case 與受測元件的內部架構與實作細節之相依程度很高,一旦受測元件改採不同的架構或實作方式,test case 就得重新設計,所以它的可重複使用性很低;最後,相較於 black box test case,white box test case 通常必須等到受測元件完成之後,才能開始設計,所以它在設計的時程上是受制於產品的研發進度。

Static Testing versus Dynamic Testing

所 謂「Static Testing」,顧名思義,就是在受測元件處於靜止的狀態下,對它所執行的測試;所謂「Dynamic Testing」,是指在受測元件處於活躍的狀態下,對它所執行的測試。以買車為例,到汽車展示場看車時,就是在執行 static testing,例如,打開車門進去坐坐,握握方向盤,用手觸摸內裝的質感,觀察其內裝的組裝品質與儀表版的配置,再走出車外用眼睛觀察烤漆的品質,用腳 踢踢輪胎,掀開引擎蓋看看引擎等等;跟汽車展商約定試車時間與地點,將車子駛上道路做試開,感受其馬力、扭力、引擎運轉的流暢性、避震的效果、隔音的靜謐 性以及轉彎的順暢度與離心力道等等,就是在執行 dynamic testing。

就軟體而言,只要程式碼還沒開始動工,就只能執行 static testing,例如 requirement validation、specification review、architecture review 等等,等到程式碼開始動工之後,才能依序展開各項的 dynamic testing,例如 unit test、integration test 與 systemtest 等等。

Validation versus Verification

簡 單地說,「Validation」是指「做對的事情 (do the right things)」,例如產品研發的大方向與目標對或不對、正確或不正確;而「Verification」則是指「把事情做對 (do the things right)」,例如專案的執行力好或不好,實作的技術能力精準或不精準。

以患病求診為例,針對醫生所開立的處方,檢視其 是否「對症下藥」,就是在做 validation;就病人而言,檢視其是否「按時服藥」,就是在做 verification。就軟體而言,validation 是在檢視產品所提供的功能,是否為使用者所期盼的功能,以及是否滿足了使用者的需求等等,而 verification 則是在檢視產品是否依照當初所規劃的規格來設計與實作,以及完工後的品質是否符合標準等等。


衡量程式複雜度的方法

更多精彩请到 http://www.139ya.com

在軟體工程的領域中,有著各式各樣衡量軟體複雜度的方法;其中,有些衡量方法雖 然有其理論的論述基礎,但其計算過程則顯得有些繁瑣,例如 Function Points 與 Halstead's Software Science。以筆者的觀點而言,實用的 software metrics 除了必須具備一定程度的說服力之外,還得具備計算簡潔的特點;據此,筆者將為讀者介紹兩種 software metrics:一、cyclomatic complexity;二、information flow complexity。其中,cyclomatic complexity 是以 control flow 的觀點,去衡量程式碼的複雜度;而 information flow complexity 則是以 data flow 的觀點,去衡量程式碼的複雜度。

Cyclomatic Complexity

這 個「Cyclomatic Complexity」,是由 Thomas J. McCabe 在1976 年12 月於IEEE Transactions on Software Engineering 所提出的,其主要目的在衡量一個 software module 的 decision logic 之複雜度,並可根據複雜度的數值,來預測這個 software module 的可靠性與穩定度;如果 cyclomatic complexity 的值越大,則表示該 software module 的 decision logic 越複雜,出錯的機率或不穩定的程度也越高。

如果將某個 software module 的程式碼化為 control flow graph 之後,其中有 e 個 edges,n 個 nodes,則其 decision logic 的複雜度之計算公式為:

cyclomatic complexity = e – n + 2

這 個由 Thomas J. McCabe 所提出的 cyclomatic complexity,對軟體研發而言有兩個很重要的意義:一、就 software testing 而言,它表示至少需要多少個 test case,才能將 control flow graph 中的每一條 independent path,都走過一遍;二、如果其值過大,例如超過 20,則表示該 software module 的 decision logic 過於複雜,以及目前的軟體架構很可能有其不當之處;因此,應當再次檢視與分析整個 software architecture,以降低這個 software module 的 cyclomatic complexity。

當然,每樣 東西都有其好壞兩面;因此,cyclomatic complexity 亦有其未盡完善之處;其中,最為人所詬病的缺點為:一、對於 non-nested control flow 與 nested control flow 皆給予相同的 complexity,但實際上 nested control flow 應當比 non-nested control flow 來得複雜;二、k-way switch statement 之 k 值通常很大,但大部分的 k-way switch statement 的 control flow 是很單純的;例如,只是單純地將 enum 轉成 string literal;因此,k-way switch statement 通常會使得 cyclomatic complexity 失真。針對第一點, 筆者在稍後會提出一個 nested cyclomatic complexity 來做修正;至於第二點,則可視情況將k-way switch statement 的 complexity contribution 由計算式中刪除,以避免 cyclomatic complexity 失真。

Information Flow Complexity

所 謂「Information Flow Complexity」,顧名思義,就是在分析某個 software module 的 data flow complexity;它所考慮的要素有:一、讀取了哪些 parameters;二、更改了哪些 parameters (updated by value 的不列入計算,只計算updated by reference);三、呼叫了哪些functions (functions called);四、被哪些 functions 所呼叫 (functions that call this function);五、讀取了哪些 global variables;六、更改了哪些 global variables。待查妥所有的要素之值後,即可根據下列公式來計算 information flow complexity:

information flow complexity = length * (fan-in * fan-out)2

where length could be cyclomatic complexity or line of code

fan-in = functions called + parameter read + global variables read

fan-out = functions that call this function + parameter updated by reference + global variables updated

如 果某個 software module 的information flow complexity 之值很高,則表示該 software module 有可能是一個負載很重的關鍵點;例如,處理過多種類的資料抑或分派與協調過多種類的功能等等。就這個 information flow complexity 而言,筆者在蒐集與閱讀相關資料的過程中,並沒有看到任何的文獻提到,有什麼樣的典型參考值可以做為負載是否過重的判斷基準;所以,到底 information flow complexity 的值要大到多大,才算是負載過重,到目前為止似乎是沒有定論的;然而,即使沒有典型的參考值,工程師或專案團隊還是可以針對所有 software module 的 information flow complexity,求出它們的「中位數」與「四分位數」,來做為比較與判斷的基準。

The seven rules of Unobtrusive JavaScript

更多精彩请到 http://www.139ya.com

转自:http://icant.co.uk/articles/seven-rules-of-unobtrusive-javascript/

I've found the following rules over the years developing, teaching and implementing JavaScript in an unobtrusive manner. They have specifically been the outline of a workshop on unobtrusive JavaScript for the Paris Web conference 2007 in Paris, France.

I hope that they help you understand a bit why it is a good idea to plan and execute your JavaScript in this way. It has helped me deliver products faster, with much higher quality and a lot easier maintenance.

1. Do not make any assumptions (JavaScript, the unreliable helper)

Probably the most important feature of unobtrusive JavaScript is that you stop making assumptions:

  • You don't expect JavaScript to be available but make it a nice-to-have rather than a dependency
  • You don't expect browsers to support certain methods and have the correct properties but you test for them before you access them
  • You don't expect the correct HTML to be at your disposal, but check for it and do nothing when it is not available
  • You keep your functionality independent of input device
  • You expect other scripts to try to interfere with your functionality and keep the scope of your scripts as secure as possible.

The first thing to consider before you even start planning your script is to look at the HTML you are enhancing with scripting and see what you can use for your own purposes.

2. Find your hooks and relationships (HTML, the base to build on)

Before you start your script look at the base that you build upon. If the HTML is unstructured or unknown there is hardly any way to create a clever scripting solution - you will most likely create either far too much markup with JavaScript or your application will depend on JavaScript.

There are several things to consider in your HTML - hooks and relationships

HTML Hooks

HTML hooks are first and foremost IDs, as these can be accessed with the fastest DOM method - getElementById. These are safe as IDs are unique in a valid HTML document (IE has a bug with name and ID, but good libraries work around that) and easy to test for.

Other hooks are HTML elements which can be read out with getElementsByTagName and CSS classes, which can not be read out with a native DOM method in most browsers (Mozilla will soon have one and Opera 9.5 already does though). However, there are a lot of helper methods that allow for a getElementsByClassName.

HTML relationships

The other interesting thing about HTML is the relationships of your markup. Questions to ask yourself are:

  • How can I reach this element the easiest and with the least steps traversing the DOM?
  • What element do I need to alter to reach as many child elements that I need to change?
  • What attributes or information does a certain element have that I can use to link to another?

Traversing the DOM is expensive and can be slow, that is why it is a good idea to leave it to a technology that is already in use in browsers.

3. Leave traversing to the experts (CSS, the faster DOM traveller)

It is pretty interesting that DOM scripting and traversing the DOM with its methods and properties (getElementsByTagName, nextSibling, previousSibling, parentNode and so on) appears as a confusing matter to a lot of people. It is interesting as we already do it with a different technology: CSS.

CSS is a technology that takes a CSS selector and traverses the DOM to access the desired elements and change their visual attributes. A rather complex JavaScript using DOM can be replaced with a single CSS selector:


var n = document.getElementById('nav');
if(n){
var as = n.getElementsByTagName('a');
if(as.length > 0){
for(var i=0;as[i];i++){
as[i].style.color = '#369';
as[i].style.textDecoration = 'none';
}
}
}

/* is the same as */

#nav a{
color:#369;
text-decoration:none;
}

This is a very powerful companion to have and you can piggyback on it. You do that by dynamically assigning classes to elements higher up in the DOM hierarchy or altering IDs. If you simply add a class to the body of the document using DOM you can easily offer a chance for a designer to define both the static and dynamic version of the document:


JavaScript:

var dynamicClass = 'js';
var b = document.body;
b.className = b.className ? b.className + ' js' : 'js';

CSS:
/* static version */

#nav {
....
}

/* dynamic version */

body.js #nav {
....
}

4. Understand browsers and users (build on existing working usage patterns and create what you need)

A really important part of unobtrusive JavaScript is to understand how browsers work (and especially how browsers fail) and what users expect to happen. It is easy to go overboard with JavaScript and create a completely different interface with it. Drag and Drop interfaces, collapsible sections, scrollbars and sliders can all be created with JavaScript, but there is much more to those than just the technical implementation. You have to ask yourself:

  • Will my new interface work independent of input device, and if not, what should be the fallback?
  • Is the new interface that I am building following rules of the browser or the richer interfaces it came from (can you navigate a multi level menu with your cursors or do you need to tab through it?)
  • What is functionality that I need to offer but that is dependent on JavaScript?

The latter is really no issue, as you can use the DOM to create HTML on the fly in case you need it. An example of this are "print this" links - browsers don't offer a non-JavaScript way of printing a document, which is why you should create links like these with the DOM. The same applies to clickable headings that collapse and expand content. Headings can not be activated with a keyboard, but links can. In order to create clickable headings you should use JavaScript to inject links inside them and all is well - even keyboard users can then collapse and expand the content sections.

Great resources for solutions of this kind of problem are design pattern libraries. As for knowing what works in browsers independent of input device, this is a matter of experience. First of all you need to understand the concept of event handling.

5. Understand Events (Event handling to initiate change)

Event handling is the next step to truly unobtrusive JavaScript. The point is not to make everything draggable and clickable or add inline handling. The point is to understand that Event Handling is true separation. We separate HTML, CSS and JavaScript but with Event Handling we go much further.

Elements in the document are there to wait for handlers to listen to a change happening to them. If that happens, the handlers retrieve a magical object (normally as a parameter called e) that tells them what happened to what and what can be done with it.

The really cool thing about most event handling is though that it does not only happen to the element you want to reach but also to all the elements above it in the DOM hierarchy (this does not apply to all events though - focus and blur don't do that). This allows you to assign one single event handler to for example a navigation list and use event handling's methods to reach what element was really involved. This technique is called event delegation and it has several benefits:

  • You only need to test if a single element exists, not each of them
  • You can dynamically add or remove new child elements without having to remove or add new handlers
  • You can react to the same event on different elements

The other thing to remember is that you can stop events from being reported to parent elements and you can override the default action HTML elements like links have. However, sometimes this is not a good idea, as browsers apply them for a reason. An example would be links pointing to in-page targets. Allowing for them to be followed makes sure that users can bookmark the state of your script.

6. Play well with others (Namespacing, scope and patterns)

Your code will hardly ever be the only script used in the document. It is therefore of utmost importance that you make sure your code does not have global function or variable names that other scripts can override. There are several patterns available to avoid this issue. The most basic is that you instantiate every variable using the var keyword. Let's say we have the following script:


var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
}
function reset(){
// do stuff
}

This has a global variable called nav and functions called init, show and reset. The functions can access the variable and each other by name:


var nav = document.getElementById('nav');
function init(){
show();
if(nav.className === 'show'){
reset();
}
// do stuff
}
function show(){
var c = nav.className;
// do stuff
}
function reset(){
// do stuff
}

You can avoid all this global code by wrapping it in an object using the object literal, thus turning the functions into methods and the variables into properties.You need to define the methods and variable with a name followed by a colon and you need to separate each of them from the others with a comma.


var myScript = {
nav:document.getElementById('nav'),
init:function(){
// do stuff
},
show:function(){
// do stuff
},
reset:function(){
// do stuff
}
}

Each of these can be accessed from outside and inside the object by prepending the object name followed by a full stop.


var myScript = {
nav:document.getElementById('nav'),
init:function(){
myScript.show();
if(myScript.nav.className === 'show'){
myScript.reset();
}
// do stuff
},
show:function(){
var c = myScript.nav.className;
// do stuff
},
reset:function(){
// do stuff
}
}

The drawbacks of this pattern is that you have to repeat the name of the object every time you access it from another method and that everything you have in your object is publicly accessible. What if you want to only make parts of the script accessible to other script in the document? For this you can use the module pattern:


var myScript = function(){
// these are all private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
}
function reset(){
// do stuff
}
// public methods and properties wrapped in a return
// statement and using the object literal
return {
public:function(){

},
foo:'bar'
}
}();

You can access the public properties and methods that are returned the same way you can in the object literal, in this case myScript.public() and myScript.foo. There is another annoyance though: if you want to access one public method from another or from a private method you need to go through the verbose long name again (the main object name can get rather long). To avoid this, you define them as private methods and only return an object with synonyms:


var myScript = function(){
// these are all private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
// do stuff
}
function reset(){
// do stuff
}
var foo = 'bar';
function public(){

}
// return public pointers to the private methods and
// properties you want to reveal
return {
public:public,
foo:foo
}
}();

Ths allows for a consistency in coding style and also allows you to write shorter synonyms when you reveal them.

If you don't want to reveal any of your methods or properties to the outside world, you can wrap the whole code block in a anonymous function and call it immediately after it was defined:


(function(){
// these are all private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
show(); // no need for prepended object name
}
function show(){
// do stuff
}
function reset(){
// do stuff
}
})();

This is a great pattern for functionality that just needs to be executed once and has no dependency on other functions.

Following all of this will make your code work well for the user and the machine it is running on as well as other developers. However, there is one more group you have to think about.

7. Work for the next developer (Making maintenance easier)

The last step to make your script truly unobtrusive is to give it another go-over when you finished and think about the next developer who has to take over from you once this went into production. Consider the following:

  • Are all the variable and function names logical and easy to understand?
  • Is the code logically structured? Can you "read" it from top to bottom?
  • Are the dependencies obvious?
  • Have you commented areas that might be confusing?

The most important bit is to understand that the HTML and CSS of a document is much more likely to change than the JavaScript (as these make up visual output). Therefore it is a great idea not to have any class and ID names or strings that will be shown to the end user buried somewhere in the code but separate it out into a configuration object instead.


myscript = function(){
var config = {
navigationID:'nav',
visibleClass:'show'
};
var nav = document.getElementById(config.navigationID);
function init(){
show();
if(nav.className === config.visibleClass){
reset();
};
// do stuff
};
function show(){
var c = nav.className;
// do stuff
};
function reset(){
// do stuff
};
}();

That way maintainers know exactly where to change these without having to alter the rest of your code.

More information