首页 > 代码库 > 编写高质量 Javascript -- 知识点小记

编写高质量 Javascript -- 知识点小记

 

一: 团队合作避免JS冲突

脚本中的变量随时存在冲突的风险,

1.   解决办法---用匿名函数将脚本包起来,让变量的作用域控制在匿名函数之内

如:

 

<script type="text/javascript">     (function(){            var a=123,b="12121";       })();</script> ..... <script type="text/javascript">     (function(){            var a=123,b="asdasa";       })(); </script>

 

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

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

如:

 

 <script type="text/javascript"> var str; </script>       <script type="text/javascript">     (function(){            var a=123,str = b = "12121";       })(); </script> ..... <script type="text/javascript">     (function(){            var a=123,b=str;       })(); </script>

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

 

<script type="text/javascript"> var GLOBAL={}; </script> <script type="text/javascript">     (function(){            var a=123,b = "12121";        GLOBAL.str = a;        GLOBAL.str2 = b;       })(); </script> ..... <script type="text/javascript">     (function(){            var a=GLOBAL.str;            var b = GLOBAL.str2;       })(); </script>

 

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

 如:

<script type="text/javascript"> var GLOBAL={}; </script>    <script type="text/javascript">     (function(){            var a=123,b = "12121";            GLOBAL.A = {};            GLOBAL.A.str = a;            GLOBAL.A.str2 = b;       })();    </script> ..... <script type="text/javascript">     (function(){                        var a=GLOBAL.A.str;            var b = GLOBAL.A.str2;       })(); </script>

如此种种,函数内属性越复杂,

又可以进一步扩展GLOBAL挂载方式.....

如 GLOBAL.A={};

GLOBAL.A.CAT={};

GLOBAL.A.DOG={};

GLOBAL.A.DOG.name="aa";

GLOBAL.A.CAT.name="bb";

 

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

如:

 

<script type="text/javascript">      var GLOBAL={};      GLOBAL.namespace = function(str){      var arr=str.split("."), o = GLOBAL;      for(i=(arr[0]="GLOBAL") ? 1 : 0 ; i<arr.length; i++){          o[arr[i]] = o[arr[i]] || {};        o = o[arr[i]];        }    }

 

</script><script type="text/javascript">     (function(){            var a=123,b = "12121";        GLOBAL.namespace("A.CAT");        GLOBAL.namespace("A.DOG");        GLOBLA.A.CAT.name="aa";        GLOBLA.A.DOG.name="bb";        })();</script>

二:
   方便JS程序执行:

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

 

<script type="text/javascript">     alert(document.getElementById("test").innerHTML);</script>     <div id="test">hello ! </div>

 

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

<div id="test">hello ! </div>     <script type="text/javascript">     alert(document.getElementById("test").innerHTML);     </script>

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

<script type="text/javascript">    window.onload= function(){     alert(document.getElementById("test").innerHTML);     }</script>     <div id="test">hello ! </div>

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

