JavaScript糟粕部分

frontend/javascript/rubbishy/banner

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

with語句

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

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語句

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
複製代碼

switch穿越

除非是明確中斷流程,不然每次條件判斷後都穿越到下一個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不多被用來執行位操做。

function語句對比function表達式

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。可以使用{} 和 []來替代。

new

JavaScript的new運算符建立了一個繼承於其運算符的原型的新對象,而後調用該運算數,把新建立的對象綁定給this。這給運算數(它應該是一個構造函數)一個機會在返回給請求者自定義新建立對象。

若是忘記了使用此new運算符,你獲得的就是一個普通的函數調用,而且this被綁定到全局對象,而不是新建立的對象。者意味着當你的函數嘗試去初始化新成員屬性時它將會污染全局變量。這是一件很是糟糕的事情。並且既沒有編譯時警告,也沒有運行時警告。

按照慣例,打算與new結合使用的函數應該以首字母大寫的形式命名,而且首字母大寫的形式應該只用來命名那些構造器函數。這個約定幫助咱們進行區分,便於咱們發現那些Javascript語言自身常常忽略可是卻帶來昂貴代價的錯誤。

一個更好的應對方法策略是根本不去使用new。幻想下就行啦~

void

在不少語言中,void 是一種類型,表示沒有值(空值)。而在JavaScript中,void是一個運算符,它接受一個運算數而且返回undefined。

function getValue(){
   a = void ( a = 90 );
   document.write('a = ' + a); # a = undefined
} 
複製代碼

這並無什麼用,並且使人很是困惑。應該避免使用。

參考

《JavaScript語言精粹》Douglas Crockford著 趙澤欣 鄢學鵾 譯

後話

各位大佬輕噴,還請多多指正。放上github地址github.com/reng99求follow就逃:)

other/loveU
相關文章
相關標籤/搜索