JavaScript中的延遲和承諾(+ Ember.js示例)

承諾是一種相對較新的處理異步的方式,它可以非常有助於結構化代碼。 警告:此帖子已經過時並且可能不反映最新的技術水平。 請查看我的Promises guide和我的async/await guide。 承諾是事件的對象表示方式。在其生命週期中,當調用時,承諾從待定狀態轉換為已解決或已拒絕狀態,或者它也可以永遠保持待定狀態而不解決。 它是一種對JavaScript事件的新方法,但我認為它生成的代碼更易讀且更少出錯。目前,在JavaScript中有兩種稍有不同的主要承諾實現:遵循Promises/A規範的庫和jQuery。 首先,我將考慮jQuery,因為它無處不在且我使用它,所以如果您不希望使用另外一個外部庫,可以使用它。 介紹jQuery Promise 讓我們介紹延遲的概念。首先,延遲是一個承諾,除了您可以觸發一個延遲(解決或拒絕它)之外,通過承諾,您只能添加回調,並且它將由其他東西觸發。如果您希望,承諾是延遲的“只收聽”部分。 這是一個清晰的例子: var promise = $('div.alert').fadeIn().promise(); 您現在可以添加.done()和.fail()來處理回調。這只是一個調用示例,使用承諾的動畫已經成為jQuery 1.8中的一個真正的東西,同時帶有進度的回調。 另一個例子是AJAX調用: var promise = $.get(url); promise.done(function(data) {}); 延遲是您可以創建的東西,設置回調並解決的東西,例如: var deferred = new $.Deferred(); deferred.done(function(data) { console.log(data) }); deferred.resolve('some data'); 延遲的狀態可以使用.resolve()或.reject()觸發。一旦延遲狀態已經更改為最終階段(已解決/已拒絕),它就無法再改變。 var deferred = new $.Deferred(); deferred.state(); // "pending" deferred.resolve(); deferred.state(); // "resolved" 我們可以將以下回調附加到一個承諾: .done() // 當承諾成功執行時運行 .fail() // 當承諾失敗時運行 .always() // 無論什麼情況下都運行 可以使用.then()一起調用這些回調,如下所示: promise.then(doneFunc, failFunc, alwaysFunc); 這只是對承諾和延遲的jQuery實現的介紹。讓我們寫一些真實世界的例子。 (如果在node中執行,可以使用$ = require('jquery');導入jQuery) 一些jQuery示例 例如,這裡我們執行一個函數,當它完成時調用dfd.resolve()。類似於使用回調,但更具結構性和可重用性。...

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中的值是傳值還是傳址?

答案是:取決於具體情況! 原始資料類型是按值傳遞。 物件是按址傳遞。 原始資料類型包括數字、字串、布林值、null、undefined和符號。 除了原始資料類型外,其他都是物件。陣列是物件。函式是物件。 當你將一個數字傳遞給一個函式時,它將被複製到該函式內部: const increment = num => { num = num + 1 } const num = 2 increment(num) console.log(num) //2 如果你傳遞一個物件,它將按址傳遞,所以如果你修改其中一個屬性,原始物件也會被修改: const increment = num => { num.value = num.value + 1 } const num = { value: 2 } increment(num) console.log(num.value) //3

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中的箭头函数与普通函数有何不同

