JavaScript糟粕部分

call_me_R發表於2018-07-08
frontend/javascript/rubbishy/banner

JavaScript是一門優秀的語言,但是難免存在著某些缺點,本博文主要說明下JavaScript的一些缺點。

原文跳轉請戳這裡

==

JavaScript有兩組相等的運算子:===和!==,以及他們邪惡的孿生兄弟==和!====和!==運算子能夠按照你期望的方式工作。如果兩個運算數型別一致且擁有相同的值,那麼===就返回true,!==返回false。但是**==和!=**只有在兩個運算子型別一致時才會做出正確的判斷,如果兩個運算數是不同的型別,他們試圖去強制轉換值的型別。轉換的規則複雜難以記憶。下面的一些有趣的例子:

# 傳遞性'0' == 0 # true0 == '' # true'' == '0' # false   為什麼不是true呢false == 'false' # falsefalse == 0 # truefalse == undefined # falsefalse == null # falsenull == undefined # true'\t\r\n' == 0 # true複製程式碼

==運算子對傳遞性的缺乏值值得我們警惕。最好永遠不要使用那對邪惡的孿生兄弟。相反的,請始終使用===和!==。如果上面的比較都是用===運算子,結果都是false,在程式設計中規定使用,很是受益。

⚠️傳遞性是一種程式設計約定。可以理解:對於任意的引用值x、y和z,如果x == y 和 y == z 為 ture,那麼 x == z 為true。而JavaScript中的 == 運算子在某種特例上違背了傳遞性。

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;

}複製程式碼

所以,它等於這些語句中的一條:

a = b;
a = obj.b;
obj.a = b;
obj.a = obj.b;
複製程式碼

通過閱讀程式碼,你不可能辨別出你會得到的是這些語句的那一條。它可能隨著程式執行到下一步時發生變化。它甚至可能在程式執行過程中就發生了變化。如果你不能通過閱讀程式就瞭解它將做什麼,你就無法確信它會正確地做你想要做的事情。

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

來源:https://juejin.im/post/5b41d45be51d45199566ed78#comment

相關文章