Javascript 高质量编写

一:团队合作避免JS冲突

脚本中的变量随时存在冲突的风险,
1.   解决办法---用匿名函数将脚本包起来,让变量的作用域控制在匿名函数之内
如:

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript">  
  2.      (function(){  
  3.             var a=123,b="12121";  
  4.        })();  
  5. </script>  
  6.  .....  
  7.  <script type="text/javascript">  
  8.      (function(){  
  9.             var a=123,b="asdasa";  
  10.        })();  
  11.  </script>  
[javascript]  view plain  copy
 print ?
  1.   

此番改善之后,匿名function里面的变量作用域不再是window,而是局限在函数内。

2.  有时各个函数之间变量又要进行通讯,故又要改善(利用全局作用域)
如:

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript"var str; </script>  
  2.       <script type="text/javascript">  
  3.     (function(){  
  4.            var a=123,str = b = "12121";  
  5.       })();  
  6. </script>  
  7. .....  
  8. <script type="text/javascript">  
  9.     (function(){  
  10.            var a=123,b=str;  
  11.       })();  
  12. </script>  

3.  但如此一来,工程变大,全局变量未免过多难以管理,(利用hash对象作为全局变量)
如:

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript"var GLOBAL={}; </script>  
  2.  <script type="text/javascript">  
  3.      (function(){  
  4.             var a=123,b = "12121";  
  5.         GLOBAL.str = a;  
  6.         GLOBAL.str2 = b;  
  7.        })();  
  8.  </script>  
  9.  .....  
  10.  <script type="text/javascript">  
  11.      (function(){  
  12.             var a=GLOBAL.str;  
  13.             var b = GLOBAL.str2;  
  14.        })();  
  15.  </script>  


4.   不过就算如此,如果工程变大,不同工程师完成不同的函数,变量难免也会冲突,
 故又继续改善(GLOBAL 属性不是直接挂在GLOBAL属性对象上,而是挂在此命名函数的命名空间下)

 如:

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript"var GLOBAL={}; </script>  
  2.     <script type="text/javascript">  
  3.      (function(){  
  4.             var a=123,b = "12121";  
  5.             GLOBAL.A = {};  
  6.             GLOBAL.A.str = a;  
  7.             GLOBAL.A.str2 = b;  
  8.        })();  
  9.     </script>  
  10.  .....  
  11.  <script type="text/javascript">  
  12.      (function(){  
  13.               
  14.             var a=GLOBAL.A.str;  
  15.             var b = GLOBAL.A.str2;  
  16.        })();  
  17.  </script>  

如此种种,函数内属性越复杂,又可以进一步扩展GLOBAL挂载方式.....
如 GLOBAL.A={};
GLOBAL.A.CAT={};
GLOBAL.A.DOG={};
GLOBAL.A.DOG.name="aa";
GLOBAL.A.CAT.name="bb";


5.  进一步,又可以给此命名空间定义方式封装成函数,方便以后调用:
如:

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript">  
  2.       var GLOBAL={};  
  3.       GLOBAL.namespace = function(str){  
  4.       var arr=str.split("."), o = GLOBAL;  
  5.       for(i=(arr[0]="GLOBAL") ? 1 : 0 ; i<arr.length; i++){  
  6.           o[arr[i]] = o[arr[i]] || {};  
  7.         o = o[arr[i]];  
  8.         }  
  9.     }  
[javascript]  view plain  copy
 print ?
  1. </script>  
  2. <script type="text/javascript">  
  3.      (function(){  
  4.             var a=123,b = "12121";  
  5.         GLOBAL.namespace("A.CAT");  
  6.         GLOBAL.namespace("A.DOG");  
  7.         GLOBLA.A.CAT.name="aa";  
  8.         GLOBLA.A.DOG.name="bb";  
  9.         })();  
  10. </script>  




