zoukankan      html  css  js  c++  java
  • Javascript模式(第二章基本技巧)------读书笔记

    本章主要帮助大家写出高质量的JS代码的方法,模式和习惯,例如:避免使用全局变量,使用单个的var变量声明,缓存for循环的长度变量length等

    一、尽量避免使用全局变量

      1 每一个js环境都有一个全局对象,通过this可以访问,创建的每一个全局变量都归这个全局对象所有,在浏览器中,这个全局对象this等于window(在其他环境中,this对象不一定是window对象) 

    var sss="sss";
    this.sss;//"sss"
    window.sss;//"sss" 

      1.1全局变量导致的问题

        1,与第三方JS库发生命名冲突;2,与广告合作伙伴的脚本发生命名冲突;3,与来自第三方的统计脚本或者分析脚本发生命名冲突;4,代码移植,如果你的代码换个环境执行,可能与另外一个环境的代码相冲突

        2, 全局变量总是出现的原因:1,JS有暗示全局变量的概念,即任何变量,如果没有声明过,那么就是全局变量;2,隐式的创建了全局变量反模式--使用var声明的链式赋值

    var a=b=0;
    //这一切的原因是操作符的优先级,=操作符的优先级是从右向左,所以上面的例子,实际上相当于
    //var a=(b=0);因此相当于隐式地创建了全局变量b,
    /*正确的写法:
    *var a,b,c;
    *a=b=c=0;
    */

       1.2 变量释放(即delete删除变量)时的副作用

        1,隐含的全局变量(即不声明而直接使用的变量)与明确定义的全局变量(即使用var定义的全局变量)的不同之处在于,能否使用delete操作符删除该变量

          首先我们理解一下delete操作符,delete操作符是用来删除对象的属性的,使用var定义的全局变量,不能被删除,而隐含的全局变量是可以删除的,因此隐含的全局变量其实并不是真正意义上的变量,而是作为全局对象的属性存在的

    var m="111";
    delete m;//false
    console.log(m);//111
    fff="d";
    delete fff;//true
    console.log(fff);//Uncaught ReferenceError: fff is not defined 

      1.3 访问全局对象

        按照以下方式获取全局对象,因为函数的调用(这里不包括使用new操作符执行的函数)一般都指向全局对象    

        但是在ECMAScript5的严格模式下不能如此使用,严格模式下对全局对象的访问:将库代码打包到一个直接函数,然后传递一个引用给this,即把this看成传递到直接函数的一个参数。

    var global=(function(){
      return this;
    })();

      1.4 单一var模式:只使用一个var,在函数的顶部对该函数中所有的变量进行声明

      优点:

        1、在一个位置可以查找到函数所需要的所有局部变量;

        2、防止变量未声明就使用;

        3、更少编码;

        4、声明的时候进行初始化,防止在后期使用时出现逻辑错误(数字变量当成字符串这样低级的错误)

        5、将DOM引用赋值给局部变量,不需要每次都去重新进行DOM搜索,可大量节约时间

    function fun(){
        var a=1,
         b=2,
         my={},
        i,
        j; //函数体 }
    function fun(){
        var element=document.getElementById("result"),
           style=element.style;
    }

      1.5、提升:无论在js函数内的任意位置声明的变量,效果都等同于在函数顶部进行声明

        JS允许在函数中的任意位置声明变量,但无论在哪里声明最终都会被提升到函数的顶部,因此先使用后声明可能会导致逻辑问题

        js分为预编译阶段与执行阶段,

        预编译阶段:

          1 对使用function语句声明的函数进行处理,不仅按照函数名按照变量标识符进行索引,对函数体也进行处理(即对函数名(也可以认为是变量名)进行赋值),如果出现同名函数,则会用后者覆盖前者(即后者的赋值覆盖前者)

            2 对匿名函数在此阶段视而不见

         3 对使用var声明的变量进行索引,但是对变量的初始化忽略掉,

          在预编译阶段遇到var声明的变量,如果前面没有做过初始化,就是undefined,如果该变量在前面是函数名,那么其值就是函数体,这里不做覆盖

    function fun(){}
    /*变量名fun在预编译阶段,不仅对fun这个变量名进行索引,对其函数体也进行了处理,即fun的值为函数体*/
    console.log(fun);//function()
    var fun="123456";
    /*使用var声明的变量名fun在预编译阶段,只对其变量名进行索引,不对其进行赋值,所以这fun的初始化值不会覆盖上面的函数体,但是在执行阶段会覆盖*/
    console.log(fun);//123456

        执行阶段:

          1 对匿名函数按表达式逐行进行解释执行

          2 为预编译阶段索引的变量读取初始值,由于执行阶段是逐行解释执行的,所以如果在赋值语句前面进行调用的话,值应该为预编译阶段的

    function fun(){
        alert(myName);//undefined
        var myName="local";
        alert(myName);//local
    }
    fun(); 

    二、for循环

      for循环一般用于遍历数组或者类数组对象(arguments,html容器等)

      优化点:1 对数组的长度进行缓存,var len=arr.length

          2 使用单一的var模式,把所有的变量都提到函数体的开始,使用一个var进行声明

          3 i++替换掉i=i+1与i+=1

          4 在没有要求的时候,递减到0,即i--

      1 一般情况下的for循环

    for(var i=0;i<arr.length;i++){
    //对数组或者类数组对象中的元素的操作
    } 

      缺点:显然每次循环都会计算一下要访问数据的长度,这样效率就会降低,尤其是当访问的是HTML容器对象时

      改进:将要访问数据的长度缓存起来,对访问速度的提升相当显著,ie可提高170倍

    for(var i=0,max=ayy.length;i<max;i++){
    } 

      2  进一步改进:结合我们前面提到单var变量模式

    function fun(){
        var i=0,ayy=[],max;//将所有该函数中用到的变量都声明在函数的顶部
        for(i=0,max=ayy.length;i<max;i++){
        }
    } 

      该模式的缺点:复制粘贴的时候,要保证将所需变量的声明全部复制进去

      3 JSLint推荐使用++与--,即逐步递增或者递减(这个有不同的意见,可以保留)

      4 更进一步改进:从最后一个元素,逐个遍历到第一个元素,将i与0比较,比i与非0的max比较效率要高

    var a,b,c,d,e,f;
    a=+new Date();
    for(i=10000000;i>=0;i--){
    }
    b=+new Date();
    console.log(b-a);
    //4508
    
    
    c=+new Date();
    for(j=0;j<=10000000;j++){
    }
    d=+new Date();
    console.log(d-c);
    //4310
    e=+new Date();
    for(k=10000000;k--;){
    }
    f=+new Date();
    console.log(f-e);//3151 

    三、for-in循环

      for-in主要用于遍历非数组对象,称之为枚举

          1 当要遍历对象属性,并过滤掉原型中的属性和方法时,使用hasOwnProperty()方法

    var man={
        a:1,
        b:2,
        c:3
    },i;
    
    for(i in man){
        if(man.hasOwnProperty(i)){//当确定不了对象属性和原型中的内容时,使用hasOwnProperty方法加以判断,如果可以确定则可以省略该判断,提高效率
            console.log(i+":"+man[i]);
        }
    }

       2 在上面我们提到对原型链中的属性和方法进行过滤时,使用hasOwnProperty方法,由于hasOwnProperty方法是属于Object原型的方法,所以我们可以这样使用,这样避免命名冲突(即man对象也有一个hasOwnProperty方法,那么就与我们想要过滤使用的hasOwnProperty方法冲突了)

    var i,hasOwn=Object.prototype.hasOwnProperty,man={
    "name":"jim",
    "age":12
    };
    for(i in man){
      if(hasOwn.call(man,i)){
        console.log(i+":"+man[i]);
      }
    }




    var i,hasOwn=Object.prototype.hasOwnProperty,man={
    "name":"jim",
    "age":12,
      hasOwnProperty:function(){console.log("man's hasownproperty")}
    };
    for(i in man){
      if(man.hasOwnProperty(i)){
        console.log(i+":"+man[i]);
      }
    }

    四、不要给内置对象的原型增加方法(这里的内置对象指的是Date,Math,Array,String,Event,Object等)

      我们经常给构造函数的原型增一些方法,但是给js的内置对象的构造函数的原型增加方法会严重影响可维护性,因此这里不推荐

      下面的几种情况例外

        1,可以添加ECMAScript5中描述的确尚未实现的方法,等待ECMAScript加以实现

        2,某些浏览器的JS引擎已经实现了该方法

        3,写成文档形式,与团队充分沟通

      给内置对象添加自定义方法

    if(typeof Object.prototype.MyMethod!=="function"){
        Object.prototype.MyMethod=function(){
        };
    }  

    五、switch模式

      1 每个case语句结尾都有一个break

      2 使用default来作为switch的结束 

    六、避免使用隐式类型转换

      1 什么是隐式类型转换:false==0、""==false

      2 为了避免出现上面的情况,我们在使用比较语句的时候尽量使用===和!==

       6.1 避免使用eval

        1 eval可以将任何的字符串当作js代码执行

         2 将ajax返回的数据字符串转换成对象时,不推荐使用eval,推荐JSON.parse或者JSON.org网站的类库,因为eval执行的字符串可能是一家被篡改过的代码

         3 setInterval/setTimeout传递参数时,也会导致类似于eval的隐患,因此尽量避免使用 

        4 new Function()与eval和相似,使用时要小心

          如果一定要使用eval,可以使用new Function来替代,因为new Function将在局部函数空间运行,因此代码中var定义的变量不会成为全局变量

          另外一个避免eval中使用var定义的变量成为全局变量,将eval放到一个即时函数中 

           new Function(或者Function),这个方法是只看到全局变量,对局部变量影响较小     

    setTimeout("fun(1,2,3)",100);//反模式
    
    //推荐模式
    setTimeout(function(){
        fun(1,2,3);
    },100); 
                            
    var jsString1='var aaaaaa=1;console.log(aaaaaa);';
    var jsString2='var bbbbbbb=2;console.log(bbbbbbb);';
    var jsString3='var ccccccc=3;console.log(ccccccc);';
    eval(jsString1);//1
    new Function(jsString2)();//2new Function将在局部函数空间运行
    
    /*另外一个避免eval中使用var定义的变量成为全局变量,将eval放到一个即时函数中*/ (function(){ eval(jsString3); })();//3 console.log(aaaaaa);//1eval代码里面var定义的变量会成为全局变量 console.log(bbbbbbb);//ReferenceError: ccccccc is not defined console.log(ccccccc);//ReferenceError: ccccccc is not defined
     new Function(或者Function),这个方法是只看到全局变量,对局部变量影响较小

    function
    fff(){ var bbb='bbb'; var jsString='console.log(bbb);' new Function(jsString)();//或者Function(jsString)() } fff();//ReferenceError: bbb is not defined
    var a="global a";
    function fff(){
        var a='local a';
        var jsString='console.log(a);'
        new Function(jsString)();//或者Function(jsString)()
    }
    fff();//global a

    七、使用parseInt方法时,第二个参数尽量不要省略  

    八、编码约定

      8.1 缩进:使用tab键进行缩进

      8.2 大括号:for与if语句最好都使用大括号

      8.3 开放大括号的位置:和语句放在同一行  

    /*根据分号插入机制,也就是一行结束如果后面没有分号,会自动插入分号*/
    return 
    {
        "name":"Amy",
        "age":18
    };
    
    /*相当于
    return ;
    {
        "name":"Amy",
        "age":18
    };
    *因此下面这种方式更合适
    */
    return {
        "name":"Amy",
        "age":18
    }; 

    九、命名约定

      9.1 构造函数的首字母大写

      9.2 分隔单词:函数/方法名:小驼峰式;变量:小写字母,下划线分隔;常量:全部大写;私有变量/方法:下划线做前缀

    十、编写注释

    十一、编写API文档

    十二、编写可读性强的代码

    十三、同行互查

    十四、在正式发布时精简代码

    十五、运行JSLint

  • 相关阅读:
    springcloud配置eureka后启动application失败原因Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'trace
    Parameter index out of range (1 > number of parameters, which is 0
    mysql返回某月份的第一天
    mybatisplus的自定义sql使用方法及VO的使用
    mysql截取地址为城市名
    mybatisplus多表查询及自定义查询
    mybatisplus条件构造器wrapper
    HTTP 【值得你看个究竟】
    Kafka 【入门一篇文章就够了】
    notepad++ 删除末尾换行符
  • 原文地址:https://www.cnblogs.com/amy2011/p/3350455.html
Copyright © 2011-2022 走看看