JavaScript学习笔记(二) 基础

本文探讨了如何编写高质量的JavaScript代码,包括最小化全局变量、合理使用var声明变量、优化for循环、正确使用switch语句等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一篇就会讨论如何写成高质量的JavaScript代码,如避免使用全局变量,仅使用一个var声明变量,提前获取length在循环的时候,编码约定等;

还有一些其他的习惯和技巧,写JavaScript API 文档等。

写可维护的代码(Writing Maintainable Code)

修复软件的bug是非常昂贵的,成本随时间流逝而增加,特别当bug出现在发布的版本中;
最好的就是当你发现bug的时候就消灭它,这时候在你的大脑中你对问题代码还是熟悉的;
然后当你去做别的事情,忘了这些代码,重新阅读代码的时候,你需要时间去学习和了解问题,花费时间去理解代码;
在大的项目或公司,一般写代码和改bug的不是一个人,阅读别人的代码和看自己很久之前自己写的代码都是很费时间的。
可维护主要因素:1.可读性,2.一致性,3.可预知性, 4.看起来像一个人写的, 5.是否写有文档

最少化全局变量(Minimizing Globals)

JavaScript使用函数管理作用域,在函数中声明的变量是一个局部变量(local),在函数外面是不可被访问的;
另一方面,不是在函数中声明的变量就是一个全局变量或者直接使用而没有声明的变量;
每一个JavaScript环境(浏览器)都有一个全局(global)对象(不能直接访问),当你在函数外使用this时,就是被关联到这个对象;
你声明的每一个全局变量,都会成为global对象的属性;
为了简单起见,在浏览器中global对象有一个属性window指向global对象自己;
[javascript]  view plain  copy
  1. myglobal = "hello"// antipattern  
  2. console.log(myglobal); // "hello"  
  3. console.log(window.myglobal); // "hello"  
  4. console.log(window["myglobal"]); // "hello"  
  5. console.log(this.myglobal); // "hello"  

全局变量带来的问题(The Problem with Globals)

全局变量在任何地方都可以被访问到,当程序的两个地方都声明了相同名称的变量,但目的不同的时候就会产出莫名其妙的问题;
如:引入了别人写的代码,就会产生冲突;
在JavaScript中创建的一个全局变量是非常非常简单,你可以使用一个未经声明的变量,它就会自动成为了一个全局变量,意味着成为global对象的一个属性
[javascript]  view plain  copy
  1. function sum(x, y) {  
  2.     // antipattern: implied global  
  3.     result = x + y;  
  4.     return result;  
  5. }  

上面这段代码中,result未经声明就直接使用,这段代码功能是完全可以的,但当函数调用结束之后,就多了一个result全局变量,这可能会造成其他问题;

经验告诉我们声明一个变量一定要带上var,像下面这样;

[javascript]  view plain  copy
  1. function sum(x, y) {  
  2.     var result = x + y;  
  3.     return result;  
  4. }  
但要注意的是,即使你使用var声明变量了,但也有可能无意中造就一个全局变量,就想下面一样(实际中可能真会这么干),a是局部的,但b是全局的;

[javascript]  view plain  copy
  1. // antipattern, do not use  
  2. function foo() {  
  3.     var a = b = 0;  
  4.     // ...  
  5. }  


如何你想知道为什么?别告诉我你不想。这是因为从由往左求值;

表达式b=0赋值,在这种情况下b是未声明(定义)的,所以b会成为一个全局变量;

返回结果是0然后再赋值给a(使用var声明的)这个局部变量;

上面的代码就等同于:var a = (b = 0);

如何你已经声明了变量,使用链式赋值就没有问题了,就不会无意中产生新的全局变量;

[javascript]  view plain  copy
  1. function foo() {  
  2.     var a, b;  
  3.     // ...  
  4.     a = b = 0; // both local  
  5. }  

另一个原因避免全局变量是为了移植性,如果你想你的代码运行在不一样的环境(host)中,使用全局变量是非常危险的,因为它可能无意中

