您的位置:首页 > Web前端 > JavaScript

JavaScript需要避免的问题总结

2016-11-30 15:13 232 查看

JavaScript需要避免的问题总结

最近看了Douglas Crockford的《JavaScript 语言精粹》,对于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种方式定义全局变量:

在任何函数之外声明: var foo = value;

直接给全局对象添加属性: window.foo = value; (window为web浏览器的全局对象)

直接使用未经声明的变量: 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的几点编码规范提高代码质量
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: