A-A+

如何书写高质量js代码

2016年04月01日 JavaScript 暂无评论 阅读 113 次
  1. >>>>> 避免全局变量,因为全局变量容易发生名称上的冲突,可维护性不好。
  2.     1,使用命名空间
  3.     2,使用闭包
  4.     3,在函数内部使用var声明
  5. function sum(x,y){
  6.      result = x + y;
  7.      return result;
  8. }
  9. alert(window.result); 所有全局变量都是window的一个属性
  10. 如果调用sum函数,就会自动多一个result的全局变量,在javascript中,所有未声明的变量都会成为全局对象的一个属性
  11. 使用链式方法声明变量
  12. function foo(){
  13.      var a=b=0;
  14.      ....
  15. }
  16.  虽然a是局部变量,但这样b就变成全局变量,这是因为,
  17.  b = 0这个表达式先执行,执行的时候b并没有被声明,所以b就成为了全局变量,然后返回这个表达式的值0,给声明了的变量a
  18.  如果你已经声明了var a,b这样是可以的。
  19.  另外一个避免使用全局变量的原因是考虑到程序的可移植性。
  20.  如果你想让你的代码在不同的环境中都可以工作,那么使用全局变量就很可能会与新的系统中的全局变量冲突(或许在之前的系统中没有问题)。
  21. 使用var声明的全局变量和没有使用var生成的全局变量还有一个区别在于删除:
  22. 使用var声明创建的全局变量不能被删除
  23. 没有使用var声明的全局变量可以被删除
  24. 这说明没有使用var声明生成的全局变量不是真正的变量,他们只是全局对象的属性。属性可以通过delete删除,但是变量不行
  25. 在你的代码的顶部只是用一个var关键字,会有以下的好处:
  26.  对于所有需要的变量,在一个地方就可以全部看到
  27.  避免使用一个未定义的变量
  28.  帮助你记忆声明的变量,减少全局变量
  29.  更精简的代码
  30.  书写很简单
  31. 通过一个var和逗号来声明多个变量。
  32. 在声明的时候给变量赋默认值也是不错的做法,可以避免一些逻辑错误,提高代码的可读性。而后你阅读的代码的时候也可以根据变量的默认值来方便的猜测变量的用途。
  33. JavaScript允许你在函数内部有多个var语句,但是却都表现的如同在函数的顶部声明一样。
  34. 这个特性在你使用一个变量然后在后面又声明了这个变量时会导致一些奇怪的逻辑问题。
  35. 对于JavaScript来说,只要变量在同一个作用域,那么就认为是声明了的,就算是在var语句之前使用也一样。看看这个例子:
  36. myname = "global"// global variable  
  37. function func() {
  38.     alert(myname); // "undefined"  
  39.     var myname = "local";
  40.     alert(myname); // "local"  
  41. }
  42. func();
  43. 在这个例子中,或许你期望第一次会弹出global,第二次弹出local。
  44. 因为第一次的时候没有还没有使用var声明myname,这是应该是全局变量的myname,第二次声明了,然后alert之后应该是local的值。
  45. 而事实上不是这样的,只要你在函数中出现了var myname,那么js就认为你在这个函数中声明了这个变量,
  46. 但是在读取这个变量的值的时候,因为var语句还没有执行,所以是undefined,很奇怪的逻辑吧。上面的代码相当于:
  47. myname = "global"// global variable  
  48. function func() {
  49.    var myname; // same as -> var myname = undefined;  
  50.    alert(myname); // "undefined"  
  51.    myname = "local";
  52.    alert(myname); // "local"  
  53. }
  54. func();
  55. 在代码的解析中,分两个步骤,第一步先处理变量函数的声明,
  56. 这一步处理整个代码的上下文。第二步就是代码的运行时,创建函数表达式以及未定义的变量。
  57. >>>>>  for循环
  58. for (var i = 0; i < myarray.length; i++) {
  59.    // do something with myarray[i]  
  60. }
  61. 这样每次迭代都会计算数组的长度。尤其在这个参数不是一个数组而是一组HTML元素的时候会降低你的程序的性能。
  62. for (var i = 0, max = myarray.length; i < max; i++) {
  63.    // do something with myarray[i]  
  64. }
  65. 先保留长度,不用每次都计算。
  66. 在查找HTML元素集合的时候,缓存参数长度可以带来可观的性能提升,Safari下面提高两倍的速度,在IE7下面提高190倍的速度。
  67. 需要注意的是,当你需要操作修改DOM元素的数量的时候,你肯定希望这个值是随时更新的而不是一个常量。
  68. 使用下面的单一var模式,你也可以把var提到循环之外:
  69. function looper() {
  70.    var i = 0,
  71.         max,
  72.         myarray = [];
  73.    // ...  
  74.    for (i = 0, max = myarray.length; i < max; i++) {
  75.       // do something with myarray[i]  
  76.    }
  77. }
  78. 这个模式可以增强整个代码的连续性,但是不好的一点是当你重构代码的时候复制粘贴就没那么容易了。
  79. 例如:如果你想在其他函数中也使用这个循环,那你需要确定在新的函数中处理好了i和max(或许还需要删掉这个)。
  80. 这个函数还有两个点可以优化的:
  81. 可以少一个变量(不需要max)
  82. 递减到0,一个数字与0比较比这个数字与另外一个数字比较更快
  83. 所以就可以写为:
  84. var i, myarray = [];
  85. for (i = myarray.length; i--;) {
  86.    // do something with myarray[i]  
  87. }
  88. 针对第二点:
  89. var myarray = [],
  90.     i = myarray.length;
  91. while (i--) {
  92.    // do something with myarray[i]  
  93. }
  94.   🙂 哟西。
  95. >>>>>>> for in 循环 for-in循环用来迭代非数组的对象。使用for-in循环通常也成为枚举。
  96. 从技术上来说,你也可以用for-in来循环数组,因为数组也是对象,但是不推荐。如果数组有一些自定义的扩展函数,
  97. 那么就会出错。另外,对象属性的顺序在for-in循环中也是不确定的。所以最好还是用普通的循环来循环数组用for-in来循环对象
  98. 在循环对象的过程中,使用hasOwnProperty()方法来检验是对象本身的属性还是原型链上的属性很重要。
  99. 看看下面的这个例子。
  100. // the object  
  101. var man = {
  102.    hands: 2,
  103.    legs: 2,
  104.    heads: 1
  105. };
  106. // somewhere else in the code  
  107. // a method was added to all objects  
  108. if (typeof Object.prototype.clone === "undefined") {
  109.    Object.prototype.clone = function () {};
  110. }
  111. 在这个例子中,我们有一个简单的称作man的对象字面量。在其他man定义之前或之后的地方,对象原型有一个很有用的clone()方法。
  112. 因为原型链的原因,所有的对象都自动获得了这个方法。
  113. 为了在枚举man对象的时候出现clone方法,你需要使用hasOwnProperty方法来区别。如果没有区别来自原型链的方法,那么就会有一些意想不到的事情发生:
  114. // 1.  
  115. // for-in loop  
  116. for (var i in man) {
  117.    if (man.hasOwnProperty(i)) { // filter  
  118.       console.log(i, ":", man[i]);
  119.    }
  120. }
  121. /* result in the console 
  122. hands : 2 
  123. legs : 2 
  124. heads : 1 
  125. */
  126. // 2.  
  127. // antipattern:  
  128. // for-in loop without checking hasOwnProperty()  
  129. for (var i in man) {
  130.    console.log(i, ":", man[i]);
  131. }
  132. /* 
  133. result in the console 
  134. hands : 2 
  135. legs : 2 
  136. heads : 1 
  137. clone: function() 
  138. */
  139. 另外一种使用方法如下:
  140. for (var i in man) {
  141.    if (Object.prototype.hasOwnProperty.call(man, i)) { // filter  
  142.       console.log(i, ":", man[i]);
  143.    }
  144. }
  145. 这样写的好处是可以防止man重新定义了hasOwnProperty方法导致的冲突。如果不想写这么长的一串,你也可以这样:
  146.  var i, hasOwn = Object.prototype.hasOwnProperty;
  147. for (i in man) {
  148.     if (hasOwn.call(man, i)) { // filter  
  149.         console.log(i, ":", man[i]);
  150.     }
  151. }
  152. 严格意义上讲,不适用hasOwnProperty也不是什么错误。根据任务的难度和你对代码的自信程度,你也可以不用这个直接循环。
  153. 但是当你不确定的时候,最好还是使用这个方法检测一下。
  154. 另外一种格式上的改变(不会通过jsLint的检查),去掉for的大括号,然后把if放在同一行。这样做的好处可以让循环体更加突出,缩进也就少一些:
  155. // Warning: doesn't pass JSLint  
  156. var i, hasOwn = Object.prototype.hasOwnProperty;
  157. for (i in man) if (hasOwn.call(man, i)) { // filter  
  158.     console.log(i, ":", man[i]);
  159. }
  160. >>>>>>> 不要扩展内建的原型
  161. 扩展原型的构造函数,可以提供一些很强大的功能,但是有时候他太强大了。
  162. 有时候你会去扩展Object(),Array(),Fucntion()的原型方法,这样会导致可维护性的问题,因为这会让你的代码的移植性变差。其他的开发人员使用你的代码的时候,可能只需要原生的方法,并不需要额外的功能。
  163. 另外,你添加进去的方法,如果在循环的时候没有使用hasOwnProperty方法就会被遍历出来,这会让人很迷惑。
  164. 所以,最好还是不要扩展基本的对象。除非是下面的情况:
  165. 你确定在将来根据ECMAScript规范,浏览器会添加相应的原型方法,那么是可以的,你只不过是提前实现了这个功能。
  166. 你确定的你要实现的方法不存在–或许有时候在代码的其他的地方实现了,或者有的浏览器支持,这都是不行的。
  167. 有非常清晰的文档,并且与团队成员沟通过
  168. 如果在这些情况之下,那么你就可以添加,最好是下面这种形式:
  169. if (typeof Object.protoype.myMethod !== "function") { 先判断是否存在
  170.    Object.protoype.myMethod = function () {
  171.       // implementation...  
  172.    };
  173. }
  174. >>>>>>>>>>>>>>> switch模式
  175. 按照下面的风格写switch的话,可以提高你的代码可读性和健壮性:
  176. var inspect_me = 0,
  177.     result = '';
  178. switch (inspect_me) {
  179. case 0:
  180.    result = "zero";
  181.    break;
  182. case 1:
  183.    result = "one";
  184.    break;
  185. default:
  186.    result = "unknown";
  187. }
  188. 需要注意下面几个方面:
  189. caseswitch对齐。
  190. case的内容缩进
  191. 每一个case之后都有一个清晰的break
  192. 避免顺序往下执行case,非要如此的话,文档一定要写清楚
  193. 最后使用default,保证在没有命中case的情况下也有反馈
  194. >>>>>>>>>>>>>>>>>>避免隐藏的类型转换
  195. Javascript在你比较两个变量的时候会进行类型的转换,这就是为什么 false == 0或者”" == 0会返回true。  
  196.   
  197. 为了避免这种隐藏的类型转换带来的迷惑,最好使用===或者!==操作符来比较:  
  198.   
  199. var zero = 0;  
  200. if (zero === false) {  
  201.    // not executing because zero is 0, not false  
  202. }   
  203.    
  204. // antipattern  
  205. if (zero == false) {  
  206.    // this block is executed...  
  207. }  
  208.   
  209. >>>>>>>>>>>>>>>>>>>>>避免使用eval()  
  210. 如果你在你的代码中使用eval(),那么要记住”eval() is evil”。这个方法会将传入的字符串当做js代码来执行。  
  211. 如果代码是在运行前就确定的,那么没有必要使用eval()。如果代码是在运行时动态确定的,那么也有其他更安全的办法。例如使用方括号形式访问元素的属性:  
  212.   
  213. // antipattern  
  214. var property = "name";  
  215. alert(eval("obj." + property));    
  216.    
  217. // preferred  
  218. var property = "name";  
  219. alert(obj[property]);  
  220.   
  221. 使用eval()还有安全问题,比如你运行网络上的一段代码,而这段代码又被别人篡改了。  
  222. 在处理Ajax请求返回的JSON数据的时候,最好还是使用浏览器内建的处理方法,如果对于低端的浏览器不支持的,可以从JSON.org上下载对应的处理库。  
  223.   
  224.   
  225. 另外还要记住使用setTimeout、setInterval以及Function的构造函数的是,传入的字符串的参数,js的处理方法跟eval()类似,所以也要注意。  
  226. 因为,js会把你传入的字符串解析执行:  
  227. // no good   
  228. setTimeout("myFunc()", 1000);  
  229. setTimeout("myFunc(1, 2, 3)", 1000);    
  230.    
  231. // good   
  232. setTimeout(myFunc, 1000);  
  233. setTimeout(function () {  
  234.    myFunc(1, 2, 3);  
  235. }, 1000);  
  236.   
  237. 使用Function的构造函数,跟eval()差不多,也要注意。这是个非常有用的功能,但是常常被错用。如果你必须使用eval(),  
  238. 那么可以考虑new一个Function来替代。另外的一个好处就是,使用Function的构造函数,  
  239. 函数的作用域在本方法内,这样你使用var声明的变量就不会变成全局的。  
  240. 另外一个防止eval()生成全局变量的办法就是使用匿名函数  
  241. 看看下面这个例子,只有un变量最终是全局的:  
  242.   
  243. console.log(typeof un); // "undefined"  
  244. console.log(typeof deux); // "undefined"  
  245. console.log(typeof trois); // "undefined"    
  246.    
  247. var jsstring = "var un = 1; console.log(un);";  
  248. eval(jsstring); // logs "1"    
  249.    
  250. jsstring = "var deux = 2; console.log(deux);";  
  251. new Function(jsstring)(); // logs "2"    
  252.    
  253.  jsstring = "var trois = 3; console.log(trois);";  
  254.  (function () {  
  255.     eval(jsstring);  
  256.  }()); // logs "3"    
  257.    
  258.  console.log(typeof un); // number  
  259.  console.log(typeof deux); // undefined  
  260.  console.log(typeof trois); // undefined  
  261.    
  262.  new Function 一般是这么用的, var a = new Function("a","alert(a)");  
  263. 一般前面几个是参数,最后一个是函数体。要注意的是函数体是必选项,参数是可选项。一般用在动态定义函数体。  
  264.   
  265. eval()和Function构造函数的另一个区别就是eval()会影响到作用域,而Function则相当于一个沙盒。例如:  
  266.    
  267.  (function () {  
  268.    var local = 1;  
  269.    eval("local = 3; console.log(local)"); // logs 3  
  270.    console.log(local); // logs 3  
  271. }());    
  272.    
  273. (function () {  
  274.    var local = 1;  
  275.    Function("console.log(typeof local);")(); // logs undefined  
  276.  }());
标签:

给我留言

Copyright © web前端技术开发个人博客 保留所有权利  京ICP备14060653号 Theme  Ality

用户登录