摘要:對于一些小數組來說,這樣做當然沒有問題。第一個主要問題在于,我們將要追加的數組的元素數量翻倍了當然是臨時性的,因為實質上要將數組內容拷貝到函數調用棧上。所以,假如要追加的數組中有一百萬個元素,那么幾乎一定會超過函數和的調用棧限制的大小。
原文鏈接: https://davidwalsh.name/combi...
這是一篇介紹 JavaScript 技術的小短文。我們將會講到組合/合并兩個數組的不同策略,以及每一種方法的優缺點。
首先展示一下應用場景:
var a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; var b = [ "foo", "bar", "baz", "bam", "bun", "fun" ];
很顯然,拼接后的結果是這個樣子滴:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, "foo", "bar", "baz", "bam" "bun", "fun" ]concat(..)
最常見的做法如下:
var c = a.concat( b ); a; // [1,2,3,4,5,6,7,8,9] b; // ["foo","bar","baz","bam","bun","fun"] c; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
從上述代碼可以看出,c 是一個由 a 和 b 兩個數組合并而成的全新的數組,而 a 和 b 則不受影響。相當簡單,對吧?
假如 a 和 b 分別包含 10,000 元素呢?那么 c 中就會包含 20,000 個元素,占用的內存基本上讓 a 和 b 占用的內存翻倍。
“這沒什么大不了的!”你微微一笑。我們可以把 a 和 b 刪除嘛,這樣就可以將占據的內存回收了,這樣總可以吧?危機解除!
a = b = null; // `a` and `b` can go away now
哦。對于一些小數組來說,這樣做當然沒有問題。但是對于大數組來說,或者經常性地執行這樣的操作,再或者在執行環境內存有限的情況下,這樣做還遠遠不夠。
循環插入好吧,那使用 Array#push(..) 方法將一個數組的內容追加到另外一個數組呢:
// `b` onto `a` for (var i=0; i < b.length; i++) { a.push( b[i] ); } a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"] b = null;
現在,a 中包含的是原本 a 中的元素外加 b 中的元素。
看起來對于內存的使用有效多了。
可是假如 a 比較小而 b 相對來說很大呢?出于內存利用以及執行速度的考量,你一定希望把小數組 a 插入到 b 的前面而不是把大數組 b 追加到 a 后面。沒問題,只要用 unshift(..) 替換 push(..) 然后反方向遍歷就可以了:
// `a` into `b`: for (var i=a.length-1; i >= 0; i--) { b.unshift( a[i] ); } b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"] a = null;使用函數小技巧
遺憾的是,for 循環不夠優雅,也不容易維護。還有沒有更好的辦法呢?
下面是我們的第一次嘗試,用的是 Array#reduce:
// `b` onto `a`: a = b.reduce( function(coll,item){ coll.push( item ); return coll; }, a ); a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"] // or `a` into `b`: b = a.reduceRight( function(coll,item){ coll.unshift( item ); return coll; }, b ); b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
Array#reduce(..) 與 Array#reduceRight(..) 看起來不錯,只是有點笨拙。ES6 中的 => 箭頭表達式可以對其進行適當“瘦身”,但是依然需要對于每一個元素進行一次函數調用,這一點有些令人遺憾。
下面的方法怎么樣呢:
// `b` onto `a`: a.push.apply( a, b ); a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"] // or `a` into `b`: b.unshift.apply( b, a ); b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
看起來好多了,是吧?!尤其是這里的 unshift(..) 不再需要顧及遍歷順序的問題。ES6 中的展開運算符(spread operator)會更棒:a.push( ...b ) 或是 b.unshift( ...a )。
但是呢,公主與王子并沒有從此過上幸福無虞的生活。在兩種情況下,將 a 或 b 傳給 apply(..) 第二個參數(或者通過 ... )展開運算符意味著數組需要展開為函數的參數。
第一個主要問題在于,我們將要追加的數組的元素數量翻倍了(當然是臨時性的),因為實質上要將數組內容拷貝到函數調用棧上。另外,不同的 JS 引擎因實現方式的不同,對于可以傳入函數的參數的數量限制也不盡相同。
所以,假如要追加的數組中有一百萬個元素,那么幾乎一定會超過函數 push(..) 和 unshift(..) 的調用棧限制的大小。嗯!幾千元素應該是沒有問題的,不過需要小心設定一個合理的安全上限。
注意: 你可以嘗試使用 splice(..),但是結論與 push(..) / unshift(..) 相同。
一個可行的方式是,依然采用上述方法,將數組劃分為處于安全范圍的片段,進行批處理:
function combineInto(a,b) { var len = a.length; for (var i=0; i < len; i=i+5000) { b.unshift.apply( b, a.slice( i, i+5000 ) ); } }
且慢,接下來我們要回到可讀性(或者還有執行效率)的老話題了。我們還是在拋棄當前所獲得的所有有效方式之前就此打住吧。
總結Array#concat(..) 是合并兩個(甚至多個)數組的行之有效的方法。但是隱含的風險是,它直接創建了一個新的數組,而不是在原來數組的基礎上進行修改。
在原來數組的基礎上進行修改有多種可行的方式,但均有某種程度的妥協。
從不同方法(包括未在這里展示的方法)的優缺點來看,或許最好的方法就是 reduce(..) 和 reduceRight(..)。
無論選擇采用哪種方法,都需要對數組合并策略進行批判性思考,而不是想當然。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88956.html
摘要:如果第一個參數返回的值為,那么第二個值將會認為是一個默認值。但這個對象不具有數組的函數功能,比如等。 轉載自:http://www.w3cplus.com/javasc...英文出處:https://blog.jscrambler.com/1... 12個JavaScript技巧 在這篇文章中將給大家分享12個有關于JavaScript的小技巧。這些小技巧可能在你的實際工作中或許能幫助...
摘要:如果第一個參數返回的值為,那么第二個值將會認為是一個默認值。獲取數組中最后一個元素用來獲取和之間的數組元素。但這個對象不具有數組的函數功能,比如等。 使用!!操作符轉換布爾值 有時候我們需要對一個變量查檢其是否存在或者檢查值是否有一個有效值,如果存在就返回true值。為了做這樣的驗證,我們可以使用!!操作符來實現是非常的方便與簡單。對于變量可以使用!!variable做檢測,只要變量的...
摘要:接下來先介紹七個馬上就能用起來的小技巧。老實講,文章所說的小技巧大部分都是新增的語法特性,,或者說已經發布好些年頭,這些特性大家可能已經非常熟識。,對象合并,不多說,大部分場景可以取代。 作者:@davidwalshblog原文:7 Useful JavaScript Tricks 和許多其他語言一樣,JavaScript 也需要靠很多小技巧去完成各種不同的事情。有的可能早已經廣為人...
摘要:每種編程語言都有一些黑魔法或者說小技巧,也不例外,大部分是借助或者瀏覽器新特性實現。下面介紹的個實用小技巧,相信其中有些你一定用過。當然不管語言如何變化,我們總能在編程中總結一些小技巧來精簡代碼。 showImg(https://segmentfault.com/img/remote/1460000018902642); 每種編程語言都有一些黑魔法或者說小技巧,JS也不例外,大部分是借...
摘要:以下我經常用,又總是記不住的幾個方法轉成數組形式獲取中的內容關鍵在頁面標簽加載完成事件關鍵在阻止瀏覽器事件默認行為關鍵,記憶方法阻止事件冒泡關鍵,記憶方法數組的幾個常用方法是數組的每一項如的下標,每一個元素的順序等同于一個單純的循環可以對進 以下我經常用,又總是記不住的幾個方法 document.querySelectorAll 轉成數組形式; [].slice.call(docume...
閱讀 2210·2021-11-19 09:40
閱讀 1925·2021-11-08 13:24
閱讀 2460·2021-10-18 13:24
閱讀 2863·2021-10-11 10:57
閱讀 3588·2021-09-22 15:42
閱讀 1122·2019-08-29 17:11
閱讀 2532·2019-08-29 16:11
閱讀 2427·2019-08-29 11:11