重写global对象的属性,而在你原来的环境中不存在。你认为变量的名称(name)是安全的,但其实不然。

忘写var的副作用(Side Effects When Forgetting var)

使用var明确定义的全局变量,和直接使用未经定义的全局变量有一个细微的差别,就是使用delete操作符时操作结果不一样;
使用var定义的全局变量不能够被delete操作符删除;而隐式的未经var定义的全局变量够被delete操作符删除
[javascript]  view plain  copy
  1. // define three globals  
  2. var global_var = 1;  
  3. global_novar = 2; // antipattern  
  4. (function() {  
  5.     global_fromfunc = 3; // antipattern  
  6. } ());  
  7. // attempt to delete  
  8. delete global_var; // false  
  9. delete global_novar; // true  
  10. delete global_fromfunc; // true  
  11. // test the deletion  
  12. typeof global_var; // "number"  
  13. typeof global_novar; // "undefined"  
  14. typeof global_fromfunc; // "undefined"  

访问global对象

在浏览器中,global对象在任何地方都可以被访问到通过window这个属性(除非你做了一些手脚,比如声明了一个叫window的本地变量);
也许在其它的一些环境中,这个方便的属性叫别的名字或者无法访问,如果你想访问到它,又不想在代码中写死(hard-coding);
我们可以使用匿名函数:
[javascript]  view plain  copy
  1. var global = (function() {  
  2.     return this;  
  3. } ());  

这样做我们就可以获取到global对象,因为我们在立即执行匿名内置函数中返回this,因为它没有和使用new关键字,所以this应该一直指向global对象;
但这也不是唯一的方法,当你在开发一个类库的时候,你也可以把你的代码封装在一个立即执行的匿名函数中,然后this引用作为一个传入参数,从而访问到global对象,缩短了作用域链的搜索。

仅适用一个var(Single var Pattern)

在函数的最顶部(第一行)仅仅使用一个var声明变量,是非常有用的;
1.函数的局部变量一目了然;
2.防止逻辑错误,防止一个变量还没声明就被使用;
3.帮助你记住声明的变量,减少全局变量;
4.更少的代码;
像下面一样:
[javascript]  view plain  copy
  1. function func() {  
  2.     var a = 1,  
  3.     b = 2,  
  4.     sum = a + b,  
  5.     myobject = {},  
  6.     i, j;  
  7.     // function body...  
  8. }  
可以使用一个var声明多个变量,中间用逗号隔开;这也是个好机会去初始化变量,可以防止一些逻辑错误(所有声明但未初始化的变量,初始值都是undefined),增加代码可读性;
在声明的时候,我们也可以做些实际的工作;
[javascript]  view plain  copy
  1. function updateElement() {  
  2.     var el = document.getElementById("result"),  
  3.     style = el.style;  
  4.     // do something with el and style...  
  5. }  

使用多个var作用域提升问题(Hoisting: A Problem with Scattered vars)

JavaScript允许你在函数任何地方使用多条var语句声明变量,而且好像和在函数顶部声明的变量一样,这个表现叫做hosting;
但这也会导致一些逻辑错误,当你使用一个变量然后又在一个内部函数中声明了同名变量就会产生问题;
在JavaScript中,只要变量在相同的作用域中(同一个函数),它就会被认为是被声明的,即使在使用var声明之前被使用
举例:
[javascript]  view plain  copy
  1. // antipattern  
  2. myname = "global"// global variable  
  3. function func() {  
  4.     alert(myname); // "undefined"  
  5.     var myname = "local";  
  6.     alert(myname); // "local"  
  7. }  
  8. func()  
在这个例子中,你可能期望第一个alert()提示“global”并且第二个alert()提示“local”;这是个合情合理的判断,因为在第一次alert()时,myname并没有被声明,
所以函数应该能"看见"全局的myname;
但是结果并不是我们想当然的那样,第一个alert()会提示“undefined”,因为myname被认为一个函数的局部变量,即使是在后面的代码中声明的
所有的变量声明,都会被提升到函数的顶端,因此为了避免这用混淆,最好提前声明要使用的变量;
[javascript]  view plain  copy
  1. myname = "global"// global variable  
  2. function func() {  
  3.     var myname; // same as -> var myname = undefined;  
  4.     alert(myname); // "undefined"  
  5.     myname = "local";  
  6.     alert(myname); // "local"  
  7. }  
  8. func();  

