JavaScript是一门优秀的语言,可是不免存在着某些缺点,本博文主要说明下JavaScript的一些缺点。javascript
原文跳转请戳这里java
JavaScript有两组相等的运算符:===和!==
,以及他们邪恶的孪生兄弟==和!=
。===和!==
运算符可以按照你指望的方式工做。若是两个运算数类型一致且拥有相同的值,那么===
就返回true,!==
返回false。可是**==和!=**只有在两个运算符类型一致时才会作出正确的判断,若是两个运算数是不一样的类型,他们试图去强制转换值的类型。转换的规则复杂难以记忆。下面的一些有趣的例子:git
# 传递性
'0' == 0 # true
0 == '' # true
'' == '0' # false 为何不是true呢
false == 'false' # false
false == 0 # true
false == undefined # false
false == null # false
null == undefined # true
'\t\r\n' == 0 # true
复制代码
==
运算符对传递性的缺少值值得咱们警戒。最好永远不要使用那对邪恶的孪生兄弟。相反的,请始终使用===和!==
。若是上面的比较都是用===
运算符,结果都是false,在编程中规定使用,非常受益。程序员
⚠️传递性是一种编程约定。能够理解:对于任意的引用值x、y和z,若是x == y 和 y == z 为 ture,那么 x == z 为true。而JavaScript中的 == 运算符在某种特例上违背了传递性。github
JavaScript提供了一个with语句,本意是想使用它来快捷访问对象的属性。然而,它的结果可能有时不可预料,因此应该避免使用它。编程
下面的语句:浏览器
with (obj){
a = b;
}
复制代码
和下面的代码作的是一样的事情:安全
if(obj.a === undefined) {
a = obj.b === undefined ? b : obj.b;
} else {
obj.a = obj.b === undefined ? b : obj.b;
}
复制代码
因此,它等于这些语句中的一条:bash
a = b;
a = obj.b;
obj.a = b;
obj.a = obj.b;
复制代码
经过阅读代码,你不可能辨别出你会获得的是这些语句的那一条。它可能随着程序运行到下一步时发生变化。它甚至可能在程序运行过程当中就发生了变化。若是你不能经过阅读程序就了解它将作什么,你就没法确信它会正确地作你想要作的事情。frontend
with语句在JavaScript中存在,自己就严重影响了JavaScript处理器的速度,由于它阻断了变量名的语法做用域绑定。它的本意是好的,可是若是没有它,JavaScript语言会更好一点。
eval函数传递了一个字符串给JavaScript编译器,而且执行其结果。它是一个被滥用的JavaScript特性。那些对JavaScript语言只知其一;不知其二的人们最经常使用到它。例如你知道点表示法,可是不知道下标表示法,就可能会这么写:
eval("myValue = myObject." + myKey + ";");
复制代码
而不是这么写:
myvalue = myObject[myKey];
复制代码
使用eval形式的代码更加难以阅读。这种形式使得性能显著下降,由于它须要运行编译器,但也许只是为了执行一个微不足道的赋值语句。它也会让JSLint【⚠️JSLint是一个JavaScript语法检查器和校验器。】失效,让此工具检测问题的能力大打折扣。
eval函数还减弱了你的应用程序的安全性,带来XSS攻击,由于它被求值的文本授予了太多的权力。并且就像with语句执行的方式同样,它下降了语言的性能。
Function 构造器是eval的另外一种形式,一样也应该避免使用它。
浏览器提供的setTimeout和setInterval函数,他们可以接受字符串参数或函数参数。当传递的是字符串参数时,setTimeout和setInterval会像eval那样去处理。一样也应该避免使用字符串参数形式。
continue语句跳转到循环的顶部。可对代码重构后,性能会获得必定的改善,看下面的代码:
var beginTime = (new Date()).getTime();
var loop = 10000000;
for(var i = 0 ; i < loop ; i++){
if(i % 2 == 0){
continue;
}else{
console.log(i);
}
}
var endTime = (new Date()).getTime();
console.log('耗费时间:'+ (endTime-beginTime)); # 58625
复制代码
var beginTime = (new Date()).getTime();
var loop = 10000000;
for(var i = 0 ; i < loop ; i++){
if(i % 2 == 0){
}else{
console.log(i);
}
}
var endTime = (new Date()).getTime();
console.log('耗费时间:'+ (endTime-beginTime)); # 58471
复制代码
var beginTime = (new Date()).getTime();
var loop = 10000000;
for(var i = 0 ; i < loop ; i++){
if(i % 2 != 0) console.log(i);
}
var endTime = (new Date()).getTime();
console.log('耗费时间:'+ (endTime-beginTime)); # 56063
复制代码
除非是明确中断流程,不然每次条件判断后都穿越到下一个case条件。在使用的时候要当心这种带刺的玫瑰,他们是有用的,也是危险的。
If、while、do 或 for 语句能够接受一个括在花括号中的代码块,页能够接受单行语句。单行语句的形式是另外一种带刺的玫瑰。它带来的好处是能够节省两个字节,但这是否是一个好处值得商榷。它模糊了程序的结构,使得在随后的操做代码中可能容易插入错误。例如:
if (ok)
t = true;
复制代码
可能变成:
if (ok)
t = true;
advance();
复制代码
它看起来就像要这样:
if (ok) {
t = true;
advance();
}
复制代码
可是实际上它的本意是这样的:
if (ok) {
t = true;
}
advance();
或
if (ok) {
t = true;
} else {
advance();
}
复制代码
貌似是在作一件事情,但其实是在作另外一件事的程序是很难理解清楚的。团队中制定严格的规范要求始终使用代码块是得代码更加容易理解。
递增和递减运算符使得程序员能够用很是简洁的风格去编码。好比在C语言中,它们使得用一行代码实现字符串的复制成为可能:
for(p = src, q = dest; *p; p++, q++) *q = *p;
事实上,这两个运算符鼓励了一种不够严谨的编码风格。大多数的缓冲区溢出错误所形成的安全漏洞,都是由像这样编码而致使的。
当使用++ 和 --
时,代码每每过于拥挤、复杂和隐晦。所以,做为一条原则,我再也不使用它们。团队上也能够规范一波,那样咱们的代码风格会变得更加整洁。
JavaScript有着和Java相同的一套位运算符:
& and 按位与
| or 按位或
^ xor 按位异或
~ not 按位非
>> 带符号的右移动
>>> 无符号的(用0补足的)右移动
<< 左位移
复制代码
在Java中,位运算符处理的是整数。JavaScript没有整数类型,它只有双精度的浮点数。所以,位操做符吧它们的数字运算数先转换成整数,接着执行运算,而后再转换回去。在大多数语言中,这些运算符接近于硬件处理,因此很是快。但JavaScript的执行环境通常接触不到硬件,因此很是慢。JavaScript不多被用来执行位操做。
JavaScript既有function语句,同时也有function表达式。这使人困惑,由于它们看起来好像是相同的。一个function语句就是其值为一个函数的var语句的速记形式。
下面的语句:
function foo() {}
复制代码
意思至关于:
var foo = function foo() {}
复制代码
第二种写法相对友好,由于它明确表示foo是一个包含一个函数值的变量。要学好JavaScript这门语言,理解函数就是数值是很重要的。
function语句在解析时会发生被提高的状况,这意味着无论function被放置在哪里,它会被移动到被定义时所在做用域顶层
。这就放宽了函数必须先声明后使用的要求,这会致使混乱的。在if语句中使用function语句
也是被禁止的。结果代表大多数的浏览器都容许在if语句里使用function语句,可是它们在解析的时候处理上各不相同。这就形成了可移植性的问题。
一个语句不能以一个函数表达式开头,由于官方的语法假定以单词function
开头的语句是一个function
语句。解决的方法就是把函数调用括在一个圆括号中。
(function () {
var hidden_variable;
# 这个函数可能对环境有一些影响,可是不会映入新的全局变量
})();
复制代码
JavaScript有一套类型的包装对象。例如:
new Boolean(false)
会返回一个对象,该对象有一个valueOf方法会返回被包装的值。这其实彻底没有必要,而且有时还使人困惑。不要使用new Boolean、new Number 或 new String
。
此外,页应该避免使用new Object 和 new Array
。可以使用{} 和 []
来替代。
JavaScript的new运算符建立了一个继承于其运算符的原型的新对象,而后调用该运算数,把新建立的对象绑定给this。这给运算数(它应该是一个构造函数)一个机会在返回给请求者自定义新建立对象。
若是忘记了使用此new运算符
,你获得的就是一个普通的函数调用,而且this被绑定到全局对象,而不是新建立的对象。者意味着当你的函数尝试去初始化新成员属性时它将会污染全局变量。这是一件很是糟糕的事情。并且既没有编译时警告,也没有运行时警告。
按照惯例,打算与new结合使用的函数应该以首字母大写的形式命名,而且首字母大写的形式应该只用来命名那些构造器函数。这个约定帮助咱们进行区分,便于咱们发现那些Javascript语言自身常常忽略可是却带来昂贵代价的错误。
一个更好的应对方法策略是根本不去使用new。幻想下就行啦~
在不少语言中,void 是一种类型,表示没有值(空值)。而在JavaScript中,void是一个运算符
,它接受一个运算数而且返回undefined。
function getValue(){
a = void ( a = 90 );
document.write('a = ' + a); # a = undefined
}
复制代码
这并无什么用,并且使人很是困惑。应该避免使用。
《JavaScript语言精粹》Douglas Crockford著 赵泽欣 鄢学鹍 译
各位大佬轻喷,还请多多指正。放上github地址github.com/reng99求follow就逃:)