immutability-helper因React官方出鏡,而被寵幸!
作者: ofollow,noindex">首席填坑官∙蘇南
公Z好: honeyBadger8
,群:912594095,本文原創,著作權歸作者所有,轉載請註明原連結及出處。
引言
之前專案中遇到資料拷貝、引用之間資料層級巢狀過深,拷貝的值相互之間影響的問題,後來引入了 immutability-helper
,使用過程中的一些總結,跟大家分享下,至於為什麼不是 immutable
,請看下文分解,這裡是@IT·平頭哥聯盟,我是 首席填坑官
—— 蘇南 。
相信大家在面試/工作中都遇到過js物件/陣列的拷貝問題,面試官問你,你一般怎麼做??在現在ES6盛行的當下,不會一點ES6都不好意思說自己是前端(其實我一般都說自己是 攻城獅、切圖崽 :stuck_out_tongue_closed_eyes:),我們想的大多第一想法,如下:
-
Object.assign
- 最方便; -
[...]
- 最有逼格; -
JSON.parse
、JSON.stringify
- 完美組合; -
$.extend()
- jQuery時代的引領潮流時尚前沿的API; - 最後想到的才是自己遞迴實現一個;
但是通常我們使用的 Object.assign
屬於淺拷貝,當資料巢狀層級較深時,就……呵呵了;而 JSON.parse、stringify
它應該是建立一個臨時可能很大的字串,然後又訪問解析器,效能是比較慢的。於是後來發現了 immutable
「不可變資料」,曾經我也一度特別喜歡它,但時間久了,慢慢發現,它過於有個性了些、凡事都都沒有任何商量的餘地,所有的資料,從建立、變更、插入、刪除等操作,都要按它的套路來,對於我這種一生放蕩不羈愛自由的人來說,長時間的約束,是不能忍的;都說兩人如果三觀不合,是無法長久下去的,可能也是緣份吧,在後來的某一天偶然的閒逛中邂逅了新歡 ————Immutability Helpers。
嗯,今天的主題就是給大家分享一下, Immutability Helpers
的一些用法,會介紹API的使用操作、小技巧和注意的點,如有理解不對,請糾正:
太興奮了,差點忘了,補充一下,一個簡單的拷貝:
//實現一個簡單的遞迴資料拷貝 let customClone = (rawObj)=>{ let copyObj = {}; for (var key in rawObj) { if( typeof rawObj[key] === 'object' && Object.prototype.toString.call(rawObj[key]) !== '[object Array]'){ copyObj[key] = customClone(rawObj[key]); }else{ copyObj[key] = rawObj[key]; }; }; return copyObj; }; let objA ={"name":"蘇南","sex":"男","height":"176"}; let objB =customClone(objA); objB.signature = "寶劍鋒從磨礪出,梅花香自苦寒來,做有溫度的攻城獅"; console.log(objA); console.log(objB); 複製程式碼

- 補充一個
Object.assign
的坑 :
let data = { a:1, b:2, children:{ name:"蘇南", organization:"@IT·平頭哥聯盟", job:"首席填坑官", address:"ShenZhen", age:18 } }; let data2 = Object.assign({},data); data2.children.age = 28; data2.children.job = "首席甩鍋官"; data2.b = 666; console.log("我是原始資料 data:",data); console.log("我是複製後的資料 data2:",data2); 複製程式碼

immutable 最後的一次回顧
都說有了新歡,忘了舊愛,但我不是那種無情無義的人,最後正式介紹一下 immutable
,為我倆的……畫上一個圓滿的句號:
再次強調,並不是覺得 immutable
不好,不夠強大,只是自己個人觀點,有些不喜歡而已,各位 immutable
粉勿噴,想了解更多的同學可以點選這裡
Immutable data encourages pure functions (data-in, data-out) and lends itself to much simpler application development and enabling techniques from functional programming such as lazy evaluation.
使用示例:
const list1 = List([ 1, 2, 3 ]); const list2 = List([ 4, 5, 6 ]); const array = [ 7, 8, 9 ]; const list3 = list1.concat(list2, array); console.log(list3) // List {size: 9, _origin: 0, _capacity: 9, _level: 5, _root: null, …} 是不能直接獲取到資料的,須使用get,-- list3.get(0) 複製程式碼
let data = fromJS({ obj:{} }); let data1 = { a:1, b:2, children:{ name:"蘇南", } }; let data2 = data.mergeIn(['obj'],data1,{c:666}); console.log("獲取的資料:",data2.getIn(['obj','c'])); console.log("這裡是由formJS建立的資料:",data2.getIn(['obj','children','name']));// 複製程式碼