for循环(for Loops)

在for循环中,你可以遍历一个数组或者一个类似数组(array-like)的对象(例如:arguments HTMLCollection);
常见的for循环模式就像下面一样:
[javascript]  view plain  copy
  1. // sub-optimal loop  
  2. for (var i = 0; i < myarray.length; i++) {  
  3.     // do something with myarray[i]  
  4. }  
这种模式有个问题就是在每次循环都要访问一次数组的长度,这会减慢你的代码执行的速度,当myarray不是一个数组而是一个HTMLCollection对象时;
也许你还不清楚HTMLCollection是什么;HTMLCollection是DOM方法返回的对象,如document.getElementsByName()
 document.getElementsByClassName() document.getElementsByTagName();还有一些其他的HTMLCollection,比如:
document.images document.forms document.forms[0].elements等;这种collection的问题就是每次你访问collection的length属性,你都在查询活动DOM对象,
而查询DOM对象是非常耗费资源的;这也是为什么更好的for循环模式应该缓存(cache)collection的length;
[javascript]  view plain  copy
  1. for (var i = 0, max = myarray.length; i < max; i++) {  
  2.     // do something with myarray[i]  
  3. }  
这种方法,只需要访问一次length的值,在整个for循序过程中都可以使用;
当你明确的想要在for循环中修改collection(添加或删除DOM元素),你很可能就需要length被更新而不是常量了;
但这并不符合我们先前所说的仅使用一个var的原则,所以可以这样:
[javascript]  view plain  copy
  1. function looper() {  
  2.     var i = 0,  
  3.     max, myarray = [];  
  4.     // ...  
  5.     for (i = 0, max = myarray.length; i < max; i++) {  
  6.         // do something with myarray[i]  
  7.     }  
  8. }  
这样的好处就是严格遵守使用一个var声明变量的原则,缺点就是复制和粘贴整个for循序变的复杂,好比重构的时候,当你从一个函数复制一个for循序到另外一个函数,
你必须保证也将i和max也带上到新函数中,否则就可能会造成错误,无意中产生全局变量。

还有两种for循序模式的变种;优点:1.少使用了一个变量(没有max);2.递减到0,更快,因为和0比较比和数组的length比较更有效率;
[javascript]  view plain  copy
  1. var i, myarray = [];  
  2. for (i = myarray.length; i--;) {  
  3.     // do something with myarray[i]  
  4. }  
[javascript]  view plain  copy
  1. var myarray = [],  
  2. i = myarray.length;  
  3. while (i--) {  
  4.     // do something with myarray[i]  
  5. }  

for-in循环(for-in Loops)

for-in 循环应该被用来遍历非数组类型的变量,被称之为枚举(enumeration);
技术上,你也可以用for-in循环去遍历数组(因为在JavaScript中数组,也是对象),但不推荐这样做;这样可能会导致逻辑错误,如果数组对象增加了自定义的函数;
而且在for-in循环中属性的顺序是没有保证的,所以数组最好是和for循环使用,对象配合for-in循环一起使用;

当遍历一个对象使用hasOwnProperty()方法也是非常重要的,它能过滤出那些从原型链上继承下来的属性;
考虑下面的例子:
[javascript]  view plain  copy
  1. // the object  
  2. var man = {  
  3.     hands: 2,  
  4.     legs: 2,  
  5.     heads: 1  
  6. };  
  7. // somewhere else in the code  
  8. // a method was added to all objects  
  9. if (typeof Object.prototype.clone === "undefined") {  
  10.     Object.prototype.clone = function() {};  
  11. }  
