zoukankan      html  css  js  c++  java
  • <<Javascript Patterns>>阅读笔记 第2章 基本技巧(二)

    关于for-in循环

    循环数据时, 强烈不推荐使用for-in循环.因为当Array对象被扩展后, 再用for-in循环遍历数据会导致逻辑上的错误, 举例说明:

    var arr = ['a', 'b', 'c'];
    // 下标遍历
    for(var i=0, len=arr.length; i<len; i++) {
        console.info(typeof i); // number
        console.info(i);
    }
    // for-in遍历
    for(var i in arr) {
        console.info(typeof i); // string
        console.info(i);
    }
    console.log(arr.length); // 3

    此时, for-in遍历数组时, i的类型为string, 已不再是数组的下标, 而是作为数组对象的key出现的(Javascript中数组也是对象).

    若在遍历前加上以下代码, 则for-in循环将出现逻辑上的错误

    if(typeof Array.prototype.len === 'undefined') {
        Array.prototype.len = function() {
            return this.length;
        };
    }

    即给数组添加原型方法len()

    console.log(arr.length); // 输出仍为3

    但for-in循环却同时将len输出出来了, 这说明for-in确实是将Array当成一个Object来遍历的, len作为对象的一个属性自然也会被遍历出来.

    总之, 我们可以不乱在原型上添加方法, 不能保证别人不会, 所以最好是不要使用for-in循环数组.

    当使用for-in遍历一个对象时, 一般与hasOwnProperty()一起使用, hasOwnProperty()用于过滤原型属性或原型方法, 举例说明:

    // 定义一个person对象, 包含两个属性和一个方法
    var person = {
        name: '张三',
        age: 28,
        say: function() {
            // ...
        }
    }
    // 在Object上扩展一个方法sleep()
    if(typeof Object.prototype.sleep === 'undefined') {
        Object.prototype.sleep = function() {
            // ...
        }
    }
    // 使用hasOwnProperty()的for-in循环
    for(var i in person) {
        if(person.hasOwnProperty(i)) {
            console.info(i);
        }
    }
    /*
    控制台打印结果:
    name
    age
    say
     */
    // 不使用hasOwnProperty()的for-in循环
    for(var i in person) {
        console.info(i);
    }
    /*
    控制台打印结果:
    name
    age
    say
    sleep
     */

    需要说明的是, 不使用hasOwnProperty()并没有错, 只是可能会对循环结果带来一些混乱, 如果自己的代码有足够信心, 可以不hawOwnProperty(), 还可以稍微提高循环速度.

    不要增加内置的原型

    给内置原型增加方法或属性是很强大的一个功能, 但正是由于它的强大, 往往会给维护带来巨大的成本. 此外, 如上面所述, 如果给扩展了内置原型, 在没有使用hasOwnProperty()的循环中会导致一些混乱.

    如果, 一定要扩展原型, 那么要先检查自定义的属性或方法是否已经存在, 若不存在再进行扩展, 此外一定要用文档记录下来, 以便团队交流. 以下模式可以用来扩展原型:

    if(typeof Object.prototype.myMethod === 'undefined') {
        Object.prototype.myMethod = function() {
            // ...
        }
    }

    避免使用隐匿的类型转换

    Javascript存在隐匿的类型转换, 比如fasle == 0, “” == 0这类的比较语句都回返回true, 在一定程度上会引起混淆, 类型不清.

    所以建议在比较比较语句中使用===和!==比较数值和类型

    避免使用eval()

    第一句话”eval()是魔鬼”

    一般提前编写好的代码是不需要使用eval()的, 如果代码是在运行过程中动态生成的, 可能需要使用eval()让其转换为Javascript代码, 但有更好的方法来代替eval(), 此类情况多数出现在处理ajax返回值时, 例如:

    // ajax请求返回一个字符串"name", 要给一个对象obj添加一个属性, 并以name作为属性名, '张三'作为属性值
    var property = "name";
    var obj = {
        age: 22
    };
    // 反模式, 不要使用
    // eval('obj.' + property + '= "z3"');
    // 推荐
    obj[property] = '张三';
    for(var i in obj) {
        console.info(i);
    }

    使用eval()也包含一些隐患, 有可能会执行一些被篡改过的代码.

    同时, setInterval()和setTimeout()的调用与eval()有类似的问题

    // 反模式
    setTimeout(“myFunc()”, 1000);
    setTimeout(“myFunc(2, 5, 7)”, 1000);
    // 推荐
    setTimeout(myFunc, 1000);
    setTimeout(function(){
        myFunc(2, 5, 7);
    }, 1000);

    如果一定要使用eval(), 那么可以考虑是否可以用new Function()来代替eval().

    因为使用new Function()代码将在一个局部函数空间中运行, 任何使用var定义的变量不会自动成为全局变量, 例如:

    new Function("var p = 3; console.info(p);")(); // 3
    console.info(p); // ReferenceError: p is not defined
    eval("var p1 = 4; console.info(p1);"); // 4
    console.info(p1); // 4

    另一个防止自动成为全局变量的方法是将eval()封闭到一个立即执行函数中, 如:

    (function(){
        eval('var num = 11; console.info(num);'); // 11
    })();
    console.info(num); // ReferenceError: p is not defined

    new Function()和eval()的另一个区别是, eval()会影响到作用域链, 而new Function()对局部变量的影响比较小.

    eval()可以访问和修改它外部作用域的变量, 而new Function()不能. 例子:

    (function(){
        var num = 13;
        eval('var num = 20; console.info(num);'); // 20
        console.info(num); //20, 改变了num的值
    })();
    (function(){
        var num = 12;
        new Function('var num = 17; console.info(num);')(); // 17
        console.info(num); // 12
    })();

    使用parseInt()的数值约定

    这个约定主要是为了解决ECMAScript新旧版本不一致的问题,

    在ECMAScript早期的版本中, 0开头的字符串会被当成一个八进制数, 在ECMAScript5中发生了改变, 所以在使用parseInt()方法时, 最好不要忽略第二个参数(进制参数),

    例如在处理日期字符串时:

    var month = '07',
        date = '02';
    month = parseInt(month, 10);
    date = parseInt(date, 10);

    另外, 如果是转换纯数字字符串, 例如'09', 那么使用Number('09')会更效率一些,

    如果是非纯数字字符串, 例如'02 hello', 那么只有使用parseInt('02 hello')了, Number('02 hello')会返回NaN

    编码约定

    书中提到的编码约定, 都为了提高代码的可读性, 降低代码的维护成本,

    包括缩进, 空间, 大括号, 命名规则, 空格的使用等.

    其中, 要注意的一点是大括号的位置, 由于分号的插入机制, 会导致某些代码执行结果出人意料, 例如:

    // 出人意料的返回结果
    function func() {
        return
        {
            name: '鸣人'
        };
    }
    console.info(func()); // undefined;
    // 以上写法相当于
    function func1() {
        return undefined;
        {
            name: '鸣人'
        };
    }
    // 总之, 推荐将大括号放在前面语句的的同一行:
    function func3() {
        return {
            name: '鸣人'
        };
    }
    ------------------
    要比昨天的自己更强
  • 相关阅读:
    Call KernelIoControl in user space in WINCE6.0
    HOW TO:手工删除OCS在AD中的池和其他属性
    关于新版Windows Server 2003 Administration Tools Pack
    关于SQL2008更新一则
    微软发布3款SQL INJECTION攻击检测工具
    HyperV RTM!
    OCS 2007 聊天记录查看工具 OCSMessage
    CoreConfigurator 图形化的 Server Core 配置管理工具
    OC 2007 ADM 管理模板和Live Meeting 2007 ADM 管理模板发布
    Office Communications Server 2007 R2 即将发布
  • 原文地址:https://www.cnblogs.com/lzj0616/p/4419759.html
Copyright © 2011-2022 走看看