箭头函数与普通函数在JavaScript中有着不同的使用方式和特点。 普通函数是我们在JavaScript中一直以来使用的“老派”函数: function run() { } 它们可以直接调用: run(); 或者赋值给一个变量: const run = function run() { } run(); 在赋值给变量时,函数也可以是匿名的: const run = function () { } run(); 唯一的区别是,在出现错误时,当我们查看堆栈追踪信息时,将不再显示函数名。 箭头函数是在2015年ES6中引入的,它们有点像普通函数的最后一个版本,因为它们不具有名称。从来没有。 它们的语法比较简洁: const run = () => { } run(); 如果只有一个参数,我们可以省略括号: const run = param => { } run(); 如果只有一条语句,我们还可以省略大括号: const run = param => 'running'; run(); 在这种情况下,返回值是字符串'running'。 箭头函数和普通函数都可以用作对象的方法。 现在我们来谈谈这两种函数之间最大的区别,即与方法中绑定this有关。 考虑以下示例: const car = { brand: 'Ford', model: 'Fiesta', start: function() { console.log(`Started ${this....

JavaScript中的類型轉換

如何在JavaScript中從一個類型轉換為另一個類型 即使JavaScript是一個弱類型的語言,你可能需要將一個值從一個類型轉換為另一個類型。 在JavaScript中,我們有以下的原始類型: Number String Boolean Symbol 以及物件類型: Object (還有null和undefined,但不需要將它們轉換) 例如,你可能想要將以下類型進行轉換: 從數字轉換為字符串 從字符串轉換為數字 從字符串轉換為布爾值 從布爾值轉換為字符串 …等等。 以下是你可以使用的技巧將一種類型轉換為另一種類型。我將介紹最常見的情況。 將值轉換為字符串 通常將任何值轉換為字符串通常只需要調用該值的toString()方法,JavaScript將創建一個與該類型對應的字符串值。或者你可以將任何值傳遞給全局函數String()。 從數字轉換為字符串 使用全局函數String()或者數字類型的toString()方法: String(10) //"10" (10).toString() //"10" 從布爾值轉換為字符串 使用全局函數String()或者布爾值類型的toString()方法: String(true) //"true" true.toString() //"true" String(false) //"false" false.toString() //"false" 從日期轉換為字符串 使用全局函數String()或者日期類型的toString()方法: String(new Date('2019-01-22')) //"Tue Jan 22 2019 01:00:00 GMT+0100 (Central European Standard Time)" (new Date('2019-01-22')).toString() //"Tue Jan 22 2019 01:00:00 GMT+0100 (Central European Standard Time)" 字符串的特殊情況 String(null) //"null" String(undefined) //"undefined" String(NaN) //"NaN" 將值轉換為數字 從字符串轉換為數字 我們可以使用Number()全局函數來完成這個操作,這個函數可以將字符串轉換為數字:...

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物件屬性

專案需要了解關於JavaScript物件屬性的所有資訊。 JavaScript物件 擁有屬性,結構是由標籤和數值所組成的。 我們所見到的物件文字語法: const car = { } 讓我們能夠像這樣定義屬性: const car = { color: '藍色' } 這裡我們有一個名為car的物件,擁有一個名為color,具有數值藍色的屬性。 標籤可以是任何字串。注意到我在color周圍沒有使用引號,但是如果我想要在屬性名稱中包含無法當作變數名稱的字元,我必須這樣寫: const car = { color: '藍色', 'the color': '藍色' } 這意味著空格、連字號和其他特殊字元。 就像你看到的,我們用逗號分隔每個屬性。 檢索屬性的數值 我們可以使用2種不同的語法檢索屬性的數值。 第一種是點表示法: car.color //'藍色' 第二種,適用於具有無效名稱的屬性,是使用方括號: car['the color'] //'藍色' 如果您存取一個不存在的屬性,您將得到 undefined: car.brand //undefined 檢查屬性值但預設為預定義值的一個好方法是使用 || 運算子: const brand = car.brand || '福特' 正如前面所述,物件可以擁有嵌套的物件作為屬性: const car = { brand: { name: '福特' }, color: '藍色' } 您可以使用以下方法來訪問品牌名: car.brand.name 或 car['brand']['name'] 甚至可以混合使用:...

JavaScript的堆疊資料結構

堆疊是一種資料結構,相較於陣列具有更多限制。 我們只能將項目加入堆疊頂部,並且只能移除堆疊頂部的項目。 想像一下一堆書。你只能從頂部添加書,並且只能從頂部移除書。 因此,如果你添加了一堆書,然後想要存取第一本添加的書,你需要先移除所有書,直到到達第一本書。 這個概念被稱為「先進後出」(First In, Last Out,FILO)。 雖然JavaScript的陣列是內建的,不需要自己建立,但我們必須自己實現堆疊。 我們將創建一個封裝我們資料的資料結構,使其無法從外部訪問,只允許使用push()方法將資料添加到堆疊中,並使用pop()方法從堆疊中移除資料。 我們可以用多種不同的方式實現這一點。其中一種方式是使用類別,尤其是我要使用私有類別欄位。私有類別欄位尚不是JavaScript標準的一部分,但自版本12起在Chrome、Edge、Safari和Node.js中可用。但在Firefox中尚不可用,希望能夠早日支援。 為什麼我使用它們?因為它們使我們能夠非常容易地封裝類別的內部狀態並保護它免受外界的訪問。 class Stack { #items = [] push = (element) => this.#items.push(element) pop = () => this.#items.pop() isempty = () => this.#items.length === 0 empty = () => (this.#items.length = 0) size = () => this.#items.length } 我們有5個公共方法:push和pop用於將項目添加/移除堆疊,isempty用於檢查堆疊是否為空,empty用於清空堆疊,size用於獲取堆疊的大小。 現在,我們可以從這個類別建立堆疊物件並進行操作: const stack = new Stack() stack.push(1) stack.push(2) stack.push(3) console.log(stack.size()) //3 console.log(stack.pop()) //[ 3 ] console.log(stack.size()) //2 如果使用公共屬性,一切都會以相同的方式運作: class Stack { items = [] push = (element) => this....

JavaScript非同步程式設計與回呼函式(Callbacks)

JavaScript默認是同步的,並且是單線程的。這意味著程式碼無法創建新的線程並且並行運行。了解一下非同步程式碼的含義以及它是什麼樣子。 程式語言中的非同步性 JavaScript 回呼函式 處理回呼函式中的錯誤 回呼函式的問題 回呼函式的替代方案 程式語言中的非同步性 電腦的設計使其成為非同步的。 非同步意味著事情可以獨立於主程式流程發生。 在現代消費者電腦中,每個程式運行一段特定的時間片段,然後停止執行,以讓其他程式繼續執行。這個循環運行得非常快,我們認為我們的電腦同時運行許多程式,但這只是一種幻覺(除了在多處理器計算機上)。 程式內部使用中斷,這是發送給處理器以引起系統注意的信號。 我不打算深入探討這方面的內部細節,只是讓你記住,程式是非同步的很正常,它們在等待時會暫停執行,並且電腦可以在此期間執行其他任務。當程式在等待網絡響應時,它無法阻止處理器停止執行。 通常,程式語言都是同步的,一些語言提供了一種處理非同步的方式,可以在語言本身或通過函式庫中進行處理。C、Java、C#、PHP、Go、Ruby、Swift、Python等都是默認同步的。有些語言通過使用線程來處理非同步,而線程則產生新的進程。 JavaScript JavaScript默認是同步的,並且是單線程的。這意味著程式碼無法創建新的線程並且並行運行。 程式碼按照順序一行行執行,例如: const a = 1 const b = 2 const c = a * b console.log(c) doSomething() 然而,JavaScript誕生於瀏覽器中,最初的作用是響應用戶操作,比如onClick、onMouseOver、onChange、onSubmit等等。它如何在同步程式模型下實現這一點呢? 答案就在它的環境中。瀏覽器提供了一種處理此類功能的方式,它提供了一組API。 最近,Node.js引入了一個非阻塞的I/O環境,用於擴展此概念到文件訪問、網絡請求等等。 回呼函式 你無法知道用戶何時會點擊一個按鈕,所以你要做的是,為點擊事件定義一個事件處理函式。這個事件處理函式接受一個函式作為參數,在事件觸發時將調用該函式。 document.getElementById('button').addEventListener('click', () => { //當按鈕被點擊時執行 }) 這就是所謂的回呼函式。 回呼函式就是一個簡單的函式,它作為值傳遞給另一個函式,並且只有在事件發生時才會被執行。我們之所以能夠做到這一點,是因為JavaScript提供了一級函式的功能,這些函式可以被賦值給變量並且可以傳遞給其他函式(稱為高階函式)。 通常會將所有的用戶端代碼包裝在window對象上的load事件監聽器中,只有在頁面準備好時才會運行回呼函式: window.addEventListener('load', () => { //視窗已加載 //執行你想做的事情 }) 回呼函式無處不在,不僅僅在DOM事件中使用。 一個常見的例子是定時器: setTimeout(() => { //2秒後執行 }, 2000) XHR請求也接受回呼函式,這個例子中通過將一個函式賦值給一個屬性,在特定事件發生時調用該函式(在此例中,是請求狀態發生改變): const xhr = new XMLHttpRequest() xhr.onreadystatechange = () => { if (xhr....