<script type="text/javascript">    function init(){     alert(document.getElementById("test").innerHTML);     }</script>     <div id="test">hello ! </div>         <script type="text/javascript">    init();    </script>    </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不支持  

  代码如:

 

                        <ul>                           <li id="item1"></li>               <li id="item2"></li>               <li id="item3"></li>            </ul>    <script type="text/javascript">            var item1 = document.getElementById("item1");            var nextNode = item1.nextSibling;            if(document.all){                while(true){                  if(nextNode.nodeType == 1){                     break;                     }                  else{                  if(nextNode.nextSibling){                    nextNode = nextNode.nextSibling;                    }                     else{  break;  }                    }                }            }            alert(nextNode.id);            //////////////////////////////////////////////////            var item2 = document.getElementById("item2");            var nextNode = item2.nextSibling;            if(!document.all){                while(true){                  if(nextNode2.nodeType == 1){                     break;                     }                  else{                  if(nextNode2.nextSibling){                    nextNode2 = nextNode2.nextSibling;                    }                     else{  break;  }                    }                }            }            alert(nextNode2.id);            </script>

 

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

                        <ul>                           <li id="item1"></li>               <li id="item2"></li>               <li id="item3"></li>            </ul>        <script type="text/javascript">            function getNextNode(node){              node = typeof node=="string" ? document.getElementById("node") : node;              var nextNode = node.nextSibling;              if(!nextNode)                 return NULL;                        if(!document.all){                while(true){                  if(nextNode.nodeType == 1){                     break;                     }                  else{                  if(nextNode.nextSibling){                    nextNode = nextNode.nextSibling;                    }                     else{  break;  }                    }                }            }            return nextNode;            }       //funciton over                        var nextNode = getNextNode("item1");            var nextNode2 = getNextNode("item1");            alert(nextNode.id);            alert(nextNode2.id);        </script>

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

 

         <style type="text/css">              #test1 { background:blue; height: 100px;}          #test1 { background:blue; height: 100px;}    </style>             <div id="test1"></div>             <div id="test2"></div>                     <script type="text/javascript">                function setOpacity(node,level){         // level 为滤镜程度                    node = typeof node=="string" ? document.getElementById("node") : node;                    if(document.all){                       node.style.filter = ‘alpha(opacitu= ‘ + level + ‘)‘;                        }                     else{                        node.style.opacity = level / 100;                        }                }                                setOpacity("test1",20);   //test1.style.filter = ‘alpha(opacitu=20)‘;                setOpacity("test2",80);   //test2.style.opacity = 0.8;        </script>

 

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