在这个例子中,我们用对象字面量(object literal)声明的简单对象man;在其它什么地方或者在man声明后面,Object对象的原型增加了一个有用方法clone();
原型链是活动的,意味着所有对象都会自动获得这个新方法;当枚举man对象时,为了防止出现clone()方法,你需要调用hasOwnProperty()方法过滤出原型方法;
[javascript]  view plain  copy
  1. // 1.  
  2. // for-in loop  
  3. for (var i in man) {  
  4.     if (man.hasOwnProperty(i)) { // filter  
  5.         console.log(i, ":", man[i]);  
  6.     }  
  7. }  
  8. /* 
  9. result in the console 
  10. hands : 2 
  11. legs : 2 
  12. heads : 1 
  13. */  
  14. // 2.  
  15. // antipattern:  
  16. // for-in loop without checking hasOwnProperty()  
  17. for (var i in man) {  
  18.     console.log(i, ":", man[i]);  
  19. }  
  20. /* 
  21. result in the console 
  22. hands : 2 
  23. legs : 2 
  24. heads : 1 
  25. clone: function() 
  26. */  
  27. 18  
另外一种调用hasOwnProperty()方法是从Object.prototype对象身上调用;
[javascript]  view plain  copy
  1. for (var i in man) {  
  2.     if (Object.prototype.hasOwnProperty.call(man, i)) { // filter  
  3.         console.log(i, ":", man[i]);  
  4.     }  
  5. }  
这样的好处就是你可以避免命名冲突,当man已经定义了一个hasOwnProperty.属性;
为了避免在Object对象身上很长属性查找(Object.prototype.hasOwnProperty),我们可以使用要给局部变量去缓存一下;
[javascript]  view plain  copy
  1. var i, hasOwn = Object.prototype.hasOwnProperty;  
  2. for (i in man) {  
  3.     if (hasOwn.call(man, i)) { // filter  
  4.         console.log(i, ":", man[i]);  
  5.     }  
  6. }  

扩展内置对象(不建议)(Augmenting Built-in Prototypes)

扩展构造方法的原型(prototype)属性是一种给对象添加方法的有力方式,但有时候有力过了头;
扩展内置对象的原型是非常诱人的,但是这会严重导致可维护性,让你代码没有可预见性,其他的开发人员可能只期望内置方法工作正常而不是你添加的;
你添加到原型上的属性,也会出现在for-in循序中,如何你没有使用hasOwnProperty()方法,这会导致混淆;
因此,最好不要扩展内置的原型,除非你已经写在文档中并在团队中告知了这个改变。

[javascript]  view plain  copy
  1. if (typeof Object.protoype.myMethod !== "function") {  
  2.     Object.protoype.myMethod = function() {  
  3.         // implementation...  
  4.     };  
  5. }  

swithch模式(switch Pattern)

你可以提高switch语句的可读性和健壮性通过下面的这种模式:
[javascript]  view plain  copy
  1. var inspect_me = 0,  
  2. result = '';  
  3. switch (inspect_me) {  
  4. case 0:  
  5.     result = "zero";  
  6.     break;  
  7. case 1:  
  8.     result = "one";  
  9.     break;  
  10. default:  
  11.     result = "unknown";  
  12. }  
1.缩进每一个case和switch位置;2.每一个case都有一个明显的break;3:在switch结束的时候有一个default:确保始终有一个结果即使一个case没匹配到;

防止隐式的类型转换(Avoiding Implied Typecasting)

JavaScript会隐式的做类型转换,当你比较他们的时候;这也是为什么我们可以做这些比较,好比 false == 0 or "" == 0 返回true;
为了避免这种有隐式类型转换导致的混淆,我们通常使用 === 和 !== 操作符去比较表达式的值或者类型;
[javascript]  view plain  copy
  1. var zero = 0;  
  2. if (zero === false) {  
  3.     // not executing because zero is 0, not false  
  4. }  
  5. // antipattern  
  6. if (zero == false) {  
  7.     // this block is executed...  
  8. }  
也有人认为当使用 == 是足够的的时候使用 === 是多余的,好比你在使用typeof的时候,明确知道它会返回一个字符串,所以没有理由去使用严格的相等(===);
然而,JSLint要求使用严格相等,它会让代码看起来更有一致性并且减少阅读代码时候的脑力(这里的 == 是故意的,还是疏忽呢?)。

 避免使用eval()(Avoiding eval())

