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