zoukankan      html  css  js  c++  java
  • 编写高质量 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="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="#" 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>

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

  • 相关阅读:
    Flash中先获取flv的尺寸然后再显示的方法
    雕虫小艺:Slider控件的制作(Flash)
    用几十行代码写一个可以在PC Web,PC桌面,安卓,iOS上运行的程序
    仰望星空,结果南辕北辙
    Flash播放mp4的两个问题:编码问题和需要下载完后才能播放的问题
    只学一点点:我的技术学习策略
    2012年计划
    提高ipad浏览器下大尺寸xml文件解析的性能
    html5/haXe开发偶感
    支点:技术选择的精髓
  • 原文地址:https://www.cnblogs.com/imwtr/p/4069264.html
Copyright © 2011-2022 走看看