如果你在有的代码中发现使用eval(),请记住这个咒语"“eval() is evil";这个函数会接受一个字符串参数,并把它作为JavaScript代码来执行;
当代码不是在运行时才能确定下来,就没有理由使用eval();
如果代码是在运行时动态生成的,通常也有更好的方法去实现这个目标而不是使用eval();
举例,仅仅使用方括号去动态的访问的属性更好而且更简单;

[javascript]  view plain  copy
  1. // antipattern  
  2. var property = "name";  
  3. alert(eval("obj." + property));  
  4. // preferred  
  5. var property = "name";  
  6. alert(obj[property]);  
使用eval()也会有安全性的影响,因为你有可能执行被篡改的代码(比如从网上获取到的),有一个常见的不好的模式就是处理Ajax请求的JSON反馈(response);
在这些情况下,最好使用浏览器内置的方法去解析JSON对象,确保是安全合法的;如果浏览器没有提供JSON.parse()方法,可以使用从json.org下载的类库;

非常重要的要记住向setInterval(),setTimeout(),Function()方法的传入字符串和用eval()是非常相像的(很大程度上),因此这需要避免;
事实上,JavaScript还是去解析你传入的字符串并且当做代码执行;
[javascript]  view plain  copy
  1. // antipatterns  
  2. setTimeout("myFunc()", 1000);  
  3. setTimeout("myFunc(1, 2, 3)", 1000);  
  4. // preferred  
  5. setTimeout(myFunc, 1000);  
  6. setTimeout(function() {  
  7.     myFunc(1, 2, 3);  
  8. },  
  9. 1000)  
使用new Function()构造函数和eval()类似,要格外小心;这是个强大的构造方法但是又经常被滥用;如果你就绝对必须要使用eval(),你可以考虑用new Function() 替代;
有一个潜在的好处就是在new Function()中解析的代码,会在封装一个函数环境中,所以任何使用var声明的变量被解析但不会自动成为一个全局变量;
另一种防止自动产生变量的方法就是将eval()包含在一个立即执行的匿名函数中;
在下面这段代码中,只有 un 会变成一个全局变量;
[javascript]  view plain  copy
  1. console.log(typeof un); // "undefined"  
  2. console.log(typeof deux); // "undefined"  
  3. console.log(typeof trois); // "undefined"  
  4. var jsstring = "var un = 1; console.log(un);";  
  5. eval(jsstring); // logs "1"  
  6. jsstring = "var deux = 2; console.log(deux);";  
  7. new Function(jsstring)(); // logs "2"  
  8. jsstring = "var trois = 3; console.log(trois);"; (function() {  
  9.     eval(jsstring);  
  10. } ()); // logs "3"  
  11. console.log(typeof un); // "number"  
  12. console.log(typeof deux); // "undefined"  
  13. console.log(typeof trois); // "undefined"  
eval()和Function构造方法另一个不同之处就是eval()能够影响到作用域链,而Function更像一个沙箱,封闭的,不会对外界产生很大影响;
无论你在什么地方运行Function,它只能看到全局作用域,所以就不会有太多局部变量的干扰;
就像下面一样,eval()能访问和修改它外部作用域的变量,而Function不能(使用new Function()和Function是一样的);
[javascript]  view plain  copy
  1. (function() {  
  2.     var local = 1;  
  3.     eval("local = 3; console.log(local)"); // logs 3  
  4.     console.log(local); // logs 3  
  5. } ()); (function() {  
  6.     var local = 1;  
  7.     Function("console.log(typeof local);")(); // logs undefined  
  8. } ());  
eval()就是执行传入的代码,和写在该处的效果一模一样;而Function则是声明了一个新的函数(在全局作用域中定义的),并且该处调用它;

使用parseInt()进行数字转换(Number Conversions with parseInt())

