JavaScript需要避免的问题总结

JavaScript需要避免的问题总结

最近看了Douglas Crockford的《JavaScript 语言精粹》,对于JavaScript有了新的了解,本文主要总结一下JavaScript语言特性引起的一些常见问题,以及避免方法。


1.检索

  • JavaScript检索对象包含值的方法有两种,一种是”[]”,另一种是”.”,一般两种方式都可用。但在检索的字符串是一个非法JavaScript标示符或者为保留字时,”.”是不可用的,只能使用name[“first-name”]
    (在对象字面量定义时也是如此,由于“first-name”是非法标示符,所以定义时必须用引号包含——“first-name:”“Joe”)。
    书中建议使用”.”方法检索,也就是name.first_name。所以就需要编码时注意标示符的合法性,以及不要和保留字冲突

  • 另外一个更重要的问题是,检索一个不存在的成员属性时,将返回undefined,而从undefined中取值会导致TypeError异常,例:age不为name对象的属性,name.age为undefined,name.age.child则会抛出异常。解决方法为 设置默认值var age = name.age || “unknown”, 也可以通过“&&”运算避免错误: name.age&&name.age.child

2.枚举

for in 语句可以遍历一个对象中所有属性名,但同时还包括函数和原型中的属性。解决方法一就是,过滤掉不需要的属性

var name;
for(name in names){
  if(names.hasOwnProperty(name) && typeof names[name] !=='function'){
    document.writeln(name + ' : ' + names[name]);
  }
}

其中hasOwnProperty方法检测对象是否有指定属性,且不检查原型链;若属性值为函数,typeof返回’function’类型。
另一种解决方法就是,完全避免使用for in语句,使用常规for语句
因为for in语句除了上述问题以外,枚举的属性名出现的顺序是不确定的,若要使顺序确定,则可以创建一个数组,以正确的顺序包含属性名,然后用for语句遍历

var i;
var properties =[
    'first_name',
    'middle_name',
    'last_name'
    ];
for(i=0; i<properties.length; i+=1){
    document.writeln(properties[i] + ':' + names[properties[i]]);
}

这样既保证了顺序,又直接确定了需要的属性。(但这种方法需要自己创建数组,且需要自己确定属性名,虽然书中推荐这种方法,但还要看具体情况)

3.减少全局污染

Douglas Crockford在书中指出,JavaScript对全局变量的依赖是所有糟糕特性中最糟糕的一个,因为全局变量可以被程序的任何部分在任意时间修改,降低了程序的可靠性,且全局变量名称会和子程序变量名称产生冲突,导致程序无法运行且难以调试。
共有3种方式定义全局变量:

  1. 在任何函数之外声明: var foo = value;
  2. 直接给全局对象添加属性: window.foo = value; (window为web浏览器的全局对象)
  3. 直接使用未经声明的变量: foo = value; (即隐式的全局变量)

减少全局污染的方法一:最小化使用全局变量 ,一个应用只创建一个唯一全局变量

var MYAPP = {};
MYAPP.name = {
    ...
};
MYAPP.method={
    ...
};

另一种减少全局污染的方法是,使用函数和“闭包”构建模块来进行信息隐藏
“内部函数可以访问它们外部函数的参数和变量(除了this和arguments),内部函数拥有比它的外部函数更长的生命周期,内部函数访问外部函数的实际变量而无需复制。”

  • 所谓“闭包”即是函数可以访问它被创建时所处的上下文环境(可以简单理解为内部函数可以访问外部函数定义的变量)。
  • 模块即利用函数作用域和“闭包”构建的只提供接口,隐藏其中状态信息的函数或对象。

这里举书中的一个例子:

var serial_maker = function(){
    var prefix = '';
    var seq = 0;
    return {
        set_prefix: function(p){
            prefix=String(p);
        },
        set_seq: function(s){
            seq = s;
        },
        gensym: function(){
            var result = prefix + seq;
            seq +=1;
            return result;
        }
    };
};

var seqer = serial_maker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym();

seqer只提供接口方法set_prefix、set_seq和gensym,由于函数作用域,私有变量对其他程序是不可见的,只能通过这些方法改变prefix 和seq的值。

也可以构造一个对象:

var myObj = (function() {
    var value=0;
    return {
        increment: function(inc){
            value += typeof inc === 'number'?inc:1;
        },
        getValue: function(){
            return value;
        }
    };
}());
myObj.increment(1);
myObj.getValue();

注意“()”,以上返回的是函数运行的结果,即包含两个方法的对象。

模块模式的一般形式为第一种方式:定义了私有变量和函数的函数;利用闭包创建访问私有变量和函数的接口函数;最后返回这个函数,或者放到可以访问的地方。

利用模块模式可以摒弃全局变量的使用。
可参考另外一篇文章JavaScript的几点编码规范提高代码质量

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值