摘要:突變引起狀態(tài)的改變。純函數(shù)和副作用純函數(shù)是接受輸入并返回值而不修改其范圍之外的任何數(shù)據(jù)的函數(shù)副作用。如果它們不同,則調用函數(shù),以更新新狀態(tài)。
作者:Chidume Nnamdi
英文原文:https://blog.bitsrc.io/unders...
[譯] 理解 JavaScript Mutation 突變和 PureFunction 純函數(shù)不可變性、純函數(shù)、副作用,狀態(tài)可變這些單詞我們幾乎每天都會見到,但我們幾乎不知道他們是如何工作的,以及他們是什么,他們?yōu)檐浖_發(fā)帶來了什么好處。
在這篇文章中,我們將深入研究所有這些,以便真正了解它們是什么以及如何利用它們來提高我們的Web應用程序的性能。
Javascript:原始數(shù)據(jù)類型和引用數(shù)據(jù)類型我們將首先了解JS如何維護以及訪問到我們的數(shù)據(jù)類型。
在JS中,有原始數(shù)據(jù)類型和引用數(shù)據(jù)類型。原始數(shù)據(jù)類型由值引用,而非原始/引用數(shù)據(jù)類型指向內存地址。
原始數(shù)據(jù)類型是:
Boolean
Number
String
Null
Undefined
Symbol
引用數(shù)據(jù)類型:
Object
Arrays
當我們寫原始數(shù)據(jù)類型時是這個樣子:
let one = 1
在調用堆棧中,one 變量直接指向值 1:
Call Stack #000 one -> | 1 | #001 | | #002 | | #003 | |
如果我們改變這個值:
let one = 1 one = 3
變量 one 的內存地址 #000 原本存儲 1 這個值,會直接變成 3
但是,如果我們像這樣寫一個引用數(shù)據(jù)類型:
let arr = { one: 1 }
或:
let arr = new Object() arr.one = 1
JS將在內存的堆中創(chuàng)建對象,并將對象的內存地址存儲在堆上:
Call Stack Heap #000 arr -> | #101 | #101 | one: 1 | #001 | | #102 | | #002 | | #103 | | #003 | | #104 | |
看到 arr 不直接存儲對象,而是指向對象的內存位置(#101)。與直接保存其值的原始數(shù)據(jù)類型不同。
let arr = { one: 1 } // arr holds the memory location of the object {one: 1} // `arr` == #101 let one = 1; // `one` a primitive data type holds the value `1` // one == 1
如果我們改變 arr 中的屬性,如下所示:
arr.one = 2
那么基本上我們就是在告訴程序更改 arr 對對象屬性值的指向。如果你對 C/C++ 等語言的指針和引用比較熟悉,那么這些你都會很容易理解。
傳遞引用數(shù)據(jù)類型時,你只是在傳遞其內存位置的遞值,而不是實際的值。
function chg(arg) { //arg points to the memory address of { one: 1 } arg.one = 99 // This modification will affect { one: 1 } because arg points to its memory address, 101 } let arr = { one: 1 } // address of `arr` is `#000` // `arr` contains `#101`, adrress of object, `{one: 1}` in Heap log(arr); // { one: 1 } chg(arr /* #101 */) // #101 is passed in log(arr) // { one: 99 } // The change affected `arr`
譯者注:arr 本身的內存地址是 #000;arr 其中保存了一個地址 #101;這個地址指向對象 {one:1};在調用 chg 函數(shù)的時候,那么修改 arg 屬性 one 就會修改 arr 對應的 #101 地址指向的對象 {one:1}
因為引用數(shù)據(jù)類型保存的是內存地址,所以對他的任何修改都會影響到他指向的內存。
如果我們傳入一個原始數(shù)據(jù)類型:
function chg(arg) { arg++ } let one = 1; // primitive data types holds the actual value of the variable. log(one) // 1 chg(one /* 1 */) // the value of `one` is passed in. log(one) // one is still `1`. No change because primitives only hold the value
譯者注:不像原始數(shù)據(jù)類型,他的值是多少就是多少如果修改了這個值,那么直接修改所在內存對應的這個值狀態(tài)突變和不可變性
在生物學領域,我們知道 DNA 以及 DNA 突變。DNA 有四個基本元素,分別是 ATGC。這些生成了編碼信息,在人體內產生一種蛋白質。
ATATGCATGCGATA |||||||||||||| TACGAGCTAGGCTA | | v AProteinase Information to produce a protein (eg, insulin etc)
上述DNA鏈編碼信息以產生可用于骨結構比對的AP蛋白酶蛋白。
如果我們改變DNA鏈配對,即使是一對:
ATATGCATGCGATA |||||||||||||| TACGAGCTAGGCTA | v GTATGCATGCGATA |||||||||||||| TACGAGCTAGGCTA
DNA將產生不同的蛋白質,因為產生蛋白質AP蛋白酶的信息已經被篡改。因此產生了另一種蛋白質,其可能是良性的或在某些情況下是有毒的。
GTATGCATGCGATA |||||||||||||| TACGAGCTAGGCTA | | V Now produces _AProtienase
我們稱這種變化突變或DNA突變。
突變引起DNA狀態(tài)的改變。
而對于 JS 來說,引用數(shù)據(jù)類型(數(shù)組,對象)都被稱為數(shù)據(jù)結構。這些數(shù)據(jù)結構保存信息,以操縱我們的應用程序。
let state = { wardens: 900, animals: 800 }
上面名為 state 的對象保存了 Zoo 應用程序的信息。如果我們改變了 animals 屬性的值:
let state = { wardens: 900, animals: 800 } state.animals = 90
我們的 state 對象會保存或編碼一個新的信息:
state = { wardens: 900, animals: 90 }
這就叫突變 mutation
我們的 state 從:
state = { wardens: 900, animals: 800 }
變?yōu)椋?/p>
state = { wardens: 900, animals: 90 }
當我們想要保護我們的 state 時候,這就需要用到不可變性了 immutability。為了防止我們的 state 對象發(fā)生變化,我們必須創(chuàng)建一個 state 對象的新實例。
function bad(state) { state.prp = "yes" return state } function good(state) { let newState = { ...state } newState.prp = "yes" return newState }
不可變性使我們的應用程序狀態(tài)可預測,提高我們的應用程序的性能速率,并輕松跟蹤狀態(tài)的變化。
純函數(shù)和副作用純函數(shù)是接受輸入并返回值而不修改其范圍之外的任何數(shù)據(jù)的函數(shù)(副作用)。它的輸出或返回值必須取決于輸入/參數(shù),純函數(shù)必須返回一個值。
譯者注:純函數(shù)必須要滿足的條件:不產生副作用、返回值只取決于傳入的參數(shù),純函數(shù)必須返回一個值
function impure(arg) { finalR.s = 90 return arg * finalR.s }
上面的函數(shù)不是純函數(shù),因為它修改了其范圍之外的狀態(tài) finalR.s。
function impure(arg) { let f = finalR.s * arg }
上面的函數(shù)也不是純函數(shù),因為雖然它沒有修改任何外部狀態(tài),但它沒有返回值。
function impure(arg) { return finalR.s * 3 }
上面的函數(shù)是不純的,雖然它不影響任何外部狀態(tài),但它的輸出返回 finalR.s * 3 不依賴于輸入 arg。純函數(shù)不僅必須返回一個值,還必須依賴于輸入。
function pure(arg) { return arg * 4 }
上面的函數(shù)才是純函數(shù)。它不會對任何外部狀態(tài)產生副作用,它會根據(jù)輸入返回輸出。
能夠帶來的好處就個人而言,我發(fā)現(xiàn)的唯一能夠讓人理解的好處是 mutation tracking 變異追蹤。
知道何時渲染你的狀態(tài)是非常重要的事情。很多 JS 框架設計了不錯的方法來檢測何時去渲染其狀態(tài)。但是最重要的是,要知道在首次渲染完畢后,何時觸發(fā)再渲染 re-render。這就被稱為變異追蹤了。這需要知道什么時候狀態(tài)被改變了或者說變異了。以便去觸發(fā)再渲染 re-render。
于我們已經實現(xiàn)了不變性,我們確信我們的應用程序狀態(tài)不會在應用程序中的任何位置發(fā)生變異,況且純函數(shù)完全準尋其處理邏輯和原則(譯者注:不會產生副作用)。這就很容易看出來到底是哪里出現(xiàn)變化了(譯者注:反正不是純函數(shù)也不是 immutable 變量)。
let state = { add: 0, } funtion render() { //... } function effects(state,action) { if(action == "addTen") { return {...state, add: state.add + 10} } return state; } function shouldUpdate(s) { if(s === state){ return false } return true } state = effects(state, "addTen") if(shouldUpdate(state)) { render(); }
這里有個小程序。這里有個 state 對象,對象只有一個屬性 add。render 函數(shù)正常渲染程序的屬性。他并不會在程序的任何改變時每次都觸發(fā)渲染 state 對象,而是先檢查 state 對象是否改變。
就像這樣,我們有一個 effects 函數(shù)和一個純函數(shù),這兩個函數(shù)都用來去修改我們的 state 對象。你會看到它返回了一個新的 state 對象,當要更改狀態(tài)時返回新狀態(tài),并在不需要修改時返回相同的狀態(tài)。
因此,我們有一個shouldUpdate函數(shù),它使用===運算符檢查舊狀態(tài)和新狀態(tài)是否相同。如果它們不同,則調用render函數(shù),以更新新狀態(tài)。
結論我們研究了 Web 開發(fā)中這幾個最常見的術語,并展示了它們的含義以及它們的用途。如果你付諸實踐,這將是非常有益的。
如果有任何對于這篇文章的問題,如我應該增加、修改或刪除,請隨時評論、發(fā)送電子郵件或直接 DM 我。干杯
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/102576.html
摘要:于是就有了即學即用這個系列的文章。系列第一篇,就從純函數(shù)開始,由于我是前端方向,所以就從語言中的純函數(shù)說起。并行代碼純函數(shù)是健壯的,改變執(zhí)行次序不會對系統(tǒng)造成影響,因此純函數(shù)的操作可以并行執(zhí)行。 最近一直在思考如何通過文章或者培訓快速提升團隊的編碼能力,總結下來其實技術的學習分為兩類:一種是系統(tǒng)性的學習,比如學習一門語言,學習一個開發(fā)框架,這更需要自己從入門到進階再到實踐一步步系統(tǒng)性的...
摘要:概觀是現(xiàn)代瀏覽器提供的,用于檢測中的變化。您可能正在使用所見即所得的編輯器,試圖實現(xiàn)撤銷重做功能。函數(shù)的第一個參數(shù)是在一個批次中發(fā)生的所有改變的集合。雖然有用,但中的每一次更改都會觸發(fā)突變事件,這又會導致性能問題。 showImg(https://segmentfault.com/img/bV9Z7q?w=1016&h=252);Web應用程序在客戶端越來越重要,原因很多,比如需要更豐...
摘要:來自不同視圖的行為需要變更同一狀態(tài)。圖解后端的行為,響應在上的用戶輸入導致的狀態(tài)變化。中的非常類似于事件每個都有一個字符串的事件類型和一個回調函數(shù)。 什么是Vuex? Vuex 是一個專為 Vue.js 應用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲管理應用的所有組件的狀態(tài),并以相應的規(guī)則保證狀態(tài)以一種可預測的方式發(fā)生變化。 Vuex采用和Redux類似的單向數(shù)據(jù)流的方式來管理數(shù)據(jù)。用戶...
摘要:事件循環(huán)持續(xù)運行,直到清空列隊的任務。在執(zhí)行期間,瀏覽器可能更新渲染。線索可能會發(fā)生多次。由于冒泡,函數(shù)再一次執(zhí)行。這意味著隊列不會在事件回調之間處理,而是在它們之后處理。當觸發(fā)成功事件時,相關的對象在事件之后轉為非激活狀態(tài)第四步。 一 前言 一直想對異步處理做一個研究,在查閱資料時發(fā)現(xiàn)了這篇文章,非常深入的解釋了事件循環(huán)中重的任務隊列。原文中有代碼執(zhí)行工具,強烈建議自己執(zhí)行一下查看結...
摘要:對于每個案例,我們插入所需要的測試數(shù)據(jù),調用需要測試的函數(shù)并對結果作出斷言。我們將這個套接字和用戶返回以供我們其他的測試使用。 原文地址:Elixir, Phoenix, Absinthe, GraphQL, React, and Apollo: an absurdly deep dive - Part 2 原文作者:Zach Schneider 譯文出自:掘金翻譯計劃 本文永久鏈接:gi...
閱讀 2326·2021-09-22 15:27
閱讀 3178·2021-09-03 10:32
閱讀 3506·2021-09-01 11:38
閱讀 2503·2019-08-30 15:56
閱讀 2220·2019-08-30 13:01
閱讀 1543·2019-08-29 12:13
閱讀 1425·2019-08-26 13:33
閱讀 899·2019-08-26 13:30