JavaScript中的提升機制是什麼?

對於JavaScript編程語言中的提升機制做一個簡要解釋。 在執行JavaScript代碼之前,它會解析代碼並將所有的函數和變量聲明添加到它自己的內存中,並將它們保存在內存中。這就是所謂的提升機制。 對於函數聲明和函數表達式,我們有不同的行為。 對於函數聲明,我們可以在函數定義之前調用函數,並且我們的代碼將正常運行。但對於其他情況,我們將會得到錯誤。 一個通用的法則是在使用之前,總是先定義函數、變量、對象和類,以避免出現意外情況。 假設我們有一個函數: function bark() { alert('wof!') } 由於提升機制,我們可以在它被聲明之前調用bark(): bark() function bark() { alert('wof!') } 這種情況只適用於函數聲明,就像上面的例子一樣。 而對於函數表達式則不同。 這是一個函數表達式: bark() var bark = function() { alert('wof!') } 在這種情況下,var聲明會被提升並初始化為undefined,類似於下面的代碼: var bark = undefined bark() bark = function() { alert('wof!') } 運行此代碼將引發TypeError: bark is not a function錯誤。 const和let聲明也會被提升,但它們不像var一樣被初始化為undefined。 const bark = function() { alert('wof!') } 或者 let bark = function bark() { alert('wof!') } 在這種情況下,如果在聲明之前調用bark(),將引發ReferenceError: Cannot access 'bark' before initialization錯誤。...

JavaScript中的物件解構是什麼?

什麼是「物件解構」,解構操作的結果是什麼? 假設你有一個包含一些屬性的物件: const person = { firstName: 'Tom', lastName: 'Cruise', actor: true, age: 57 } 你可以提取物件中的一部分屬性,並將它們放入指定的變數中: const { firstName, age } = person; 現在,我們有了兩個新的變數firstName和age,它們包含了所需的值: console.log(firstName); // 'Tom' console.log(age); // 57 變數的值分配不取決於我們列出它們的順序,而是根據屬性名稱。 你也可以自動將一個屬性分配給另一個具有不同名稱的變數: const { firstName: name, age } = person; 現在,不再有一個名為firstName的變數,而是有一個名為name的變數,它包含person.firstName的值: console.log(name); // 'Tom'

JavaScript中的自定义事件

我们编写的许多代码涉及对事件的反应,例如I/O事件(如鼠标点击或键盘事件)和网络事件(当您监听HTTP调用时)。这就是我所说的内置事件。 在JavaScript中,我们可以创建自定义事件,并且它在浏览器和Node.js中的工作方式不同。 在前端,我们使用浏览器提供的Event对象: const anEvent = new Event('start'); 您可以使用以下代码触发事件: document.dispatchEvent(anEvent) 当发生这种情况时,事件监听器将被触发: document.addEventListener('start', event => { console.log('started!') }) 您可以使用内置的CustomEvent对象发送自定义数据,而不是使用Event对象,该对象接受一个对象作为第二个参数: const anotherEvent = new CustomEvent('start', { detail: { color: 'white' } }) 在事件监听器中,您可以使用event.detail从事件对象中获取数据(您不能使用其他属性): document.addEventListener('start', event => { console.log('started!') console.log(event.detail) }) 在后端,Node为我们提供了一种使用events模块构建类似系统的选项。 这个模块特别提供了EventEmitter类,我们将使用它来处理我们的事件。 使用以下代码初始化: const EventEmitter = require('events') const eventEmitter = new EventEmitter() 该对象提供了许多其他方法,其中包括on和emit方法。 emit用于触发事件 on用于在事件触发时添加要执行的回调函数 例如,让我们创建一个start事件,并作为示例,我们只是将其记录到控制台: eventEmitter.on('start', () => { console.log('started') }) 当我们运行以下代码时: eventEmitter.emit('start') 事件处理函数将被触发,并且我们会得到控制台日志。 您可以通过将它们作为额外参数传递给emit()来将参数传递给事件处理程序: eventEmitter.on('start', (number) => { console.log(`started ${number}`) }) eventEmitter....

JavaScript在這十年的發展

