JavaScript 通常被認為是最容易掌握的語言,也是最難掌握的語言。 我完全贊同。 這是因為 JavaScript 是一種非常古老且非常靈活的語言。 它充滿了神秘的語法和過時的功能。 我已經使用 JavaScript 多年了,迄今為止,我仍時不時地發現一些我從未知道的隱藏語法或技巧。
比如下圖,讓很多前端程式設計師感到很無奈。
我曾嘗試過列出一些鮮為人知的 JavaScript 特性, 儘管這些功能在嚴格模式下是無法使用的,但它們仍然是完美的JavaScript 代碼。請牢記,我並不建議使用全部的特性,這會讓你的同事變得暴躁儘管這些特性看起來很酷。
注意:我這裡談到的並不包括,提升,閉包,代理,原形繼承,異步,生成器等。雖然以上這些特性不易理解,但是卻非常實用。
Void操作符
JavaScript 有一個一元的void操作符。你可能已經看到它被用作void(0)或void 0。它在生活中有一個目的——計算一個表達式並返回undefined。使用「0」只是一種約定。 你不必使用「0」, 它可以是任何有效的表達式,如void <expression>,它仍然返回undefined。
void 操作符
為什麼要創建一個特殊的關鍵字來返回undefined而不是直接返回undefined?聽起來有點多餘,不是嗎?有趣的事實好吧,事實證明,在ES5之前,你實際上可以在大多數瀏覽器中為原始未定義的類用undeunfined =「abc」分配新值。因此,定義未定義!在那些日子裡,使用void是一種確保的方法,你總是返回原來的undefined。
構造函數括號是可選的
是的,我們在調用構造函數時在類名後添加括號 - 完全可選!(前提是您不需要將任何參數傳遞給構造函數)
下面的代碼樣式都被認為是有效的JS語法,並且會給你完全相同的結果!
構造函數括號是可選的
可以跳過立即調用的函數表達式括號
對於我來說,立即調用的函數表達式的語法總是有點奇怪.
所有括號都有什麼用?
事實證明,這些額外的括號僅僅是為了告訴JavaScript解析器,即將到來的代碼是一個函數表達式,而不是一個函數。可以想像,知道了這一點,有很多方法可以跳過這些額外的括號,仍然可以生成一個有效的立即調用的函數表達式。
void操作符告訴解析器代碼是函數表達式,因此,我們可以跳過函數定義的括號。
你猜怎麼著?我們可以使用任何一元運算符(void, +, !, -, etc.),這仍然有效!
這太酷了!
但是,如果你是一個敏銳的觀察者,你可能會想,
一元運算符不會影響IIFE返回的結果嗎?
那麼,它會影響結果。 但好消息是,如果您關心結果並說您將其存儲在某個變量中,那麼您首先不需要額外的括號。
確實如此!
IIFE 的返回
我們添加這些括號只是為了更好的可讀性。
如果想深入了解 IIFE,請查看Chandra Gundamaraju 的這篇很酷的文章。
With語句
你知道JavaScript有with語句塊嗎?事實上with是JS中的一個關鍵詞。編寫with語句塊的語法如下
with (object) statement // for multiple statements add a blockwith (object) { statement statement ...}
with會把傳入對象的所有屬性添加進來, 並在其作用域內計算語句的值.
with 塊例子
Fun FactWith聽起來很酷, 不是嗎? 甚至好過 object destructuring 語句.其實不然.with 語句一般不鼓勵使用, 因為它已被廢棄. 在嚴格模式下,它是被完全禁止使用的. 事實證明, with 帶來了性能和安全問題. 真倒黴!
函數構造器
function語句不是唯一的方式來定義新的函數; 你也可以使用Function() 構造器和 new 操作符來動態地定義函數.
使用Function構造器的動態函數
最後一個構造器參數是函數體轉換字符串以後的代碼,它之前的參數是函數的參數.
Fun Fact在JavaScript中,Function構造器是所有構造器的父類. 甚至Object的構造器也是 Function 構造器. 並且Function自己的構造器是Function本身. 不過, 在JavaScript中,任何對象調用object.constructor.constructor...多次後,最終會返回一個Function構造器 .
函數屬性
我們都知道函數在 JavaScript 中是一等公民對象。因此,我們可以自由地給函數對象添加自定義屬性。這在 JS 中絕對沒有問題,但是很少有人會這樣做。
那麼,什麼時候我們應該這樣做呢?
這裡會講到一些不錯的使用場景,比如:
配置函數
假如我們有一個叫 greet的函數,我們希望它能根據不同的地域設置列印出不同的問候語,而位置是可配置的。我們當然可以配置一個全局變量來做這個事情,不過也可以像下面的代碼這樣,使用函數屬性來實現這一功能:
有 locale 屬性的 greet 函數
帶有靜態變量的函數
還有一種類似的用法。如果你想實現一個數字生成器,它會按順序產生一系列的數。一般情況下,可以使用帶有靜態變量的類或IIFE,使用這個靜態變量來保存產生的最後一個數。使用這種方法,我們可以限制對計數器的訪問,並且可以避免全局汙染。
但是如果我們想靈活地讀取甚至修改計數器,同時又能保持沒有全局汙染,應該怎麼辦呢?
我們仍然可以創建一個類,使用計數器變量並通過特殊的方法來讀取它;我們也可以用簡單一點的辦法,使用函數上的屬性。
具有 counter 屬性的 generateNumber 函數
哎呀!這個清單很長,我們已經快進行到一半了。如果你想休息一下,現在就是個不錯的時機。如果你仍然精神振奮,我向你致敬,鬥士。繼續!
arguments 的屬性
我相信大多數人都知道函數中的
arguments
對象。這是一個數組形式的對象,所有函數中都可用。這個對象包含了函數調用時傳入的所有參數列表。但除此之外,它還有一些很有意思的屬性,
arguments.callee: 引用當前調用的函數(即自身)arguments.callee.caller: 引用調用當前函數的那個函數(即調用者)
callee & caller
注意:雖然 ES5 在嚴格(strict)模式下禁止使用callee和caller,但他們仍然普遍存在於很多已經編譯的庫中。所以,學習他們並非毫無意義。
帶標籤的模板字符串
如果你不是生活在原始社會,那就一定聽說過模板字符串。模板字符串是 ES6 中新增的眾多優秀特性之一。不過,話說回來,你知道帶標籤的模板字符串嗎?
模板字符串
帶標籤的模板字符串能讓你更好地控制從模板解析成字符串的過程,它比普通的模板字符串只是多了一個自定義的標籤。這個標籤代表著一個解析函數,它會得到從模板字符串解析出來的字符串和值列表,通過邏輯處理返回最終的生成的字符串。
下面的示例中,我們定義一個標籤 —— highlight,它會將模板字符串解析出來的值處理成 <mark> 元素,使之高亮。
帶標籤的模板字符串 - highlight
已經存在不少庫在使用這個有趣的特性,比如下面這些,React 的 styled-components用於翻譯和國際化的 es2015-i18n-tag用於為輸出上色的chalk
Getters & Setters
在大多數情況下, javascript 對象很簡單。假設我們有一個
user
對象, 我們試圖訪問其上的age屬性,可使用
user.age
我,如果其已被定義,我們將得到age屬性的值, 否則,我們將得到undefined。簡單吧。
但是, 不一定非要這麼簡單。javascript對象有Getters和Setters的概念。我們可以編寫自定義Getter函數來返回所需的任意內容, 而不是直接返回對象上的值。設置值也可同樣處理。
這使我們在獲取或設置欄位時能夠擁有強大的概念, 如虛擬欄位、欄位校驗、副作用
ES5 Getters & Setters
Getters & Setters並不是ES5新加的;他們一直都在那裡。ES5 只是向現有功能添加了便捷的語法。要了解更多關於Getters & Setters的信息, 請參閱這篇文章
Colors,一個流行的node.js庫,是使用Getter的很棒的例子。該庫e擴展自String類,並在其上添加了大量Getter方法。這允許我們將任何字符串轉換為其彩色化版本, 以便於日誌記錄, 僅需簡單訪問它對應的屬性即可。
逗號運算符
javascript 支持逗號運算符。它允許我們在一行中編寫用逗號分隔的多個表達式, 並返回最後一個表達式的結果
// 語法let result = expression1, expression2,... expressionN
在這裡, 將對所有表達式進行計算, 並為結果變量賦值為expressionN的返回值。
你可能已經在 for 循環中使用過逗號運算符了
for (var a = 0, b = 10; a <= 10; a++, b--)
有時在一行中編寫多個語句,它是很有所幫助
functiongetNextValue() { return counter++, console.log(counter), counter}
或編寫簡短的lamda函數
const getSquare = x => (console.log (x), x * x)
+ 加號運算符
你曾經需要將字符串快速轉換為數字嘛?
只要在字符串前面添加+運算符前綴即可。加號運算符也適用於負號、八進位、十六進位、指數值。另外,它甚至可以將日期或Moment.js對象轉換為時間戳!
加號運算符
!! 砰砰運算符
好吧, 從技術上講, 這不是一個獨立的javascript 運算符。這只是使用了兩次的javascript否定運算符。
但砰砰聽起來太酷了!砰砰或雙砰是將任意表達式轉換為布爾值的巧妙技巧。
如果表達式是真值, 它返回true;。否則它返回false。
砰砰運算符
~ 波浪運算符
讓我們面對現實吧--沒有人關心位運算符。我們什麼時候才能使用它呢!
好吧, 有一個波浪或位否定運算符的日常用例。
原來, 當與數字一起使用時, 波浪運算符有效地執行了
~N => -(N+1)
。只有當
N == -1
時, 此表達式的計算結果為 "0"
我們可以通過將~放在
indexOf(...)
函數前面來執行布爾檢查, 是否某個項存在於一個字符串或數組之中。
使用波浪運算符的indexOf函數
注: ES6 & ES7分別在String及Array中添見了新的.includes()方法。當然, 它是一種比波浪運算符更簡潔的方式來檢查某項是否存在於數組或字符串中。
帶標籤的語句
javascript中有
label
語句的概念。它允許我們在 javascript 中命名循環和塊。然後, 我們可以使用這些標籤在後續使用
break
或
continue
時引用此代碼。
在嵌套循環中, 帶標記的語句特別方便。但我們也可以使用它們簡單地將代碼組織到塊中或創建一個可中斷的塊
帶標籤的語句
註:不同於其他語言,JavaScript並不支持goto建構。因此,我們只能在標籤中使用break和continue。