• <<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: '鸣人'
        };
    }
    ------------------
    要比昨天的自己更强
  • 相关阅读:
    Vue日期转化
    javascript数组去重
    javascript数组及类数组详解
    javascript的argument和克隆方法详解
    javascript中对this的理解
    javascript命名空间及对象枚举的理解
    javascript继承模式详解
    javascript的call和apply区别
    javascript原型及原型链详解
    javascript对象及包装类
  • 原文地址:https://www.cnblogs.com/lzj0616/p/4419759.html
Copyright © 2020-2023  润新知