二:方便JS程序执行:
   
   1. 给程序一个统一的入口===== window.onload 或DOMReady 
  (先把所有函数定义部分放入 init函数中,最后再加载 init()即可使用
  
  如:在DOM节点加载进来之前就调用会出错

      

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript">  
  2.      alert(document.getElementById("test").innerHTML);  
  3. </script>  
  4.      <div id="test">hello ! </div>  


改善方式:  
  调换位置,在DOM节点加载进来之后再调用即可

    

[javascript]  view plain  copy
 print ?
  1. <div id="test">hello ! </div>  
  2.      <script type="text/javascript">  
  3.      alert(document.getElementById("test").innerHTML);  
  4.      </script>  


或者,使用window.onload事件,window对象会在网页内元素全部加载完毕之后才触发onload时间

   

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript">  
  2.     window.οnlοad= function(){  
  3.      alert(document.getElementById("test").innerHTML);  
  4.      }  
  5. </script>  
  6.      <div id="test">hello ! </div>  


但此种方式,仍有不足。
  window.onload要等到网页元素全部加载才进行
  而DOMReady则只要页面内所有DOM节点皆全部生成即可进行。
  DOMReady 方式原生JS并不支持,要使用第三方类库(JS框架)
jQuery的方式:
    $(document).ready(init);    // init() 是一个函数...
 $(function(){ ...});  /// 等等等等..
 
 当然了,我们也可以用原生JS模拟DOMReady ,事实上很简单,就是:

  

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript">  
  2.     function init(){  
  3.      alert(document.getElementById("test").innerHTML);  
  4.      }  
  5. </script>  
  6.      <div id="test">hello ! </div>  
  7.        
  8.     <script type="text/javascript">  
  9.     init();  
  10.     </script>  
  11.     </body>  


如此一来 body标签加载完成之前才调用函数,这种方式也不错!
 
 
 2.   CSS 文件与 JS 文件的位置
   因为JS是阻塞型的,所以一般” CSS放在页头,JavaScript放在页尾“
   (这样一来,网页先呈现给用户,再慢慢加载页面里面的脚本,减少页面空白的时间)
   
   
  
 三.  Javascript 分层概念
    一般来说,我们要把Javascript分层三层: base层 --> common层 --> page 层

 1. page层
  page 层提供统一的接口,可以在这里实现封装不同浏览器下Javascript的差异,依靠它来完成跨浏览器兼容的工作!
  还可以扩展Javascript语言底层提供的接口,以便提供出更多有用的接口(主要是为common page 层提供)
  
各种问题类举:

   <1> 在IE中,它只视DOM节点为childNodes中的一员,但在FireFox中,它会将包括空白.换行等文本信息在内的信息也当做childNodes的一员。
   未解决此问题,可用 document.all 方式处理,它是IE支持的属性,FireFox不支持
   代码如:

[html]  view plain  copy
 print ?
  1.                      <ul>  
  2.                         <li id="item1"></li>  
  3.    <li id="item2"></li>  
  4.    <li id="item3"></li>  
  5. </ul>  
  6. t;script type="text/javascript">  
  7.          var item1 = document.getElementById("item1");  
  8. var nextNode = item1.nextSibling;  
  9. if(document.all){  
  10.     while(true){  
  11.       if(nextNode.nodeType == 1){  
  12.          break;  
  13.          }  
  14.       else{  
  15.       if(nextNode.nextSibling){  
  16.         nextNode = nextNode.nextSibling;  
  17.         }  
  18.          else{  break;  }  
  19.         }  
  20.     }  
  21. }  
  22. alert(nextNode.id);  
  23. //  
  24. var item2 = document.getElementById("item2");  
  25. var nextNode = item2.nextSibling;  
  26. if(!document.all){  
  27.     while(true){  
  28.       if(nextNode2.nodeType == 1){  
  29.          break;  
  30.          }  
  31.       else{  
  32.       if(nextNode2.nextSibling){  
  33.         nextNode2 = nextNode2.nextSibling;  
  34.         }  
  35.          else{  break;  }  
  36.         }  
  37.     }  
  38. }  
  39. alert(nextNode2.id);  
  40.          </script>  


哦对,好像有点冗余,那就把这个功能封装成函数吧!
 如:

[html]  view plain  copy
 print ?
  1.                       <ul>  
  2.                          <li id="item1"></li>  
  3.        <li id="item2"></li>  
  4.        <li id="item3"></li>  
  5.     </ul>  
  6. <script type="text/javascript">  
  7.     function getNextNode(node){  
  8.       node = typeof node=="string" ? document.getElementById("node") : node;  
  9.       var nextNode = node.nextSibling;  
  10.       if(!nextNode)  
  11.          return NULL;  
  12.             
  13.     if(!document.all){  
  14.         while(true){  
  15.           if(nextNode.nodeType == 1){  
  16.              break;  
  17.              }  
  18.           else{  
  19.           if(nextNode.nextSibling){  
  20.             nextNode = nextNode.nextSibling;  
  21.             }  
  22.              else{  break;  }  
  23.             }  
  24.         }  
  25.     }  
  26.     return nextNode;  
  27.     }       //funciton over  
  28.       
  29.     var nextNode = getNextNode("item1");  
  30.     var nextNode2 = getNextNode("item1");  
  31.     alert(nextNode.id);  
  32.     alert(nextNode2.id);  
  33. </script>  


<2> 透明度的问题:
     IE下透明度是通过滤镜实现的,但在FireFox下透明度是通过CSS 的opacity属性实现的
   我们把它封装起来
   代码如:

[html]  view plain  copy
 print ?
  1.        <style type="text/css">  
  2.             #test1 { background:blue; height: 100px;}  
  3.      #test1 { background:blue; height: 100px;}  
  4. lt;/style>  
  5.      <div id="test1"></div>  
  6.      <div id="test2"></div>  
  7.        
  8. <script type="text/javascript">  
  9.            function setOpacity(node,level){         // level 为滤镜程度  
  10.                node = typeof node=="string" ? document.getElementById("node") : node;  
  11.             if(document.all){  
  12.                node.style.filter = 'alpha(opacitu= ' + level + ')';   
  13.                }  
  14.              else{  
  15.                 node.style.opacity = level / 100;  
  16.                 }  
  17.         }  
  18.           
  19.         setOpacity("test1",20);   //test1.style.filter = 'alpha(opacitu=20)';  
  20.         setOpacity("test2",80);   //test2.style.opacity = 0.8;  
  21. </script>  


<3> event 对象的问题:
    IE下 event对象是作为window的属性作用于全局作用域的,但在FireFox中 event对象是作为事件的参数存在的
    所以,为了兼容性,一般考虑用一个变量指向event对象,继而通过这个变量访问event对象
    不理解? 代码如下:

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript">  
  2.     var btn = document.getElementById("btn");  
  3.          btn.onclick = function(e){  
  4.      e  = window.event || e;  
  5.      // ..........接下来你就可以用e了, 比如 e.target 等  
  6. </script>  


另一方面,派生事件的对象在IE下是通过event对象的srcElement属性访问的
                              在FireFox下是通过event对象的target属性访问的
    如代码: 

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript">  
[javascript]  view plain  copy
 print ?
  1. var btn = document.getElementById("btn");  
  2. btn.onclick = function(e){  
  3. e  = window.event || e;  
  4. var el = e.srcElement || e.target;  
  5. alert(el.tagName);  
  6. /script>  


<4> 冒泡问题的处理
   首先理解概念---> 对于事件流,浏览器中的事件模型分为两种:捕获型和冒泡型事件
   事件的冒泡: Javascript对这种先触发子容器监听事件,后触发父容器监听事件的现象。
   事件的捕获: 即相反于冒泡(先父后子)
      比如代码中 <div id="wrapper">
                         <input type="button" value="click me" id="btn" />
                        </div>
   我们为id=wrapper绑定事件1,为id=btn绑定事件2,
   如此一来,我们的结果却是: 无论点哪里,触发的都是事件1 (因为事件2触发得很快就会迅速转变为事件1)
   
   为了解决,要阻止(对子容器)事件的冒泡机制:IE下通过设置event对象的cancelBubble 为true 实现
                                                  FireFox 通过调用event对象的stopPropagation方法实现
 
       封装成函数即是:

[javascript]  view plain  copy
 print ?
  1.                           <script type="text/javascript">  
  2.                      function stopPropagation(e){  
  3.                     e = window.event || e;  
  4.                     if(document.all){  
  5.                        e.cancelBubble = true;  
  6.                        }  
  7.                      else{  
  8.                        e.stopPropagation();  
  9.                        }  
  10.                     }  
  11.                     /调用  
  12.             wrapper.onclick = function(){  
  13.                        ..............  
  14.                        }  
  15.             btn.onlcick = function(e){  
  16.                       ................  
  17.                       stopPropagation(e);  
  18.                       }  
  19. </script>  


<5>事件监听的处理:
           可以简单地使用 onXXX 的方式, 
              如 btn.onclick = function(){ .......};
           但onXXX方法没有叠加作用,后面定义的onXXX会把前面的覆盖掉,为解决此类问题:
               IE下引入 attachEvent方法,FireFox 下引入addEventListener方法。
      IE下引入 detachEvent方法,FireFox 下引入removeEventListener方法。
      如:oSpan.detachEvent("onclick",fnClick);
          oSpan.removeEventListener("click",fnClick);
      好,我们就把这个功能封装一下:
               代码如: 

[javascript]  view plain  copy
 print ?
  1.          <script type="text/javascript">  
  2.        function on(node,eventType,handeler){  
  3. node = typeof node=="string" ? document.getElementById("node") : node;  
  4.             if(document.all){  
  5.       node.attachEvent("on"+ eventType,handeler);  
  6.         }  
  7.      else{  
  8.     node.addEventListener(eventType,handeler,false);   // 其中第三个参数:true--捕获型,false--冒泡型  
  9.         }  
  10.     }  
  11. var btn = document.getElementById("btn");  
  12. on(btn,"click",funciton(){  
  13.   alert(1);  
  14.   });  
  15.        on(btn,"click",funciton(){  
  16.  alert(2);  
  17.    });  
  18. </script>  


<6> 其他小功能:
     函数封装实现:
   

[javascript]  view plain  copy
 print ?
  1. <script type="text/javascript">  
  2.           function trim(ostr){           //trim() 去空格  
  3.              return ostr.replace(/^\s+|\s+$/g,"");  
  4.              }  
  5.           function isNumber(s){  
  6.              return !isNaN(s);  
  7.              }  
  8.           function isString(s){  
  9.              return typeof s === "string";  
  10.              }  
  11.           function isBoolean(s){  
  12.              return typeof s === "boolean";  
  13.              }  
  14.           function isFunction(s){  
  15.              return typeof s === "function";  
  16.              }  
  17.           function isNull(s){  
  18.              return s === null;  
  19.              }  
  20.           function isUndefined(s){  
  21.              return typeof s === "undefined";  
  22.              }  
  23.           function isEmpty(s){  
  24.              return /^\s*$/.test(s);  
  25.              }  
  26.           function isArray(s){  
  27.              return s instanceof Array;  
  28.              }  
  29.           function get(node){             // get() 代替ducument.getElementById()    ...alert(get("test1").innerHTML);  
  30.              node = typeof node=="string" ? document.getElementById("node") : node;  
  31.              return node;  
  32.              }  
  33.            function $(node){             // $() 代替ducument.getElementById()        ...alert($("test1").innerHTML);  
  34.              node = typeof node=="string" ? document.getElementById("node") : node;  
  35.              return node;  
  36.              }  
  37.     
  38.     
  39.   //  原生JS没有getElementByClassName,   那就给它实现一个呗...  
  40.   //   getElementByClassName函数接收3个参数,第一个参数为class名(必选),第二个为父容器,缺省值为body节点,第三个参数为DOM节点的标签名。  
  41.   // 函数 实现如下   
  42.            function getElementByClassName(str,root,tag){  
  43.                if(root){  
  44.                   root = typeof root=="string" ? document.getElementById("root") : root;  
  45.                   }  
  46.                   else{  
  47.                   root = document.body;  
  48.                   }  
  49.                 tag = tag || "*";  
  50.                 var els = root.getElementByTagName(tag),  arr=[];  
  51.                 for(var i=0,n=els.length; i<n ; i++){  
  52.                    for(var j=0,k=els[i].className.split(" "), l=k.length; j<l; j++){  
  53.                       if(k[k] == str){  
  54.                         arr.push(els[i]);  
  55.                         break;  
  56.                         }  
  57.                      }  
  58.                 }  
  59.                 return arr;  
  60.             }  
  61.               
  62.             //然后我们就可以直接调用啦..  
  63.             var aEls = getElementByClassName("a");  
  64.             var bEls = getElementByClassName("b");  
  65.             // .............  
  66.    </script>  


2.  common 层:
       common层本身依赖于base层提供的接口,common层提供的应该是相对更大的组件,供Javascript调用
  
 3.  page 层 
        就是具体的页面特设定啦... 
 
四: 编程的其他一些实用技巧:
   
     1.在遍历数组时对DOM监听事件,索引值将始终等于遍历结束后的值。
     如某个监听代码:

[javascript]  view plain  copy
 print ?
  1.                     <script type="text/javascript">  
  2.                //遍历数组,让tabMenus 监听click事件  (Tab 组件监听选项卡)  
  3.                 for(var i=0;i<tabMenus.length;i++){  
  4.     tabMenus[i].onclick = function(){  
  5.        //遍历数组,隐藏所有tabcontent  
  6.        for(var j=0;j<tabcontent.length;j++){  
  7.           tabcontent[j].style.display = "none";  
  8.           }  
  9.       //显示被点击的tabMenus 对应的tabcontent  
  10.       tabcontent[i].style.display = "block";  
  11.       }  
  12.      }  
  13. </script>  


              这样做之后,所有content将会隐藏且 有报错--->  tabcontent[i] is undefined !
   要怎么改正呢? ----------------------------->

        

[javascript]  view plain  copy
 print ?
  1.                    <script type="text/javascript">  
  2.                //方法一: 利用闭包  
  3.                 for(var i=0;i<tabMenus.length;i++){  
  4.   (function(_i){  
  5.     tabMenus[_i].onclick = function(){  
  6.        //遍历数组,隐藏所有tabcontent  
  7.        for(var j=0;j<tabcontent.length;j++){  
  8.           tabcontent[j].style.display = "none";  
  9.           }  
  10.       //显示被点击的tabMenus 对应的tabcontent  
  11.       tabcontent[_i].style.display = "block";  
  12.       }  
  13.      })(i);  // 闭包...  
  14. }  
  15.   
  16. //方法二: 给DOM节点添加 _index属性   
  17.    <script type="text/javascript">  
  18.                  
  19.                 for(var i=0;i<tabMenus.length;i++){  
  20.  tabMenus[i]._index = i;  
  21.     tabMenus[i].onclick = function(){  
  22.        //遍历数组,隐藏所有tabcontent  
  23.        for(var j=0;j<tabcontent.length;j++){  
  24.           tabcontent[j].style.display = "none";  
  25.           }  
  26.       //显示被点击的tabMenus 对应的tabcontent  
  27.       tabcontent[this._index].style.display = "block";  
  28.       }  
  29.      }  
  30. </script>  


2.  另一方面,我们还需要注意控制好 关键字this 的指向问题:
       <1> Javascript伪协议和内联事件对this的指向不同
     如  

[javascript]  view plain  copy
 print ?
  1.                               <script type="text/javascript">  
  2.                      // 弹出 “A"  
  3. <a href="#" οnclick="alert(this.tagName)";>click me</a>  
  4. // 弹出 "undefined"  
  5. <a href="Javascript:alert(this.tagName)">click me</a>  
  6. //弹出 "true"  
  7. <a href="Javascript:alert(this==window)">click me</a>  
  8.   </script>  


<2> setTimeout 和 setInterval 也会改变this指向
       他们都是直接调用函数里的this 指向window
   如 

[javascript]  view plain  copy
 print ?
  1.         <script type="text/javascript">  
  2.                  var name="somebody";  
  3.                  var adang = {  
  4.                      name : "adang";  
  5.                      say : funciton(){  
  6.                         alert("I'm" + this.name);  
  7.                      }  
  8.                   };  
  9.                      adang.say();     // I'm adang                     
  10.               setTimeout(adang.say,1000);  // I'm somebody  
  11. setInterval(adang.say,1000);  // I'm somebody     
  12. // 解决办法使用匿名函数 --->  
  13. setTimeout(function(){ adang.say()},1000);  // I'm adang  
  14.  </script>  
  15.          

<3> DomNode.on XXX  方式也会改变this 指向
        它将直接调用函数里面的this 指向DomNode
   比如:

[javascript]  view plain  copy
 print ?
  1.         <script type="text/javascript">  
  2.                  var name="somebody";  
  3.    var btn = document.getElementById("btn");  
  4.                  var adang = {  
  5.                      name : "adang";  
  6.                      say : funciton(){  
  7.                         alert("I'm" + this.name);  
  8.                      }  
  9.                   };  
  10.                       adang.say();     // I'm adang                    
  11.                btn.onclick = adang.say;   // I'm BUTTON  
  12. // 解决办法使用匿名函数 --->  
  13. btn.onclick = funciton(){ adang.say()} ;  // I'm adang   
  14.  </script>  


<4>  对此,另外我们还可以用 call 和 apply 函数来改变处理函数的this指向
        比如:

[javascript]  view plain  copy
 print ?
  1.          <script type="text/javascript">  
  2.                    var name="somebody";  
  3.         var btn = document.getElementById("btn");  
  4.                    var adang = {  
  5.                        name : "adang";  
  6.                        say : funciton(){  
  7.                           alert("I'm" + this.name);  
  8.                        }  
  9.                     };  
  10.                        adang.say.call(btn);     // I'm BOTTON ----  把this指向改成了按钮  
  11.         adang.say.apply(btn);     // I'm BOTTON ----  把this指向改成了按钮  
  12.                 setTimeout(adang.say,1000);  // I'm somebody  
  13.         setInterval(adang.say,1000);  // I'm somebody     
  14.         setTimeout(function(){ adang.say.apply(btn)},1000);  // I'm BUTTON  
  15. </script>  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值