By Viral Shah | Nov 26, 2018
js一門很容易入門但是很難精通的語言。我很認同這句話。這是因為js是一門古老的語言同時也是一門很靈活的語言。有著一堆神祕的語法和過時的功能。我已經使用js很多年了,迄今為止,我時不時地會發現一些我從未知道的隱藏語法或技巧。
我試圖列出一些鮮為人知的js特性。雖然有一些特性在嚴格模式下是不能用的,但是它依然是一種不錯的js特性。然而,請注意,我不建議你全使用上我介紹的特性。雖然它們很酷,但畢竟是鮮為人知的特性,你的同事可能會看不懂。
void 操作符
js有一元運算子。你可能已經見過了,像 void(0) 或者 void0 。它只有一個目的 - 在右邊評估表示式並返回undefined。使用0只是一種慣例。不一定要使用0.它可以是任何有效的表示式,如
void <expression>
複製程式碼
它仍然返回undefined。
為什麼要這樣子返回undefined,直接返回undefined不好麼?看起來這個特性很多餘,不是麼?
有趣的事實
好吧,事實證明,在ES5之前,你實際上可以在大多數瀏覽器中為undefined重新分配值。類似這樣
undefined =“abc”
複製程式碼
使用void的話會始終保持返回正確的undefined。
建構函式的括號是可選的
對的,你沒看錯,當呼叫建構函式的時候,我們在類名後面加的圓括號——完全是可選的!?(前提是你不需要傳遞引數)
下面的程式碼樣式都被認為是有效的JS語法,並且會給你完全相同的結果!
IIFE的括號可以被跳過
IIFE的語法讓我感覺總是有點奇怪。
IIFE所有的括號都有什麼用?
那些額外的括號只是為了告訴js解析器,即將到來的程式碼是函式表示式而不是函式。知道了這些,來想一想,有很多方法可以跳過這些額外的括號,仍然可以製作有效的IIFE。
void操作符告訴解析器這段程式碼是函式表示式。因此,我們可以省略包著函式的括號。你猜怎麼著?我們可以使用任何一元運算子(void,+ ,!, - 等),這仍然有效。
真酷!
然而,如果你觀察再仔細點,你會有個疑問,
一元運算子不會影響IIFE返回的結果嗎?
好吧,它會影響結果。但是有個好訊息,如果你關心返回的結果並且把結果放在某個變數中,那麼你首先不需要額外的括號。
這就對了!
我們新增這些括號只是為了更好的程式碼可讀性。
with 宣告
你知道js有with宣告塊?with一直是js的關鍵詞。語法如下:
with (object)
statement
// for multiple statements add a block
with (object) {
statement
statement
...
}
複製程式碼
with可以對傳遞過來的物件進行解析,在with 塊裡可以直接使用物件的屬性。
有趣的事實
with塊看起來很酷對不對?甚至比object destructuring還好。
但是,並不是這樣的。
通常不鼓勵使用with語句,因為它已被棄用。 在嚴格模式下完全禁止。 事實證明,with塊會增加語言中的一些效能和安全性問題。Bummer
建構函式
function語句並不是定義新函式的唯一方法;你可以使用【Function()】這個建構函式和new運算子動態定義函式。
建構函式的最後一個引數是字串化程式碼,就是你寫的函式。其他引數是該函式的引數。
有趣的事實
在js,Function建構函式是所有建構函式的建構函式。也包括Object的建構函式。並且Function建構函式的建構函式就是它自身。因此,呼叫object.constructor.constructor...足夠多次後,在js中,最終會在任何物件返回Function建構函式。
Function 的屬性
我們知道在js中,Functions是首個物件。因此,沒有人能阻止我們在Function物件上新增自己的屬性。這是可以的,但是很少會這樣做。
那有什麼場景會應用到呢?
有一些很好的用例。例如,
讓Functions可配置化
我們有個greet函式。我們想要讓我們的函式在不同的環境下列印不同語言的問候語。這個環境是可以配置的。我們可以在某處維護個全域性環境變數,或者使用上文所說的新增自定義屬性
Function的靜態變數
另一個相似的例子。假設你想要實現一個生成一系列有序數字的數字生成器。通常來說,你會用Class或者IIFE,在裡面維護一個計數變數,然後輸出有序的值。這樣我們就可以限制對計數器的訪問,並避免使用額外的變數來汙染全域性空間。
但是,如果我們希望靈活地讀取甚至修改計數器並且不汙染全域性空間呢?
好吧,我們仍然可以建立一個Class,帶有一個計數器變數和一些額外的方法來讀取它; 或者我們可以懶一點,只需在函式上使用屬性。
現在是中場休息時間,我們等你回來。如果你想接著繼續,你是一個勇敢的戰士,我向你致敬。
繼續吧
Arguments屬性
我相信大多數人都知道函式中的引數物件。 它是一個像物件一樣的陣列(譯者:emmm),在所有函式中都能訪問到。它具有在呼叫函式時傳遞的引數列表,也有其他一些有趣的屬性。
- arguments.callee:指向當前呼叫函式
- arguments.callee.caller:指向當前被調函式的呼叫者。
注意:雖然ES5禁止在嚴格模式下使用callee和caller,但在許多編譯庫中仍然常見。 所以,值得學習它們。
標記模版字面量
除非你是井底之蛙,你一定聽說過模版字面量。模版字面量時es6的一個很酷的補充。然而,你聽說過Tagged模版字面量麼(Tagged Template Literals)?
Tagged模板字面量允許您通過向模板字面量新增自定義標記來更好地控制將模板字面量解析為字串。Tagged只是一個獲取字串模板解析後的所有字串和值的陣列的解析器函式。Tagged函式最終返回字串。
在下面的示例中,我們的自定義標記 - highlight,解釋模板文字的值,並使用
<mark>
複製程式碼
元素將解釋的值包裝在結果字串中,以突出顯示。
Getter & Setter
在大多數情況下,js物件時很簡單的。比如說有一個物件叫user,我們嘗試使用user.age來讀取age屬性時,如果有定義並賦值了,就有返回。如果沒有,就返回undefined。簡單。
但是,它不一定非常簡單。 JavaScript物件具有Getters和Setter的概念。 我們可以編寫自定義的Getter函式來返回我們想要的任何內容,而不是直接返回物件上的值。 設定值也是一樣的。
這使我們可以在獲取或設定屬性時可以靈活的創造虛擬屬性、驗證屬性。
Getters和Setters不是es5的特性,它們是一直都存在的特性。es5只是新增了語法糖。
逗號操作符
JavaScript有一個逗號運算子。 它允許我們在一行中編寫由逗號分隔的多個表示式,並返回最後一個表示式的結果
// syntax
let result = expression1, expression2,... expressionN
複製程式碼
來看看上面的例子,這裡將會解析所有的表示式,並把result變數賦予expressionN返回的值。
你可能在迴圈裡面使用過了
for (var a = 0, b = 10; a <= 10; a++, b--)
複製程式碼
它可以幫助我們把語句寫在一行
function getNextValue() {
return counter++, console.log(counter), counter
}
複製程式碼
或者寫lamda表示式
const getSquare = x => (console.log (x), x * x)
複製程式碼
+ 加操作符
想要知道如何便捷地把字串轉換成number?
只要在字串前面加個+號。
Plus運算子也適用於負,八進位制,十六進位制,指數值。 更重要的是,它甚至可以將Date或Moment.js物件轉換為時間戳!
!! Bang Bang 操作符
好的,嚴格來講,它不是一個單獨的JavaScript運算子。 它只是兩次使用的JavaScript否定運算子。
但是Bang Bang聽起來很酷!Bang Bang是一個可以講任何表示式轉換成布林值的方法。
如果表示式是一個true,則返回true;否則返回false。
~ 位運算作符
來看一個沒人關注的位運算操作符。我們什麼時候用它呢?
好吧,有個日常用例。
當與數字一起使用時,Tilde運算子有效,像這樣
~N => -(N + 1)。
複製程式碼
僅當N == -1時,此表示式的計算結果為“0”
我們可以通過在indexOf(...)函式前面放置〜來進行布林檢查是否有一項存在。String或Array中都可以用這個。
注意:ES6和ES7分別在String&Array中新增了一個新的.includes()方法。當然,它比位運算運算子更清晰,以檢查專案是否存在於Array或String中。
標籤語句
js具有標籤語句的概念。它允許我們在js中命名迴圈和塊。然後,我們可以使用這些標籤在使用break或continue時使用。
帶標籤的語句在巢狀迴圈中特別方便。但是我們也可以使用它們來簡單地將程式碼組織成塊或建立一個可退出的塊
注意:不像其他的語言,js沒有goto語句。因此,我們只能使用labels來搭配break和continue使用。