JavaScript 中的分號

在 JavaScript 中,分號是可選的。我個人喜歡在我的代碼中避免使用分號,但許多人更喜歡使用它們。 JavaScript 中的分號在社群中存在著分歧。有些人喜歡無論如何都使用分號。其他人則喜歡避免使用它們。 在使用分號多年之後,在2017年秋季,我決定試著根據需要避免使用它們,並且我設置了 Prettier 來自動從我的代碼中移除分號,除非有特定的代碼結構需要它們。 現在我發現避免使用分號是自然的,我認為代碼看起來更好,並且更容易閱讀。 這一切都是因為 JavaScript 不強制要求使用分號。當存在需要分號的地方時,它會在幕後添加分號。 執行這一過程的機制稱為自動分號插入。 了解 JavaScript 自動分號插入的規則非常重要,以避免編寫可能產生錯誤的代碼,因為它並不按照您的期望方式運作。 JavaScript 自動分號插入的規則如下: 當下一行以打破當前行的代碼開始時(代碼可以跨越多行) 當下一行以 } 開始,結束當前的區塊 當到達源代碼文件的末尾 當單獨一行上存在 return 陳述式時 當單獨一行上存在 break 陳述式時 當單獨一行上存在 throw 陳述式時 當單獨一行上存在 continue 陳述式時 以下是一些不會按照您期望的代碼示例: 根據這些規則,這是一個示例: const hey = 'hey' const you = 'hey' const heyYou = hey + ' ' + you ['h', 'e', 'y'].forEach((letter) => console.log(letter)) 你會得到錯誤 Uncaught TypeError: Cannot read property 'forEach' of undefined,因為根據規則1,JavaScript 試圖將代碼解釋為: const hey = 'hey'; const you = 'hey'; const heyYou = hey + ' ' + you['h', 'e', 'y']....

JavaScript 中的命名空間