使用parseInt()可以将一个字符串转换成一个整数,这个方法接受一个基数作为第二个参数,通常都会被忽略,但这是不应该的;
当传入的字符串以0开头的时候就会产生问题,以0开头的字符串在ES3中会被认为是8进制,但在ES5中改变了,为了防止这种易变的不可预料的结果,应该制定一个基数;
[javascript]  view plain  copy
  1. var month = "06",  
  2. year = "09";  
  3. month = parseInt(month, 10);  
  4. year = parseInt(year, 10);  
在这个例子中,如果你忽略了基数,好比parseInt(year),那么返回的是0,因为“09”被假定为8进制,但“09”不是一个合法的8进制数
还有其他将字符串转换为整数的方法
[javascript]  view plain  copy
  1. +"08" // result is 8  
  2. Number("08"// 8  
这些通常都比parseInt()快,就像parseInt()名字一样,parse(解析)而不是简单的转换(convert),但是如果你想把“08 hello”传递给parseInt()将会返回一个整数,但是其它的都会返回NaN。

编码规范(Coding Conventions)

建立和遵循一个编码规则是很重要的,这样会让你的代码变的更有一致性,预见性,可读性,更容易去理解
当一个新成员加入时,可以通过阅读规范快速加入开发,理解其他人阅读的代码;
网上有很多人争论什么样的规范好,什么的规范不好;
重点:建立并遵循一个编码规范,比规范的细节和内容重要的多

缩进(Indentation)

代码如果没有缩进是很难(impossible)被读懂的,更杯具的事是前后不一致的缩进,因为它看起来遵循一个规范,到可能读起来一路的疑惑;
标准化缩进是非常重要的;
一些程序猿喜欢用tabs键(制表符)缩进,因为任何人能设置他们的编辑器用他们喜欢空格数去显示tabs,还有一些人喜欢用空格-通常是4个;
这都是无关紧要的,只要团队中的每个人都遵循相同的规范就行;JSLint使用4个空格缩进;
但是什么需要我们应该缩进呢?非常简单--任何在大括号中的东西都应该被缩进;
这意味着函数体,循环(do,while,for,for-in)体,if语句,siwtch语句和对象字面量(literal notation)中的属性
(bodies of functions, loops (do, while, for, for-in), ifs, switchess, and object properties in the object literal notation)都需要缩进;
[javascript]  view plain  copy
  1. function outer(a, b) { //我的的的确确缩进了,难看不是我的错  
  2.     var c = 1,  
  3.     d = 2,  
  4.     inner;  
  5.     if (a > b) {  
  6.         inner = function() {  
  7.             return {  
  8.                 r: c - d  
  9.             };  
  10.         };  
  11.     } else {  
  12.         inner = function() {  
  13.             return {  
  14.                 r: c + d  
  15.             };  
  16.         };  
  17.     }  
  18.     return inner;  
  19. }  

大括号(Curly Braces)

大括号应该推荐被使用,即使在可选的情况下;
技术上,如果你只有一条语句在if语句或者for循环中,大括号不是必需的,但是你不应该省略它们,使用大括号会让你的代码前后更加一致,更容易修改;
假设我们有一个for循环,并且只有一条语句,我们可以忽略大括号并且没有任何语法错误;
[javascript]  view plain  copy
  1. // bad practice  
  2. for (var i = 0; i < 10; i += 1)  
  3.     alert(i);  
但是,稍后如果你在循环体中又加入一行会怎样呢?
[javascript]  view plain  copy
  1. // bad practice  
  2. for (var i = 0; i < 10; i += 1)  
  3.     alert(i);  
  4. alert(i + " is " + (i % 2 ? "odd" : "even"));  
第二个alert就会在for循环体之外,尽管这个缩进可能会欺骗你;
最好是站在长远考虑,始终好使用大括号,即使只有一条语句;
[javascript]  view plain  copy
  1. // better  
  2. for (var i = 0; i < 10; i += 1) {  
  3.     alert(i);  
  4. }  
if也是类似的:
[javascript]  view plain  copy
  1. // bad  
  2. if (true)  
  3.     alert(1);  
  4. else  
  5.     alert(2);  
  6. // better  
  7. if (true) {  
  8.     alert(1);  
  9. else {  
  10.     alert(2);  
  11. }  

大括号的位置(Opening Brace Location)

程序猿常常都会选择从哪里开始写括号——在同一行,还是另起一行?
[javascript]  view plain  copy
  1. if (true) {  
  2.     alert("It's TRUE!");  
  3. }  
  4.   
  5. if (true)  
  6. {  
  7.     alert("It's TRUE!");  
  8. }  
在这个特定的例子中,括号在什么地方都没有影响,但有些情况下大括号的位置不同会导致不同的结果;
这是因为分号插入机制(semicolon insertion mechanism),JavaScript中不以分号结束的语句也不会有错
这种行为会导致一个问题,当函数返回一个对象字面量时并且大括号在下一行才开始;
[javascript]  view plain  copy
  1. // warning: unexpected return value  
  2. function func() {  
  3.     return {  
  4.         name: "Batman"  
  5.     };  
  6. }  
如果你想这个函数返回一个对象——有个一个name属性,你会非常吃惊!因为分号插入机制,这个函数会返回undefined,上面的代码等同于:
[javascript]  view plain  copy
  1. // warning: unexpected return value  
  2. function func() {  
  3.     return undefined;  
  4.     // unreachable code follows...  
  5.     {  
  6.         name: "Batman"  
  7.     };  
  8. }  
在函数结束的时候,要使用大括号的话,一定要将大括号放在同一行;
[javascript]  view plain  copy
  1. function func() {  
  2.     return {  
  3.         name: "Batman"  
  4.     };  
  5. }  

空格(White Space)

空格的使用也会有利于提高的代码的可读性和一致性;
在写英语的时候,你可以在逗号和句号后使用空格,在JavaScript中你也可以遵循相似的逻辑;
合适放空格的地方:
[javascript]  view plain  copy
  1. for (var i = 0;i < 10;i += 1) {...}  
  2. for (var i = 0,max = 10; i < max; i += 1) {...}  
  3. var a = [1, 2, 3];  
  4. var o = {a:1, b: 2};  
  5. myFunc(a, c)  
  6. function myFunc() {}  
  7. var myFunc = function() {};  
其它适合使用空格的地方——分割所有操作符和他们的操作数,就是你可以在+, -, *, =, <, >, <=, >=, ===, !==,&&, ||, +=之前或之后使用空格;
[javascript]  view plain  copy
  1. // generous and consistent spacing  
  2. // makes the code easier to read  
  3. // allowing it to "breathe"  
  4. var d = 0,  
  5. a = b + 1;  
  6. if (a && b && c) {  
  7.     d = a % c;  
  8.     a += d;  
  9. }  
  10. // antipattern  
  11. // missing or inconsistent spaces  
  12. // make the code confusing  
  13. var d = 0,  
  14. a = b + 1;  
  15. if (a && b && c) {  
  16.     d = a % c;  
  17.     a += d;  
  18. }  

命名规范(Naming Conventions)

另一种让你代码更有预见性和可维护性方法就是采用命名规范,这意味着给你的变量和函数起名要一致;
接下来的集中命名规范,你可以采用可以改造成你喜欢的;
重点:建立并遵循一个命名规范,比规范的细节和内容重要的多

常见的命名方式有骆驼命名法:

大写骆驼命名法,变量或函数名每个单词首字母大写,一般适用于构造函数与其它普通方法以示区别;

小写骆驼命名法,变量或函数名第一个单词首字母小写其余单词首字母大写,一般用于普通方法;

其它命名模式(Other Naming Patterns)

一些程序猿喜欢用一个命名规范去表示或替代语言的功能;
例如:在JavaScript中没有办法声明一个常量(虽然有一些内置的常量如:Number.MAX_VALUE),所以程序猿已经采用了这种命名规范,
用全大写去命名变量——常量(在程序运行过程中不应该被改变的变量);
[javascript]  view plain  copy
  1. var PI = 3.14,  
  2. MAX_WIDTH = 800;  
还有用命名规范去表示私有成员,虽然你使用JavaScript实现真正的私有成员,但是程序猿发现使用一个前缀去标识私有属性和方法更加简单;
[javascript]  view plain  copy
  1. var person = {  
  2.     getName: function() {  
  3.         return this._getFirst() + ' ' + this._getLast();  
  4.     },  
  5.     _getFirst: function() {  
  6.         // ...  
  7.     },  
  8.     _getLast: function() {  
  9.         // ...  
  10.     }  
  11. }  
在这个例子中,getName()是一个公共方法,_getFirst()和_getLast()有意作为私有方法;
还有一些_private的变种:
1.在变量尾部加下划线表示私有,好比name_ 和 getElements_();
2.用一个下划线表示_protected属性,两个下划线表示__private;
在火狐中,有一些内部的属性虽然不是语言的一部分,但是是可访问的,它们就是有两个下划线前缀和两个下划线后缀,就像__proto__ 和 __parent__

写注释(Writing Comments)

你不得不去注释你的代码,即使除了你不太可能没有其他人接触这些代码;程序猿常常深入一个问题而且对代码的行为了然于胸,但当你一个星期再回头看自己写的代码,你会很难记起代码是怎么回事;
你不必去注释每一个变量每一行代码,但通常需要去注释所有的方法,它们的参数和返回值,和一些有趣或者不寻常的算法或技术;
注释可以提醒其他人函数的功能,其他人通过阅读注释就可以知道代码是干什么的而不用去阅读全部的代码;

写API文档(Writing API Docs)

绝大多数程序猿认为写帮助文档是无聊和不值得的工作,但那绝对是错误的;
API文档可以通过代码中的注释自动生成,这种方式你可以获得文档却不用单独的去写;
大部分程序猿发现这个想法是很有吸引力的,因为通过特殊的关键字和格式化命令自动生成可读的文档看起来更像编程;

传统的API文档是来自Java(javadoc,jdk的一部分),但是相同的想法被应用于许多其他语言;
自动生成文档的过程:
1.写特定格式的代码块;
2.运行工具去解析代码和注释;
3.发布结果,通常是html页面;
[javascript]  view plain  copy
  1. /** 
  2. * Reverse a string 
  3. * 
  4. * @param {String} input String to reverse 
  5. * @return {String} The reversed string 
  6. */  
  7. var reverse = function(input) {  
  8.     // ...  
  9.     return output;  
  10. };  

[javascript]  view plain  copy
  1. /** 
  2. * Constructs Person objects 
  3. * @class Person 
  4. * @constructor 
  5. * @namespace MYAPP 
  6. * @param {String} first First name 
  7. * @param {String} last Last name 
  8. */  
  9. MYAPP.Person = function(first, last) {  
  10.     /** 
  11. * Name of the person 
  12. * @property first_name 
  13. * @type String 
  14. */  
  15.     this.first_name = first;  
  16.     /** 
  17. * Last (family) name of the person 
  18. * @property last_name 
  19. * @type String 
  20. */  
  21.     this.last_name = last;  
  22. };  
  23. /** 
  24. * Returns the name of the person object 
  25. * 
  26. * @method getName 
  27. * @return {String} The name of the person 
  28. */  
  29. MYAPP.Person.prototype.getName = function() {  
  30.     return this.first_name + ' ' + this.last_name;  
  31. };  

压缩JavaScript脚本(Minify…In Production)

压缩JavaScript脚本就是去除空格,注释和其他没有意义的部分去减少JavaScript的文件的大小,有利于网络传输;
这些都是由工具完成,除了删除空格换行和注释之外,压缩工具也会重命名局部变量(当这样是安全的时候)为很短的名字;
压缩工具一般会重命名全局变量,因为重命名全局变量可能会破坏代码,所以好的做法就是尽可能的使用局部变量;
当你引用一个全局变量两次以上,最好将它赋值给一个局部变量,这样会加快查找速度当处理变量的名字时候,
这样会加快代码执行速度,压缩时减小JavaScript文件体积,网络传输更快。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值