回顧過去十年的JavaScript和Web的發展,真是一段驚心動魄的旅程。 儘管我書架上有一些1998年的JS書籍,但2010年時我並沒有寫很多JavaScript。當時我主要使用Mootools和jQuery插件。可能有些JS代碼是我寫的,但並沒有什麼突破性的創新。 那時的JavaScript絕對不被視為熱門語言。它的主要用途是在像GMail、Google Maps等有大量預算的項目中進行一些高級工作。 對於大多數人來說,用JavaScript編寫整個應用程序的概念當然是陌生的。 快進到2019年12月31日,JavaScript可真是太普遍了。 JavaScript無所不在。在過去的10年中,它經歷了多次更新,包括一個主要更新(ES6),如今我們寫JavaScript的方式與2010年完全不同。 異步和等待、箭頭函數、Promises、生成器、const/let、類、模板字面量等,肯定使現代JavaScript看起來和行為也非常不同。 ES模塊使大型應用程式更易於撰寫和維護。 但這不僅僅是語法和語言的新特性改變了。 我認為,這十年最大的變化之一就是構建工具的引入和廣泛使用。從Grunt到Gulp再到Webpack、Parcel和Rollup,工具的發展如此迅速,作為開發者,我們每天都有越來越大的能力。 模塊打包器為我們提供了非常高級的功能,如樹搖篩檢。了解這些技術是如何從早期發展至今真是令人驚嘆。 我們要提到Node.js嗎?從技術上講,Node在2009年春季首次推出,所以並不屬於這個十年。但可以肯定的是,Node第一年並沒有取得很大的突破,但在這十年中卻取得了巨大的成功。 現在讓我們來談談瀏覽器。2010年1月的IE版本是8,其佔有率超過50%並且Edge還不存在。Chrome作為一歲的瀏覽器佔有率僅為5%,因為1.0版本是在2008年12月發布的。你能想像嗎?如今Chrome是最受歡迎的瀏覽器,遠超其他瀏覽器。據我所知,64%的互聯網使用Chrome,而Safari佔有16%。 說到Safari,在2010年1月,iPhone 3GS已經推出(我當時沒有,我還用著Nokia。我第一部iPhone是在那一年晚些時候推出的iPhone 4)。那時JavaScript在該設備上執行速度可能不會很快。但如今移動瀏覽器可以以閃電般的速度執行JavaScript,並且JavaScript被用於使用Cordova、Ionic、React Native等令人驚嘆的框架來構建移動應用程序。 npm於2010年1月推出,它的崛起可謂驚人。起初它只是一個Node.js模塊的包管理器,如今npm也成為了前端開發的事實標準。它在去年6月份超過了一百萬個包,我敢肯定它是世界上最大的軟件目錄。 順帶一提,GitHub在2010年1月剛剛存在1年半的時間。看看它當時的樣子真是有趣。 在這十年中,許多驚人的項目應運而生。我可以提到Ember.js、CoffeeScript、Angular、React,只舉幾個例子。 我有幸參與並加入了許多不同的社區,而JavaScript和整個生態系統之所以在這十年中發展如此迅速,是因為工作在其中的人們。 憑著熱情、勤奮、承諾和慷慨精神,開源社區以及成百上千的充滿企圖心的公司,使JavaScript這個開發世界的一角成為今天的模樣。 回顧過去,看到我們走了多遠真是令人愉悅。 我真的無法想像這個未來十年會帶給我們什麼。

JavaScript數學庫

