您好,登錄后才能下訂單哦!
這篇文章主要介紹了JavaScript數據類型對函數式編程的影響是什么的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇JavaScript數據類型對函數式編程的影響是什么文章都會有所收獲,下面我們一起來看看吧。
在 JavaScript 中,數據類型有以下 8 種:
null
undefined
boolean
number
symbol -- 在 es6 中被加入
bigint -- es6+ 被加入
object
注意點:
在 JavaScript 中,變量是沒有類型的,值才有類型。變量可以在任何時候,持有任何值。
上面 8 中類型除了 object ,其他都是原始類型,原始類型存儲的都是值,其特點有兩點:
沒有方法可以直接調用
原始類型的數據是不可被改變的,改變一個變量的值,并不是把值改變了,而是讓變量擁有新的值。
注意點:
'1'.toString()或者false.toString()等可以用的原因是被強制轉換成了 String 類型也就是對象類型,所以可以調用 toString 函數。
對于null來說,很多人會認為它是個對象類型,其實是錯誤的。typeof null 會輸出 object,這只是 JS 存在的一個悠久 Bug,而且好像永遠不會也不會被修復,因為有太多已經存在的 web 的內容依存著這個 bug。注: 在 JS 的最初版本中使用的是 32 位系統,為了性能考慮使用低位存儲變量的類型信息,000開頭代表是對象,然而 null 表示為全零,所以將它錯誤的判斷為 object 。雖然現在的內部類型判斷代碼已經改變了,但是對于這個 Bug 卻是一直流傳下來。
而除了原始類型,剩下的 object 就是對象類型,和原始類型的不同點在于:原始類型存儲的是值,對象類型存儲的是地址。
經典示例:
var c = 1; var d = c; d = 2; console.log(c === d) // false var a = { name: "張三", age: 20 } var b = a; b.age = 21; console.log(a.age === b.age) // true
示例中把變量 a 的值給到了變量 b , b 修改了age 屬性,但是 a 的 age 屬性也跟著變了,是因為 var b = a 是 a 把對象的引用地址賦值給 b ,這時候 a 和 b 指向的是內存中的同一個數據。
而 c 給 d 的是值,并不是一個引用,相當于復制了一份數據。
因此可以知道原型類型的數據是不可變的,而對象類型的數據是可變的。
JavaScript 中的對象類型的數據是可變,而可變性,就代表了不確定性,純函數 中使用了不確性的數據就會導致不純,因為其違背了 純函數 的特征:不受外界影響,不影響外界。
下面來看一個例子:
A 同學寫了這么一段代碼,初始化生成了一個 “zhangsan” 用戶。
export const defaultUserInfo = { name: "名稱", age: 20, hobby: ["玩耍"] }; export function initUser(userTemplate, name, age) { const newUser = userTemplate; newUser.name = name; newUser.age = age; return newUser; } const zhangsan = userInit(userDefaultInfo, "zhangsan", 21);
然后 B 同學在開發其他頁面的時候,看到有初始化用戶信息的方法,然后直接復制過去,初始化了一個 “lisi” 用戶。
import { defaultUserInfo, initUser } from "xxx模塊"。 const lisi = userInit(userDefaultInfo, "lisi", 21);
檢測的時候看到自己初始化的用戶信息正確的就沒有去檢查之前 A 同學的是否是正確的,上線后發現所有的用戶都變成了 lisi 。因為 userDefaultInfo 是一個引用類型,userInit(userDefaultInfo, "xxx", xx) 操作的都是內存中的同一個對象。其原因就是因為 A 和 B 開發者犯了一個錯誤,把可變數據傳遞到了 userInit 函數內部進行處理,哪怕進行了淺層拷貝,也出現了問題。究其原因還是因為給函數傳遞進去了一個 可變數據。
我們校驗一個 純函數 有效性的關鍵依據,永遠是“針對已知的輸入,能否給出符合預期的輸出”,而上面例子中 initUser 函數沒有違背這個規則,但是在可變數據的影響下,讓它產生了 副作用,對外界已有的數據造成了影響。
從使用函數方的角度來看,既然造成這個問題的原因是因為傳遞進去的數據是 可變數據 ,那么我就復制一份數據傳遞給函數內部使用,隨便你怎么修改,都不會影響外界其他數據。
比如我們使用前面例子中的 initUser 函數時,先拷貝一份數據:
function copyFunc(object) { return JSON.parse(JSON.string(object)); } const zhangsan = userInit(copyFunc(userDefaultInfo), "zhangsan", 21); const lisi = userInit(copyFunc(userDefaultInfo), "lisi", 21); console.log(zhangsan.name === lisi.name); // false
進行拷貝后的數據傳遞給 userInit 函數,就不會出現問題了。這里的 copyFunc 只能針對部分數據類型,對不少類型是不支持的,具體可以去看一下 關于JSON.parse(JSON.stringify(obj))實現深拷貝應該注意的坑 這篇文章。
從被調用函數方來看,在使用 object 類型數據時,函數內部盡量不要去修改外界 object 數據(通過參數傳遞,或者直接使用外界的對象都不建議去修改),修改之前可以拷貝一份再修改。
比如:
export function initUser(userTemplate, name, age) { const newUser = copyFunc(userTemplate); newUser.name = name; newUser.age = age; return newUser; }
拷貝的數據比較大的時候,會出現性能問題,因此出現了不可變數據的方案。
現在不可變數據常見的有兩種: Immutable.js 和 immer.js 。它們都能實現在操作數據后,返回新的一個數據,而不影響之前的數據。
Immutable.js 實現了持久化數據結構,實現原理說明(引用于immutable.js 和 immer):
使用舊數據創建新數據時,要保證舊數據同時可用且不變。同時為了避免 deepCopy 把所有節點都復制一遍帶來的性能問題,immutable 使用了結構共享方式,即如果對象樹中的一個節點改變,只修改這個節點和受它影響的父節點,其他節點共享。
immutable-js 使用了另一套數據結構 api,它會將原生數據類型都轉化為 immutable-js 內部對象。
因此 Immutable.js 需要嚴格使用它自定義的操作數據的方法才行。
關于“JavaScript數據類型對函數式編程的影響是什么”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“JavaScript數據類型對函數式編程的影響是什么”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。