使用immutable後,所有資料都要類似選擇器,一個一個往下選擇,並不是說它不好、功能不夠強大,只是自己有些不喜歡它類似JQuery選擇器一樣的語法,get、getIn、set、List等的使用方式,當然它也是可以使用 toJS
方法轉回來的。
Immutability Helpers出場
gitHub上它對自己的介紹很簡單: Mutate a copy of data without changing the original source
—— 在不更改原始源的情況下改變資料副本。
與它結緣,是因為它在react官方文件中出鏡,而被我所寵幸,真的 , 只是因為在人群中多看了它一眼再也沒能忘掉
, 它跟 immutable
不一樣,不會有那麼多條條框框約束你,給你自由、給你獨立的空間、給你獨立的思想,讓你想用即用、用之即走~~(泥馬,怎麼有點像張小龍說它的小程式一樣:grimacing:),但您放心,它的坑真的比小程式少,API也很簡潔,接下來來看一下,它的基本用法:
$push $unshift $splice $set $toggle $unset $merge $apply $add $remove
以上基本就是它全部的API了,下面一起來看看,具體用法吧:
$push 的使用 :
- 看名字就知道它的作用了啦,跟原生的
push
一樣,不過寫法有一點點不一樣;
let arr = [1,2,3,4,5,66]; let arr2 = update(arr,{ $push : ["a","b","c"], //一定要 []號的形式哦,不可以 "a"; [4]:{ // !!index ,可以指定修改下標的值 $set:"我是替換過的" } }); console.log(arr2); 複製程式碼
$unshift 的使用 :
- 一樣,跟原生的
unshift
,在原陣列開頭處插入,同樣寫法是以一個數組的形式;
let arr = [1,2,3,4,5,66]; let arr2 = update(arr,{ $unshift : ["a","b","c"], [4]:{ $set:"我是首席填坑官∙蘇南"//這裡需要注意,它的操作是在 unshift之前執行的,也就是在原 arr 上查詢 第4個下標 } }); console.log("原始陣列",arr);// [1, 2, 3, 4, 5, 66] 相互之間並不會影響 console.log(arr2); //["a", "b", "c", 1, 2, 3, 4, "我是首席填坑官∙蘇南", 66] 複製程式碼
$splice 的使用 :
- 注意 :陣列套陣列,start,end, 插入的資料……,;
let arr = [1,2,3,4,5,66]; let arr2 = update(arr,{ $splice : [[1,2,[66788,99],{a:123,b:"蘇南"}]], // or [0,1,"從我開始是插入的內容",88,89,90,"後面可以很多,是陣列、物件、字串都行"] }); console.log(arr2); //複雜一些的用法: let obj={ name:"immutable", list :[1,2,[90,55,44,3,22,55],3,4,6,7,8] }; let obj2 = update(obj,{ list:{ [2]:value=>update(value,{ $splice:[[0,2]]// [90,55,44,3,22,55] => [44, 3, 22, 55] }) } }); 複製程式碼

$set 的使用 :
- 上面已經演示過了,其實有點替換的意思,當有重複的值時,就會覆蓋,沒有就新增,來展示覆雜一點的場景,層級深的資料,也不會相互影響;
let obj={ name:"immutable", children:{ address:"ShenZhen", hobby:"@IT·平頭哥聯盟-前端開發" } }; let obj2 = update(obj,{ $set : {name:"immutability-helper",other:"其他欄位,如微信公眾號:honeyBadger8,每週為你帶來最新分享"} }); let obj3 = update(obj,{ name:{ $set : "蘇南" }, children:{ hobby:{ $set:"首席填坑官 - javascript" } } }); console.log("原始資料:",obj); console.log("obj2:",obj2); console.log("obj3",obj3); 複製程式碼

$toggle 的使用:
- 聽名字,應該就能猜出來,開關切換的意思;
- Boolean 布林值的切換,如果你是強制要
Numbe
r 型別 的 0、1,那麼使用引方法的時候就要注意了;
let obj={ name:"immutable", a:false, b:true, c:1, d:0 }; let obj2 = update(obj,{ $toggle:['b','a',"c","d"], }); console.log("原始資料:",obj); console.log("obj2:",obj2); 複製程式碼

$unset 的使用:
- 它跟 splice`;請看下圖:
let arr = [1,2,3,4,5,6]; let obj={ name:"immutable", children:{ address:"ShenZhen", hobby:"寫部落格" } }; let obj2 = update(obj,{ $unset : ["name"], children:{ $unset:["address"] } }); console.log("原始資料:",obj); console.log("obj2:",obj2); let arr2 = update(arr,{ $unset : [1] }); console.log("arr2:",arr2,arr2.length); 複製程式碼

$merge 的使用:
-
$merge
跟我們最愛的Object.assign
一樣,做合併操作的,但它比assign
優秀很多,深層次拷貝,不會相互影響 :
let arr = [1,2,3,4,5,6]; let obj={ name:"immutable", children:{ address:"ShenZhen", hobby:"寫部落格", array:["我不是程式設計師","切圖崽瞭解一下"], } }; let obj2 = update(obj,{ $merge:{ arr }, children:{ array:{ $merge:{items:["從前有坐山","山裡有個廟"]}, $splice:[[3,0,"住著一個小和尚"]] } } }); console.log("原始資料:",obj); console.log("obj2:",obj2); 複製程式碼

$apply 的使用:
-
$apply
基於當前值進行一個函式運算,從而得到新的值 : - 注意 :它必須是一個
function
哦!
let obj={ name:"immutable", children:{ items:["從前有一坐山"], array: [1,2,3,4,5,6], } }; let obj2 = update(obj,{ name:{ $apply:(val)=>("首席填坑官") }, children:{ items:{ $apply:(val)=>{ console.log("舊值",val); return [3,0,"住著一個小和尚"] } }, array:{ $apply:(val)=>(val.reverse()) //必須是一個函式 } } }); console.log("原始資料:",obj); console.log("obj2:",obj2); 複製程式碼

$remove 的使用:
-
$remove
一定一定 要是使用Set
、Map
建立的陣列: - 要刪除的值,必須是陣列成存在的,如值不存在則忽略,
$remove:[2,666]
,2會刪除,6則會被忽略; - 這個api有點奇怪,正常普通的陣列 [],這樣的刪除不了!!;
- 常見錯誤如下圖:
let obj={ name:"immutable", children:{ array:new Set([1, 2, 3, 4, 4]), } }; let obj2 = update(obj,{ children:{ array:{ $remove:[2], }, } }); console.log("原始資料:",obj); console.log("obj2:",obj2); 複製程式碼

$add 的使用:
-
$add
跟剛才的 add方法也跟es6
Map/Set
的 add方法一致: - 只是寫的時候也要注意一些, [ [] ] ,巢狀!
let obj={ name:"immutable", array:new Map([["a",1],["b",2]]), }; let obj2 = update(obj,{ array:{ $add:[["66",56]], }, }); console.log("原始資料:",obj); console.log("obj2:",obj2); console.log("獲取key a:",obj2.array.get('a')); 複製程式碼

Immutability Helpers的高階用法:
- 還可以自定義方法,如 定義一個
$trinocular
方法,來判斷陣列中的值; - 只是一個簡單的示例,更多複雜的用法,可以自己去探索哦 去官方 github :point_left:
update.extend('$trinocular', function(proportion, original) { returnoriginal > 88 ? (original/proportion ): (proportion+original); }); let array =[56,33,55,777,322,444,61,12,34,52,245]; let array2 = array.map((k,v)=>update(k,{ $trinocular:2 })) console.log("原始資料:",array); console.log("array2:",array2); 複製程式碼

總結:以上就是基礎 API 的用法 ,添加了一些官方示例,沒有講到的組合使用,以及使用過程中,可能出現的一些錯誤,需要留意的地方,更多定製高階用法,有興趣的同學可以自行了解一下。
以上就是今天為大家帶來的分享,它可能沒有 immutable
那麼多功能,但貴在簡潔,不會有太多的約束,如理解有誤之處,歡迎各位大佬糾正,畢竟我還只是個寶寶——新手上路中! 。
下方是我弄的一個 公眾號 ,歡迎關注,以後文章會第一時間,在 公眾號 上更新,原因是之前分享的有兩篇文章,竟然被其他公眾號抄襲了:sob:,前些天去更新發表的時候,微信提示我文章已經不是原創了檢測到相同的文章,寶寶心裡那個涼啊~,果斷申訴告了對方(是一個培訓學校公眾號,好氣哦),補了掘金髮布的連結和截圖日期,萬幸最後勝訴了 !:point_down::point_down:

作者:蘇南 - 首席填坑官
連結: honeybadger8.github.io/blog/
交流:912594095、公Z好:honeyBadger8
本文原創,著作權歸作者所有。商業轉載請聯絡 @IT·平頭哥聯盟
獲得授權,非商業轉載請註明原連結及出處。