Math物件包含了很多與數學有關的工具。本教程將介紹所有這些工具。 Math物件包含了很多與數學有關的工具。 它包含了常數和函數。 常數 項目 說明 Math.E 自然對數的底數e(約等於2.71828) Math.LN10 表示底數為10的自然對數 Math.LN2 表示底數為2的自然對數 Math.LOG10E 表示以10為底的對數的底數值 Math.LOG2E 表示以2為底的對數的底數值 Math.PI 圓周率π(約等於3.14159) Math.SQRT1_2 值為2的平方根的倒數 Math.SQRT2 值為2的平方根 函數 所有這些函數都是靜態的,Math物件無法實例化。 Math.abs() 返回一個數字的絕對值 Math.abs(2.5) //2.5 Math.abs(-2.5) //2.5 Math.acos() 返回操作數的反餘弦值 操作數必須在-1和1之間 Math.acos(0.8) //0.6435011087932843 Math.asin() 返回操作數的反正弦值 操作數必須在-1和1之間 Math.asin(0.8) //0.9272952180016123 Math.atan() 返回操作數的反正切值 Math.atan(30) //1.5374753309166493 Math.atan2() 返回兩個參數的商的反正切值 Math.atan2(30, 20) //0.982793723247329 Math.ceil() 向上取整 Math.ceil(2.5) //3 Math.ceil(2) //2 Math.ceil(2.1) //3 Math.ceil(2.99999) //3 Math.cos() 返回以弧度表示的角度的餘弦值 Math.cos(0) //1 Math.cos(Math.PI) //-1 Math.exp() 返回以Math.E為底,指數為參數的指數函數值 Math.exp(1) //2.718281828459045 Math.exp(2) //7....

JavaScript數組資料結構