什麼是命名空間? 命名空間是將一組實體、變量、函數、對象包裝在單一的總體下的行為。 JavaScript 有多種方式來實現命名空間,通過看例子可以更容易地理解這個概念。 創建命名空間的最簡單方式是通過創建一個物件文字: const car = { start: () => { console.log('start'); }, stop: () => { console.log('stop'); } }; 這樣,start 和 stop 就被命名為 car 的子集:car.start() 和 car.stop()。 它們不會汙染全局物件。 為什麼這很重要?一個好的理由是沒有任何東西能干擾它們。 這種方式還可以在對象創建後將變量分配給該對象: const car = {}; car.start = () => { console.log('start'); }; car.stop = () => { console.log('stop'); }; 但是它們仍然可以從外部訪問,這要歸功於 car 對象引用。 從外部完全隱藏此代碼的最佳方法是將其包裹在一個區塊中,這是一段用花括號包裹的代碼,就像 if 或 for 區塊一樣,但也可以形成獨立的區塊,像這樣: { const start = () => { console.log('start'); }; const stop = () => { console....

JavaScript 中的基本類型 vs 物件

JavaScript 中的基本類型和物件有哪些主要區別呢? 首先,讓我們定義一下什麼是基本類型。 JavaScript 中的基本類型有: 字串 (String) 數字 (Number 和 BigInt) 布林值 (true 或 false) 未定義 (undefined) 符號值 (Symbol) null 是一種特殊的基本類型。如果你執行 typeof null,會返回 'object',但它實際上是一種基本類型。 除了基本類型之外的一切都是物件。 函數也是物件。我們可以在函數上設置屬性和方法。typeof 會返回 'function',但函數構造函數是來自於物件構造函數的。 基本類型和物件之間的主要區別有: 基本類型是不可變的,物件只有一個不可變的引用,但其值可以隨時間變化 基本類型是按值傳遞,物件是按引用傳遞 基本類型是按值複製,物件是按引用複製 基本類型是按值比較,物件是按引用比較 如果我們這樣複製一個基本類型: let name = 'Flavio' let secondName = name 現在,我們可以通過將 name 變量賦予一個新值來更改它,但 secondName 仍然保存著舊值,因為它是按值複製的: name = 'Roger' secondName //'Flavio' 如果我們有一個物件: let car = { color: 'yellow' } 然後將其複製到另一個變量: let car = { color: 'yellow' } let anotherCar = car 在這種情況下,anotherCar 指向與 car 相同的物件。如果你設置:...

JavaScript 中的關鍵字和保留字列表

這是 JavaScript 中所有的關鍵字和保留字列表。這些詞彙不能用作變數標識符。 await break case catch class const continue debugger default delete do else enum export extends false finally for function if implements import in instanceof interface let new null package private protected public return super switch static this throw try True typeof var void while with yield

JavaScript 中要避免的事情(糟糕的部分)

快速列出撰寫 JavaScript 程式碼時應避免的事情。 避免使用 new Object() 來建立新物件,改用物件字面語法 {}。 陣列也一樣,優先使用 [] 而非 new Array()。 除了在 if、switch、迴圈、try 等語句需要的情況下,盡量避免使用區塊。 永遠不要在 if 或 while 語句的條件部分進行賦值。 永遠不要使用 == 和 !=,改用 === 和 !==。 永遠不要使用 eval。為什麼?它會導致效能問題(執行解譯器/編譯器)、安全問題(若與使用者輸入一起使用,可能會造成代碼注入)和調試困難。 永遠不要使用 with,因為它會修改作用域鏈並導致混淆。 總是將函式傳遞給 setTimeout 和 setInterval。 永遠不要將 Array 用作關聯陣列,改用 Object。提供該功能的部分實際上是由 Object 的原型提供的,所以你實際上可以使用 Date 物件來達到同樣的目的。 不要在字串的末尾使用 \ 來建立多行字串,這不是 ECMAScript 的一部分。改用字串串接 ' string1 ' + ' string2 ' 代替。 永遠不要修改內建物件 Object 和 Array 的原型。修改其他物件的原型(例如 Function)時要小心,因為這可能導致難以調試的錯誤。

JavaScript 中逗號的一個奇特使用方式

最近,我在 JavaScript 中發現了一個奇怪但可能很有用的用法,與逗號運算子有關。 我通常使用逗號來分隔對象中的屬性或數組中的項目。 然而,我從未對在表達式內部使用逗號給予太多注意。 舉個例子: ('a', 'b') 這兩個表達式(在本例中為字符串)都會被求值,並且返回最後一個元素,即逗號之後的表達式。在這個例子中,它返回 'b'。 你可以將值賦給一個變量,像這樣: const letter = ('a', 'b') letter === 'b' // true

JavaScript 事件循環

事件循環是理解 JavaScript 最重要的方面之一。本文將以簡單的方式解釋它。 介紹 阻塞事件循環 調用堆棧 簡單的事件循環解釋 排隊執行函數 消息隊列 ES6 任務隊列 介紹 了解 JavaScript 的事件循環是非常重要的。 我已經用 JavaScript 編程多年了,但我從來沒有完全理解它的運作原理。不知道這個概念的詳細細節是完全正常的,但通常還是有助於了解它的運作方式,而且你可能對此有點好奇。 本文旨在解釋 JavaScript 的內部細節,介紹單線程如何處理異步函數。 JavaScript 代碼在單線程運行,一次只能做一件事。 這是一個實際上非常有用的限制,因為它簡化了編程,不必擔心並發問題。 只需要注意如何編寫代碼,避免阻塞線程的任何操作,如同步網絡調用或無限循環。 通常情況下,大多數瀏覽器每個瀏覽器選項卡都有一個事件循環,以使每個過程都是獨立的,避免無限循環或繁重處理的網頁阻塞整個瀏覽器。 環境管理多個並發的事件循環,以處理 API 調用。Web Workers 也在自己的事件循環中運行。 你主要需要關注的是,你的代碼會在單個事件循環中運行,要以此為依據編寫代碼,避免對其進行阻塞。 阻塞事件循環 任何需要太長時間才能將控制權歸還給事件循環的 JavaScript 代碼都會阻塞頁面中的任何 JavaScript 代碼的執行,甚至會阻塞 UI 线程,用戶無法進行點擊、滾動頁面等操作。 在 JavaScript 中,幾乎所有 I/O 原語都是非阻塞的。網絡請求、Node.js 文件系統操作等都是非阻塞的。阻塞是例外,這就是為什麼 JavaScript 在回調、近來在 promises 和 async/await 上投入了大量資源的原因。 調用堆棧 調用堆棧是一個 LIFO(「後進先出」)隊列。 事件循環不斷檢查 調用堆棧 是否有需要運行的函數。 在此過程中,它將堆棧中找到的所有調用放入調用堆棧並按順序執行。 你可能熟悉調試器或瀏覽器控制台中的錯誤堆棧跟踪,瀏覽器會在堆棧中查找函數名以通知你當前調用的函數: 簡單的事件循環解釋 舉個例子: 我將使用 foo、bar 和 baz 作為隨機名稱。請輸入任意名稱以替換它們 const bar = () => console....

JavaScript 代理对象

在使用对象时,我们可以创建一个代理对象来拦截和改变现有对象的行为。 我们使用在ES2015引入的Proxy原生对象来实现。 假设我们有一个car对象: const car = { color: 'blue' } 一个非常简单的例子是当我们尝试访问一个不存在的属性时返回一个‘Not found’字符串。 你可以定义一个代理对象,每当你尝试访问这个对象的属性时都会被调用。 你可以通过创建另一个对象来实现,这个对象具有一个get()方法,接收目标对象和属性作为参数: const car = { color: 'blue' } const handler = { get(target, property) { return target[property] ?? 'Not found' } } 然后我们可以通过调用new Proxy()来初始化我们的代理对象,传递原始对象和我们的处理程序: const proxyObject = new Proxy(car, handler) 现在尝试访问car对象中包含的属性,但是从proxyObject引用它: proxyObject.color //'blue' 这就像调用car.color一样。 但是当你尝试访问car上不存在的属性,比如car.test,你将得到undefined。使用代理,你将得到我们告诉它返回的'Not found'字符串。 proxyObject.test //'Not found' 我们在代理处理程序中不仅限于使用get()方法。这只是我们可以编写的最简单的例子。 我们还有其他可以使用的方法: apply当我们在对象上使用apply()时调用这个方法 construct当我们访问对象的构造函数时调用已经被执行 deleteProperty当我们尝试删除一个属性时被执行 defineProperty当我们在对象上定义一个新的属性时调用 set当我们尝试设置一个属性时被执行 等等。基本上我们可以创建一个受保护的门,来控制一个对象上发生的一切,并提供其他规则和控制来实现我们自己的逻辑。 我们还可以使用其他方法(也称为陷阱): enumerate getOwnPropertyDescriptor getPrototypeOf has isExtensible ownKeys preventExtensions setPrototypeOf 所有这些都对应于相应的功能。...

JavaScript 全域物件

JavaScript 提供一個全域物件,它包含一組全域使用的屬性、函式和物件,無需使用命名空間。 這些屬性包括: Infinity NaN undefined 這些函式包括: decodeURI() decodeURIComponent() encodeURI() encodeURIComponent() eval() isFinite() isNaN() parseFloat() parseInt() 以下為這些物件: Array Boolean Date Function JSON Math Number Object RegExp String Symbol 以及以下錯誤類型: Error EvalError RangeError ReferenceError SyntaxError TypeError URIError 我在JavaScript 錯誤參考文章中描述了這些錯誤。 現在我們來介紹一下全域屬性和函式。 Infinity Infinity 在 JavaScript 中代表無窮大。 要獲得負無窮大,可使用-運算子:-Infinity。 這些值等同於 Number.POSITIVE_INFINITY 和 Number.NEGATIVE_INFINITY。 將任何數字加上 Infinity,或將 Infinity 乘以任何數字,結果仍為 Infinity。 NaN 全域的 NaN 值代表非數字。它通常由零除以零、無效的 parseInt() 執行或其他運算返回。 parseInt() //NaN parseInt('a') //NaN 0/0 //NaN 特別注意的是,NaN 值永遠不等於任何其他 NaN 值。必須使用 isNaN() 全域函式來檢查值是否為 NaN。...

JavaScript 公開類字段

一個關於新的 JavaScript 公開類字段的簡單教程 在過去,要創建一個公開的類字段,我們會使用這樣的語法,在構造函數中實例化字段: class Counter { constructor() { this.count = 0 } } 新的類字段提案可以在 Chrome 72 和 Node 12 中使用,我們可以使用以下的語法: class Counter { count = 0 } 簡單得多!