<script type="text/javascript">    var btn = document.getElementById("btn");         btn.onclick = function(e){     e  = window.event || e;     // ..........接下来你就可以用e了, 比如 e.target 等</script>

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

<script type="text/javascript">    var btn = document.getElementById("btn");    btn.onclick = function(e){    e  = window.event || e;    var el = e.srcElement || e.target;    alert(el.tagName);</script>

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

 

                              <script type="text/javascript">                               function stopPropagation(e){                                    e = window.event || e;                                    if(document.all){                                       e.cancelBubble = true;                                       }                                     else{                                       e.stopPropagation();                                       }                                    }                                    /////////////////////////调用                            wrapper.onclick = function(){                                       ..............                                       }                            btn.onlcick = function(e){                                      ................                                      stopPropagation(e);                                      }                </script>

 

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

 

            <script type="text/javascript">             function on(node,eventType,handeler){            node = typeof node=="string" ? document.getElementById("node") : node;                     if(document.all){                  node.attachEvent("on"+ eventType,handeler);                    }                 else{                node.addEventListener(eventType,handeler,false);   // 其中第三个参数:true--捕获型,false--冒泡型                    }                }            var btn = document.getElementById("btn");            on(btn,"click",funciton(){              alert(1);              });                on(btn,"click",funciton(){             alert(2);               });      </script>

 

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

 <script type="text/javascript">              function trim(ostr){           //trim() 去空格                 return ostr.replace(/^\s+|\s+$/g,"");                 }              function isNumber(s){                 return !isNaN(s);                 }              function isString(s){                 return typeof s === "string";                 }              function isBoolean(s){                 return typeof s === "boolean";                 }              function isFunction(s){                 return typeof s === "function";                 }              function isNull(s){                 return s === null;                 }              function isUndefined(s){                 return typeof s === "undefined";                 }              function isEmpty(s){                 return /^\s*$/.test(s);                 }              function isArray(s){                 return s instanceof Array;                 }              function get(node){             // get() 代替ducument.getElementById()    ...alert(get("test1").innerHTML);                 node = typeof node=="string" ? document.getElementById("node") : node;                 return node;                 }               function $(node){             // $() 代替ducument.getElementById()        ...alert($("test1").innerHTML);                 node = typeof node=="string" ? document.getElementById("node") : node;                 return node;                 }                  //  原生JS没有getElementByClassName,   那就给它实现一个呗...      //   getElementByClassName函数接收3个参数,第一个参数为class名(必选),第二个为父容器,缺省值为body节点,第三个参数为DOM节点的标签名。      // 函数 实现如下                function getElementByClassName(str,root,tag){                   if(root){                      root = typeof root=="string" ? document.getElementById("root") : root;                      }                      else{                      root = document.body;                      }                    tag = tag || "*";                    var els = root.getElementByTagName(tag),  arr=[];                    for(var i=0,n=els.length; i<n ; i++){                       for(var j=0,k=els[i].className.split(" "), l=k.length; j<l; j++){                          if(k[k] == str){                            arr.push(els[i]);                            break;                            }                         }                    }                    return arr;                }                                //然后我们就可以直接调用啦..                var aEls = getElementByClassName("a");                var bEls = getElementByClassName("b");                // .............    </script>

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

 

                          <script type="text/javascript">                        //遍历数组,让tabMenus 监听click事件  (Tab 组件监听选项卡)                         for(var i=0;i<tabMenus.length;i++){                            tabMenus[i].onclick = function(){                               //遍历数组,隐藏所有tabcontent                               for(var j=0;j<tabcontent.length;j++){                                  tabcontent[j].style.display = "none";                                  }                              //显示被点击的tabMenus 对应的tabcontent                              tabcontent[i].style.display = "block";                              }                             }               </script>

 

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

                         <script type="text/javascript">                        //方法一: 利用闭包                         for(var i=0;i<tabMenus.length;i++){                          (function(_i){                            tabMenus[_i].onclick = function(){                               //遍历数组,隐藏所有tabcontent                               for(var j=0;j<tabcontent.length;j++){                                  tabcontent[j].style.display = "none";                                  }                              //显示被点击的tabMenus 对应的tabcontent                              tabcontent[_i].style.display = "block";                              }                             })(i);  // 闭包...                        }                                                //方法二: 给DOM节点添加 _index属性                         <script type="text/javascript">                                                 for(var i=0;i<tabMenus.length;i++){                         tabMenus[i]._index = i;                            tabMenus[i].onclick = function(){                               //遍历数组,隐藏所有tabcontent                               for(var j=0;j<tabcontent.length;j++){                                  tabcontent[j].style.display = "none";                                  }                              //显示被点击的tabMenus 对应的tabcontent                              tabcontent[this._index].style.display = "block";                              }                             }                        </script>        

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

                                    <script type="text/javascript">                                 // 弹出 “A"                     <a href="http://www.mamicode.com/#" onclick="alert(this.tagName)";>click me</a>                     // 弹出 "undefined"                     <a href="Javascript:alert(this.tagName)">click me</a>                     //弹出 "true"                     <a href="Javascript:alert(this==window)">click me</a>                    </script>

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

           <script type="text/javascript">                    var name="somebody";                    var adang = {                        name : "adang";                        say : funciton(){                           alert("I‘m" + this.name);                        }                     };                        adang.say();     // I‘m adang                                         setTimeout(adang.say,1000);  // I‘m somebody            setInterval(adang.say,1000);  // I‘m somebody                // 解决办法使用匿名函数 --->            setTimeout(function(){ adang.say()},1000);  // I‘m adang       </script>                   

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

           <script type="text/javascript">                    var name="somebody";            var btn = document.getElementById("btn");                    var adang = {                        name : "adang";                        say : funciton(){                           alert("I‘m" + this.name);                        }                     };                         adang.say();     // I‘m adang                                          btn.onclick = adang.say;   // I‘m BUTTON            // 解决办法使用匿名函数 --->            btn.onclick = funciton(){ adang.say()} ;  // I‘m adang        </script>

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

          <script type="text/javascript">                    var name="somebody";            var btn = document.getElementById("btn");                    var adang = {                        name : "adang";                        say : funciton(){                           alert("I‘m" + this.name);                        }                     };                        adang.say.call(btn);     // I‘m BOTTON ----  把this指向改成了按钮            adang.say.apply(btn);     // I‘m BOTTON ----  把this指向改成了按钮                    setTimeout(adang.say,1000);  // I‘m somebody            setInterval(adang.say,1000);  // I‘m somebody                setTimeout(function(){ adang.say.apply(btn)},1000);  // I‘m BUTTON    </script>

=============================分割线=====================待续=====================================

 

编写高质量 Javascript -- 知识点小记