數組是任何編程語言中最基本的資料結構。 數組在大多數(如果不是所有)編程語言中都是內建的。 讓我們來談談數組在大多數低級編程語言(如C語言)中的表示方式:它們表示計算機內存中一組連續的存儲單元。 從一個內存單元開始(如果你想的話,可以將這些單元想象成電子表格中的單元格),我們可以通過使用10個連續的存儲單元來創建一個包含10個插槽的數組。 這使我們能夠進行諸如通過知道第一個插槽(# 0)的內存地址,然後簡單地將 2 添加到該地址來訪問插槽#2之類的操作。 在JavaScript中,我們在更高層次上進行工作,數組的工作方式也不同。我們無法像C或其他低級語言那樣直接訪問內存,因此我們無法進行這種類型的數組數學運算。 低級語言中的數組只能存儲一種特定的數據類型,因此我們可以預先計算出數組將占用多少內存,以便將其安全地存儲在可以容納它的計算機內存中。 在JavaScript中,數組可以存儲任何類型的數據,並且可以混合使用。我們可以有一個數字,然後是一個物件,然後是另一個數組。 使用以下語法初始化數組: const myArray = [] 或者 const myArray = new Array() 這兩者沒有區別,但我更喜歡使用簡寫語法[]。 在JavaScript中,我們不需要在創建時指定數組大小,但是我們可以這樣做: const myArray = new Array(10) 然後我們可以填充數組的值: let val = 1 for (const [i, v] of myArray.entries()) { myArray[i] = val++ } 您可以使用以下語法引用數組中的第一個項目: myArray[0] (索引從0開始),並且可以通過增加索引號獲取數組中的每個後續項目: myArray[4] //5 myArray[3 + 4] //8 您可以使用以下語法更改任何位置的項目值: myArray[3] = 'Another item' JavaScript中的數組在內部是對象,因此它們具有方法。您可以使用 push方法將一個項目添加到數組的末尾: myArray.push(11) 您可以使用splice()方法(不要與slice()混淆)在任何位置添加項目。 在開頭: myArray.splice(0, 0, 'new item') 在索引3處: myArray.splice(3, 0, 'new item') 您可以使用以下語法從數組的末尾刪除一個項目:...

JavaScript標記語句

一個關於JavaScript中一個很少使用的功能的教程:標記語句 JavaScript有一個相對較為不知名的功能,它允許你對語句進行標記。 我最近在Svelte中看到這個功能的使用,它用於創建反應式聲明,這些聲明在語句中聲明的變量發生變化時重新計算: $: console.log(variable) 它們還允許使用語句塊,這是JavaScript的另一個特性,它允許你在可以定義語句的地方定義一個塊: $: { console.log(variable) console.log('another thing') //... } 這可能看起來很奇怪,但這是正確的JavaScript。這個語句塊被賦予了$ 標記。 Svelte編譯器在內部使用這個功能來創建反應式聲明。 我從未在其他地方使用過這個功能,但它的主要用例是在不是最近的循環或switch的語句中跳出。 這裡有一個簡單的例子來解釋我的意思。 在這些點中的任何一個位置調用break,都會跳出switch,以避免運行其他case: for (let y = 0; y < 3; y++) { switch (y) { case 0: console.log(0) break case 1: console.log(1) break case 2: console.log(2) break } } 這將正確地輸出0 1 2到控制台。 但是,如果我們想在reache到case 1時跳出for循環呢?這是如何做到的: loop: for (let y = 0; y < 3; y++) { switch (y) { case 0: console.log(0) break case 1: console....

JavaScript演算法:二分搜尋

二分搜尋是假設要搜尋的陣列(或其他資料結構)已經排序完成。 我們從陣列和要搜尋的項目開始。 我們查看陣列的中間。我們將元素數量除以2,想像一下左邊有一部分陣列,右邊有另一部分。 如果我們的項目比正在尋找的項目小,那麼它一定在右邊的部分,因此我們可以完全捨棄右邊的部分。 然後我們執行相同的操作,將陣列的右半部分除以2,查看中間的項目,並且我們捨棄陣列的一部分。 最後,你將得到該項目(如果沒有找到該項目,將返回null)。 最後,如果陣列有8個項目,我們在最壞的情況下將在最多4步內找到該項目。 如果陣列有32個項目,在最壞的情況下我們最多需要6步。相較於線性搜尋的32步,這是一個巨大的優化! 二分搜尋的時間複雜度為O(log n)。 以下是一種可能的實現方式: const binarySearch = (list, item) => { let low = 0 let high = list.length - 1 while (low <= high) { const mid = Math.floor((low + high) / 2) const guess = list[mid] if (guess === item) { return mid } if (guess > item) { high = mid - 1 } else { low = mid + 1 } } return null //如果未找到 } 這如何運作?我們得到list陣列和要搜尋的項目值。然後我們最初將low值設置為0,並將high值設置為陣列的最後一個索引。我們首先查看中間項目,如果它就是我們正在尋找的項目,我們將返回它,並結束。如果不是,我們將low或high值設置為只查看陣列的左半部分或右半部分,並且重複這個過程直到找到該項目。...

JavaScript演算法:快速排序

快速排序是比選擇排序更有效率的搜尋演算法,它利用了遞迴的概念。 遞迴表示我們在同一函式中呼叫了該函式本身。這是一種非常有用的技巧,在某些情況下很適用,而這正是其中之一。 我說“在大多數情況下”,是因為正如我們將看到的,最壞的情況下,泡沫排序所需要的時間可能與選擇排序相同:O(n^2)。但在最佳情況下,它的運行時間會是O(n log n),落在O(n)和O(n^2)之間。 它是如何運作的呢?給定一個數組,我們選擇一個項目作為主軸。然後將比主軸小的項目和比主軸大的項目分開。 接著我們對組成比主軸小和比主軸大的項目的兩個數組進行相同的操作。 通過代碼來看會更容易理解: const quickSort = (originalList) => { const list = [...originalList] if (list.length < 2) { return list } const pivot = list[0] const smaller = list.filter((item) => item < pivot) const bigger = list.filter((item) => item > pivot) return [...quickSort(smaller), pivot, ...quickSort(bigger)] } 在這個例子中,我選擇將主軸設置為數組中的第一個項目,但也可以選擇中間的項目,例如: const pivot = list[Math.floor(list.length / 2)] 注意我們首先複製了數組,所以調用quickSort()不會修改原始數組,它只會返回一個新的排序過的數組: const a = [1, 6, 3, 4, 5, 1, 0, 4, 8] console....

JavaScript演算法:氣泡排序

氣泡排序是一種簡單的排序演算法,但它的效率相對較低,最壞的情況下的時間複雜度為 O(n^2)。 儘管如此,學習它是值得的。 我們遍歷一個數組,將每個元素與其旁邊的元素進行比較。 如果右邊的元素比較小,我們就交換它們的位置。 以下是我們的實現: const bubbleSort = (originalArray) => { let swapped = false const a = [...originalArray] for (let i = 1; i < a.length - 1; i++) { swapped = false for (let j = 0; j < a.length - i; j++) { if (a[j + 1] < a[j]) { ;[a[j], a[j + 1]] = [a[j + 1], a[j]] swapped = true } } if (!swapped) { return a } } return a } 你可以看到,O(n^2) 是因為我們對數組進行了兩次循環,以檢查是否需要將項目與右邊的項目交換位置。...