xxx网JavaScript脚本编码规范 ——V1.0 版本 Xxx科技有限公司 修改记录
目录 7.6 eval 、setInterval、setTimeout 18 1 前言网站的长期价值直接源于其编码质量。在它的整个生命周期里,一个程序可能会被许多人阅读或修改。如果一个程序可以清晰的展现出它的结构和特征,那就能减少在以后对其进行修改时出错的可能性。所有的JavaScript代码都是暴露给公众的,所以我们更应该保证其质量。本规范的宗旨就是让程序员写出的代码都是整洁并可供他人容易阅读的,尽量做到保证代码至少在IE6以上和FireFox浏览器上的可用性及健壮性。 2 脚本嵌入规则JavaScript在项目中的使用包含两种方式。第一种直接嵌入在HTML代码中,第二种建立单独的脚本文件,然后由html页面调用。 2.1 文件方式
文件名全为小写字母,单词以下划线分割,后缀名使用*.js。通用代码放在项目根目录下的js目录中,模块专属代码放在模块目录的js目录中。 /js/common.js // 通用的脚本库文件路径 /module/js/mod_add.js // 彩信增加模块所用的js文件路径 文件编码使用UTF-8。 文件方式的调用只使用type和src属性,如下示例: <script type="text/javascript" src="js/filename.js"></script> 2.2 脚本段方式
当某页面所使用的脚本只在本页面中使用,在项目的其他模块中没有详细代码时,且方法并不是很多,长度并不是很长(总长度不超过200行)的情况下可以采用脚本段方式,如果脚本过长或在项目中多次调用,请把脚本归并到.js文件在页面中调用。 把脚本段放在<head>标签中包含。尽量不要重复划分多个<script>脚本段。以下是嵌入方式的例子: <script type=”text/javascript”> alert(’sample’); </script> 3 命名规范脚本变量及方法的使用避免缩写。 3.1 脚本段下的命名
3.1.1 变量命名1. 常量以及全局变量必须全部使用大写字母,单词之间用下划线分隔。 var MMS_FRAME, LIMIT_TIME, ALERT_MESSAGE 2. 方法内变量使用多个单词组成复合词,首单词小写,其他单词首字母大写。 var arrSampleList, objSubmitButton 3. 对于指代DOM对象的变量名,尽量使用elm、obj或elm、obj开头命名。 var objSubmitButton = document.getElementById(’btn_submit’); function getCheckElement(name) { var obj = document.getElementById(name); // 没有歧义的方法内部对象命名直接使用obj return obj; } 4. 对于指代字符串对象的变量名,尽量使用str或str开头命名。 var strMaxsizeLimit = ’已超过最大限制’; function alertMessage(message) { var str = message; // 没有歧义的方法内部字符串变量命名直接使用str alert(str); } 5. 对于指代数字的变量名,尽量使用num或num开头命名。 var numMaxCount = 300; function isMaxCount(size) { var num = 1000; // 没有歧义的方法内部数字命名直接使用num return (num > size); } 6. 布尔值的变量命名前面加"is",同理还可以为 "has", "can" 或者 "should"。 var isChecked, hasTowApple, canDoit; 7. 循环变量使用"i", "j", "k" (依次类推)等名称的变量。 for (var i=0; i<10; i++) { .... } 8. 用于返回结果变量使用result或者以ret开头的变量。 function getName() { var result; // 或者使用 retName result = ’sina’; return result; } 9. 其他一些建议的约定,例如临时变量使用tmp或以tmp开头的命名;横向纵向坐标x,y;捕获的错误变量e;等等。 10. 表示数组的变量在单词前加arr进行修饰。 var arrCity = [’南京’, ’上海’]; 11. 避免使用否定的布尔变量名称,例如: isNotError, isNotFound 为非法 3.1.2 方法命名1. 方法命名使用多个单词组成复合词,首单词小写,其他单词首字母大写。 function openWin() {} 2. 方法命名的第一个单词尽量使用动词。例如: get/set, add/remove, create/destroy, start/stop, insert/delete, begin/end, 等等。 function getName() {} function beginLoop() {} 3.2 组件下的命名
API命名风格:
结构内部命名风格:
1. 类名称必须为名词并使用骆驼命名规则: Account, EventHandler 2. 常量必须在对象(类)或者枚举变量的前部声明。枚举变量的命名必须要有实际的意义,并且其成员必须使用骆驼命名规则或使用大写: var NodeTypes = { Element : 1, DOCUMENT: 2 } 3. 当变量名为一个缩写词时,不要使用全大写方式: getInnerHtml(), getXml(), XmlDocument 4. 方法的命名应该为一个动词或者动词短语: obj.getSomeValue() 5. 公有类的命名必须使用混合名称(mixedCase)命名。 6. 私有类的变量属性成员必须使用混合名称(mixedCase)命名,并前面下下划线(_): var MyClass = function() { var _buffer; this.doSomething = function() { }; } 7. 变量如果设置为私有或者被有意定义为私有,则前面必须添加下划线: this._somePrivateVariable = statement; 8. 通用的变量一般使用与其名字一致的类型名称: setTopic(topic) // 变量 topic 为 Topic 类型的变量 9. 所有的变量名使用英文名称。 10. 变量如有较广的作用域(large scope),必须使用全局变量;此时可以设计成一个类的成员。相对的如作用域较小或为私有变量则使用简洁的单词命名。 11. 如果变量有其隐含的返回值,则避免使用其相似的方法: getHandler(); // 避免使用 getEventHandler() 12. 公有变量必须清楚的表达其自身的属性,避免字义含糊不清,例如: MouseEventHandler,而非 MseEvtHdlr。 请再次注意这条规定,这样做得的好处是非常明显的。它能明确的表达表达式所定义的含义。例如: dojo.events.mouse.Handler // 而非 dojo.events.mouse.MouseEventHandler 13. 类/构造函数 可以使用 扩展其基类的名称命名,这样可以正确、迅速的找到其基类的名称: EventHandler 组件下的特殊命名规范 1. 术语 "initialize" 或者 "init" 作为变量名应为已经实例化(初始化)完成的类或者其他类型的变量。 2. 错误类建议在变量名称后加上 "Exception" 或者 "Error"。 3. 方法如果返回一个类,则应该在名称上说明返回什么;如果是一个过程,则应该说明做了什么。 4 变量的定义及使用1. 变量必须声明后再使用,必须使用var声明变量。 错误 name = ’jack’; // 未声明变量直接使用 错误 name = ’jack’; var name = ’rose’; // 声明语句在使用语句之后 正确 var name; // 此处不能省略 name = ’jack’; 2. 建议变量声明时做初始化,可用null值做初始化。 3. 变量的定义要准确,不能产生歧义。 4. 把有联系的变量尽量定义在一起。 var day, week, month; var objButtonSetDay, objButtonSetWeek, objButtonSetMonth; 5. 变量应该放在和他有关系的代码块中。 function setName() {
... do some thing ... var i = 0; // 放在此处使代码更紧凑,也更容易理解 while (i < 10) { ... } } 6. 使变量具有最小的声明周期。 不建议使用的方式 var tmp, arrBox = [...][...]; for (var i = 0; i < 10; i++ ) { tmp = arrBox[i]; alert(tmp[0]); } 建议使用的方式 var arrBox = [...][...]; for (var i = 0; i < 10; i++ ) { var tmp = arrBox[i]; // tmp变量只在循环体内使用,则在循环体内声明 alert(tmp[0]); } 7. 条件表达式: a) 应该尽量避免复杂的条件表达式,如有必要可以使用临时布尔变量。 // 不好的方式 if (a == b && b == c && c == d) { // 循环体太长或太复杂,不要使用此方式 alert(’ok’); } // 建议的方式 var isOk = (a == b && b == c && c == d); if (isOk) { alert(’ok’); } b) 应避免在条件表达式中加入执行语句。 if (a = b) { // 不要使用 if (a == b) { // 正确 5 布局5.1 缩进
缩进的单位为四个空格。避免使用Tab键来缩进,因为没有一个统一的tab长短标准。虽然使用空格会增加文件的大小,但在局域网中几乎可以忽略,且在最小化过程中也可被消除掉。 整合在HTML/JSP中的代码段首缩进,应该和<script>标签保持相同。 5.2 每行长度
避免每行超过150个字符。当一条语句一行写不下时,请考虑折行。在运算符号,最好是逗号后换行。在运算符后换行可以减少因为复制粘贴产生的错误被分号掩盖的几率。下一行应该缩进8个空格。 var str = ’sample text’ + ’sample text line 5.3 块
1. 普通代码段应该看起来如下: while (!isDone) { doSomething(); isDone = moreToDo(); } 2. if语句如下: if (someCondition) { statements; } else if (someOtherCondition) { statements; } else { statements; } 3. for语句如下: for (initialization; condition; update) { statements; } 4. while语句如下: while (!isDone) { doSomething(); isDone = moreToDo(); } 5. do...while语句如下: do { statements; } while (condition); 6. switch语句如下: switch (condition) { case ABC: statements; //fallthrough case DEF: statements; break; default: statements; //最后的case语句不需要使用break } 7. try...catch...finally语句如下: try { statements; } catch (e) { statements; } finally { statements; } 8. with 语句,不要使用with语句。 9. 只有一条语句的if-else, while或for块不能省略{},也不要放在同一行上书写: if (condition) { statement; } while (condition) { statement; } for (intialization; condition; update) { statement; } // 正确 if (confition) { statement; } while (condition) { statement; } for (intialization; condition; update) { statement; } 5.4 空白
1. 保留字后面应该都跟上一个空格: var foo = function() {} 2. 逗号后面跟一个空格 var foo = function(name, age, sex) {} 3. 冒号的前后各放一个空格 var strPosition = (a > b) ? ’top’ : ’bottom’; 4. for语句的分号后跟一个空格 for (var i = 0; i < 10; i++) {} 5. 分号前永远不要有空格 var city = ’ 6. 函数方法调用后不要加空格,例如: doSomething(someParameter); // NOT doSomething (someParameter) 7. 一个相对独立的逻辑代码块前后可以用一个空行和前后区分开 Function doSomething() { Logic block 1... Logic block 2... } 8. 声明建议对齐这样可以更易阅读 var name = ‘’’;Function doSomething() { Logic block 1... Logic block 2... } 6 注释6.1 行内注释
1. 全局变量要求有注释,仔细说明用途。 2. 从已解决的方案到未开发的功能,注释必须与代码保持同步。 3. 所有的注释请尽量使用中文。 4. 简短的注释可以在本行代码后空一格添加。 doSomething(); // 提高油价 5. 较长的注释放在代码段的上方并且缩进相同。 // 图标式按钮的鼠标移动效果 doSometing(); 6. 连续注释超过一行的注释方法使用 /* */进行段注释。 /* some old code... some old code... */ 7. 对于用注释的方法保存的代码段,一定要在注释开头写明为什么注释掉该段代码。 /* 因需求修改,不用再取用户数据,此处代码作废 some old code... some old code... */ 6.2 方法注释
6.2.1 注解原则1. 脚本段中代码若自定义方法的代码行数超出5行(不包括正则表达式),则方法需要完整的注释。参考 2. 脚本段中代码对于行数小于等于5行的方法,需在方法前按照代码段的方式进行注释,并在注释中进行参数、返回数据的说明。 // 判断给定字符是否全为a-z的字母,大小写均算 // val 输入的验证字符 // return 是否符合判断要求 var alpha = function(val) { return /^[a-zA-Z]+$/.test(val); }; 3. 脚本文件中的方法一律要求完整的注释。参考 4. 脚本文件头部需用 /* */ 说明本文件的大致功能,及包含内容。 5. 如果方法因某种原因被修改,则需求增加修改历史注解。 6. 修改历史包括:修改人、修改时间、修改原因。 7. 修改历史可根据实际情况放置在方法注释或是代码段中。 // 判断给定字符是否全为a-z的字母,大小写均算 // chengyao var alpha = function(val) { // chengyao return /^[a-zA-Z]+$/.test(val); }; 6.2.2 完整的注解要求1. 以"/**"、 "*/"将注解封闭; 2. 注解内容按顺序包括:功能描述、参数说明、返回数据说明、作者、开发时间等信息; 3. 功能描述:内容独立一行或多行,详细描述方法的功能 4. 参数:每个参数的说明占一行,多个参数则用多行描述 5. 返回数据:返回数据描述 6. 作者:以邮箱地址的用户名为准,可以附加中文名字加以描述 7. 时间:首次编写时间,格式YYYY-MM-DD 8. 作者时间放在同一行,中间以空格分割。并保证和上面的注释保持一个空白行。 /** * 格式化上传文件的文件名,保证不包含一些特殊字符,不超过特定长度 * realname 传入文件名 * return 格式化后的文件名,长度不超过20 * * chengyao */ var formatRealname = function(realname) { if (realname.length>20) { return realname.substr(0, 15) + '...' + realname.substr(realname.length-4, 4); } else { return realname; } }; 7 其他建议7.1 对象及数组的使用
使用{}代替new Object()。使用[]代替new Array()。 当成员名是一组有序的数字时使用数组来保存数据。当成员名是无规律的字符串或其他时使用对象来保存数据。 var arrCity = new Array(’南京’, ’上海’); // 不要这样写 var arrCity = [’南京’, ’上海’]; // 正确
// 不要这样写 var objPerson = new Object(); objPerson.name = ’jakson’; objPerson.sex = ’male’; // 正确 Var objPerson = { name : ’jakson’, sex : ’male’ }; 7.2 字符串限定符没有特殊理由的情况下统一使用单引号作为字符串的限定符。例如: var strIntro = ’I\’m a teacher. Say : ”hello~!”’; 对于字符串中包含的单引号可以使用\进行转义。 7.3 恒等运算符和相等运算符为了程序的可读性,建议少用相等运算符进行错值比较。 var a; // 如果要明确比较a是null/undefined/某类型值,不要使用== alert(a == null); // true alert(a === null); // false alert(a === undefined); // true 7.4 减少DOM访问三条指导建议: l 缓存已经访问过的元素 var objButton = document.getElementById(’button’); // 在使用前置入变量 for (var I = 0; I < 10; I++) { // 不要直接获取再操作
// 这样正确 objButton.innerHTML += i.toString(); } l "离线"更新节点, 再将它们添加到树中 for (var i = 0; i < items.length; i++) { var item = document.createElement("li"); item.appendChild(document.createTextNode("Option " + i); list.appendChild(item); } // 上面这样的操作是很低效的 // 正确 var fragment = document.createDocumentFragment(); for (var i=0; i < items.length; i++){ var item = document.createElement("li"); item.appendChild(document.createTextNode("Option " + i); fragment.appendChild(item); } list.appendChild(fragment); l 避免使用 JavaScript 输出页面布局--应该是 CSS 的事儿 element.style.backgroundColor = "blue"; element.style.color = "red"; element.style.fontSize = "12em"; // 尽量不要使用javascript同时更新一个元素的style // 应该定义class,然后更新元素的class .newStyle { background-color: blue; color: red; font-size: 12em; } element.className = "newStyle"; 7.5 始终使用分号语句结束后请总是用分号结束它。 var someItem = 'some string'; // 这里的分号不要省略,虽然对有些浏览器不用分号也可以 var doSomething = function() { return 'something'; }; // 这里的分号不要省略,虽然对有些浏览器不用分号也可以 7.6 eval 、setInterval、setTimeout我们知道eval给了我们接近javascript编译器的机会,我们可以执行一串给eval的字符串参数。实质上这样不仅降低了你脚本的性能,而且它也会给那段字符串代码过于高的权限而导致安全问题,所以避免使用eval。直接传递字符串给setInterval或是setTimeout是很低效代码也很难维护的,而且这也会遇到跟eval一样的问题,最好传递一个函数名称给它们。 // 不好的方式 setInterval("document.getElementById('container').innerHTML += 'number: ' + i", 3000); // 使用function更恰当 setInterval(someFunction, 3000); 8 代码示例8.1 脚本段<script type="text/javascript"> // 当前页面的表单对象 var MMS_FORM; // 页面载入后的初始化操作 window.onload = function() { // 表单对象引用 MMS_FORM = document.forms[0]; } // 让所有name为mmsId的checkbox元素都选中 function checkAllMmsid() { for (var i = 0; i < MMS_FORM.elements.length; i++) { if (MMS_FORM.elements[i].name == 'mmsId') { MMS_FORM.elements[i].checked = all.checked; } } } // 取消单个彩信推荐的表单 // mmsId 需提交的彩信id function singleCancel(mmsId) { MMS_FORM.elements['method'].value = 'cancel'; MMS_FORM.elements['ids'].value = mmsId; MMS_FORM.submit(); } // 选中的彩信推荐项目一次性提交取消 function batchCancel() { if (check('1')) { MMS_FORM.elements['method'].value = 'cancel'; MMS_FORM.submit(); } } /** * 检查用户选中彩信推荐checkbox的情况 * sign 0 推荐操作 1 取消推荐操作 * return 如果未选择或则不存在选项则返回false * * chengyao */ function check(sign) { var strCheckedValue = ''; var j = 0; if (typeof MMS_FORM.elements['mmsId'] != 'undefined') { var countMmsid = MMS_FORM.elements['mmsId'].length; // 是否存在目标选择元素 if (MMS_FORM.mmsId) { // 是否多个选择元素 // chengyao if (MMS_FORM.elements['mmsId'].length) { for (var i = 0; i < countMmsid; i++) { var objMmsid = MMS_FORM.elements['mmsId'][i]; if (objMmsid.checked) { j++; if (j > 1) { strCheckedValue += ','; } strCheckedValue += objMmsid.value; } } // 单个元素的情况 } else if (MMS_FORM.elements['mmsId'].checked) { strCheckedValue += MMS_FORM.elements['mmsId'].value; } } if (strCheckedValue == '') { alert('请先选择信息'); return false; } MMS_FORM.ids.value = strCheckedValue; return true; } else { if (sign == '0') { alert('没有可推荐的信息!'); } else { alert('没有可取消推荐的信息!'); } return false; } } </script> 8.2 脚本文件/* 文件头部说明 */ /** * 获取说明 * element 元素 * return 宽高对象 {top,left,rigth,bottom} * * chengyao */ function getpos(element) { if (arguments.length != 1 || element == null) { return null; } var elm = element; var offsetTop = elm.offsetTop; var offsetLeft = elm.offsetLeft; var offsetWidth = elm.offsetWidth; var offsetHeight = elm.offsetHeight; while (elm = elm.offsetParent) { // add this judge if (elm.style.position == 'absolute' // || elm.style.position == 'relative' || (elm.style.overflow != 'visible' && elm.style.overflow != '')) { break; } offsetTop += elm.offsetTop; offsetLeft += elm.offsetLeft; } return { top : offsetTop, left : offsetLeft, right : offsetWidth + offsetLeft, bottom : offsetHeight + offsetTop }; } /** * 得到给定字符的长度,中文为2个长度 * str 给定字符 * return 字符长度 * * chengyao */ function getStringByteLength(str) { var numLength = 0; for (var i = 0; i < str.length; i++) { if ((str.charCodeAt(i) < 0) || (str.charCodeAt(i) > 255)) { numLength = numLength + 2; } else { numLength = numLength + 1; } } return numLength; } /** * 判断浏览器是否为ie * @return true为ie false非ie * * chengyao */ function IsIE() { return document.all ? true : false; } |
|