Array.slice 8種不同用法

2021-02-20 大遷世界

譯者:前端小智 

原文:https://codeburst.io/js-by-example-8-distinct-uses-of-javascript-array-slice-4e4e95a470e4

為了保證的可讀性,本文採用意譯而非直譯。

JS數組 slice方法是JS語言中最強大、最常用的內建函數之一。

隨著React和其他面向功能的JavaScript實踐的興起,它變得越來越重要,原因有兩個:

函數式編程,尤其是高階函數,與數據列表密切配合

函數式編程需要純函數,即不會產生副作用或修改輸入數據的函數

JavaScript 數組 slice方法符合這兩個標準。

slice方法可以在不修改原始列表的情況下創建列表子集的淺拷貝。因此,它為編寫函數式 JS 提供了一個關鍵的構建塊。

在這篇文章中,我們將通過實例來掌握 slice方法,探索它的8種不同用法。

注意: slice 方法不要與 splice方法混淆, splice方法會修改原始數組。

slice 工作原理

在深入研究一些更高級的用法之前,讓我們看一下 slice方法的基礎知識。

如MDN文檔, slice 是數組上的一個方法,它最多有兩個參數:

arr.slice([begin[, end]])

begin

從該索引處開始提取原數組中的元素,如果該參數為負數,則表示從原數組中的倒數第幾個元素開始提取, slice(-2)表示提取原數組中的倒數第二個元素到最後一個元素(包含最後一個元素)。如果省略 begin,則 slice 從索引 0 開始。

end

在該索引處結束提取原數組元素(從0開始)。 slice會提取原數組中索引從 begin 到 end 的所有元素(包含begin,但不包含end)。

slice(1,4) 提取原數組中的第二個元素開始直到第四個元素的所有元素 (索引為 1, 2, 3的元素)。

如果該參數為負數, 則它表示在原數組中的倒數第幾個元素結束抽取。 slice(-2,-1)表示抽取了原數組中的倒數第二個元素到最後一個元素(不包含最後一個元素,也就是只有倒數第二個元素)。

如果 end 被省略,則 slice 會一直提取到原數組末尾。如果 end 大於數組長度, slice 也會一直提取到原數組末尾。

基本用法

我們的前4個例子突出 slice的核心功能。

用法1:簡單的複製

const arr2 = arr.slice

沒有任何參數的 slice執行一個簡單的淺拷貝。當前,主流的用法還是使用展開運算符合來實現,但是如果在舊的代碼庫中,或者沒有使用 babel的構建步驟,可能仍然希望使用 slice。

用法2:獲取從 N 開始的子數組

使用 slice方法最簡單的方法就是原始數組從 N開始抽取的所有元素。

一種情況是希望彈出數組的第一個元素並使用它,返回剩餘的數組,但希望在不修改原始數組的情況下執行此操作。

function useone (arr) {

const usedItem = arr[0]

return arr.slice(1)

}

用法3:獲取從末尾 N 開始的子數組

slice的另一種使用方法是獲取數組的末尾,利用的是 負索引從末尾開始計數。

這種負索引使刪除任意數量的元素變得超級簡單。例如,如果你只想抓取3個

const last3 = arr.slice(-3)

用法4:獲取數組的前n個

獲取數組的前面的數,我們需要使用第二個參數: end。

當有兩個參數時, slice方法返回一個從 begin開始但不包括 end的集合。

由於JavaScript數組是從 0開始的(索引從0開始),這使得獲取前N個元素變得非常簡單:

const first4 = arr.slice(0, 4)

用法5:獲取數組中某段子數組

如果我們想要使用 slice從任何索引開始獲取數組的一段,該怎麼辦?

為此,我們需要從 (begin,length) 轉換為 (begin,end)。計算邏輯很簡單,我們可以定義一個簡單的函數來做到這一點:

function pullSegment(arr, begin, length) {

return arr.slice(begin, begin + length);

}

處理類似數組的對象

JavaScript中,數組是一個特殊的對象,其 property名為正整數,且其 length屬性會隨著數組成員的增減而發生變化,同時又從 Array構造函數中繼承了一些用於進行數組操作的方法。

而對於一個普通的對象來說,如果它的所有 property名均為正整數,同時也有相應的 length屬性,那麼雖然該對象並不是由 Array構造函數所創建的,它依然呈現出數組的行為,在這種情況下,這些對象被稱為 「類數組對象」 。

slice方法也可用於類似數組的對象。

一些類似數組包如 arguments(用於訪問傳遞給函數的所有參數的關鍵字), NodeLists(從返回節點列表的任何DOM API方法返回),甚至是使用數字索引並添加 length屬性的原始對象。

要在類似數組的對象上使用 slice方法,需要直接從 Array.prototype引用它,如下所示:

Array.prototype.slice.call(arguments)

在這特定的場合中會很有用處。

用法6:將類似數組的對象轉換為數組

slice在類似數組的對象上的一個常見用途是將它們轉換為實際數組。例如:

const args = Array.prototype.slice.call(arguments);

你為什麼要這麼做?為了使用數組方法。例如,想像一個像這樣的函數

function addOne() {

return arguments.map(i => i+1);

}

這看起來可行,但如果你試著去做,你就會得到錯誤:

> addOne(1, 2, 3)

TypeError: arguments.map is not a function

at test (repl:2:18)

at repl:1:1

at ContextifyScript.Script.runInThisContext (vm.js:44:33)

at REPLServer.defaultEval (repl.js:239:29)

at bound (domain.js:301:14)

at REPLServer.runBound [as eval] (domain.js:314:12)

at REPLServer.onLine (repl.js:440:10)

at emitOne (events.js:120:20)

at REPLServer.emit (events.js:210:7)

at REPLServer.Interface._onLine (readline.js:279:10)

這是因為 arguments 實際上不是數組,而是類似數組的對象。可以使用slice實現此功能,如下所示:

function addOne() {

return Array.prototype.slice.call(arguments).map(i => i+1)

}

現在就可以得到了你所希望的數據:

> addOne(1, 2, 3)

[ 2, 3, 4 ]

用法7:將任意長度多餘的參數強制轉換為數組

有時希望接受函數的多餘參數,組成一個數組。

較新版本的JavaScript引入了所謂的 Rest語法來處理這個問題,但是如果為為了兼容舊瀏覽器,你可以使用 slice做到這一點:

function myFunc(a, b) {

const extraArgs = Array.prototype.slice.call(arguments, 2);

}

這允許使用任意數量的參數調用 myFunc, 例如:

myFunc(1, 2, 3, 4, 5, 6, 7, 8)

在函數裡面會得到 a==1, b===2, extraArgs===[3,4,5,6,7,8]

用法8:修改數組中的特定索引

slice在函數上下文中一個強大而常見的用法是替換數組中特定項的值。

從本質上講,這很簡單,只需要分配新值,但是在函數世界中,不能修改原始數組。

相反,可以將 slice與擴展運算符一起使用,以返回一個相同但對於要更新的索引的新數組:

function replaceIdx(arr, index, newVal) {

return [

...arr.slice(0, index),

newVal,

...arr.slice(index + 1)

]

}

偏函數應用

偏函數應用,英文是 partialapplication,也可以譯作「局部應用」、「部分應用」、「偏應用」

函數式編程中的另一種常見模式是所謂的偏函數應用:將函數預先應用於函數,然後返回一個新函數。

這種模式允許你組合函數,通過使用具有不同預應用參數的相同核心函數來創建更大的可重用性。

雖然像Haskell這樣的純函數語言本身支持偏函數應用程式,但是在JavaScript中,我們可以使用 slice實現一個函數來實現它

var partial = function() {

const fn = arguments[0];

const args = Array.prototype.slice.call(arguments, 1);

// Return a function that calls fn

return function() {

var remainingArgs = Array.prototype.slice.call(arguments);

return fn.apply(this, args.concat(remainingArgs));

}

}

交流

我是小智,公眾號「大遷世界」作者,對前端技術保持學習愛好者。我會經常分享自己所學所看的乾貨,在進階的路上,共勉!

關注公眾號,後臺回復福利,即可看到福利,你懂的。

延伸閱讀

JS引擎:從調用堆棧到Promise,需要知道的所有內容

如何使用 Set 來提高代碼的性能

2019年,Fluter 和 React Native 誰主沉浮

相關焦點

  • 8 種 Array.slice 不同用法,你需要知道
    原文地址 | https://codeburst.io/js-by-example-8-distinct-uses-of-javascript-array-slice
  • 【譯】Rust中的array、vector和slice
    (&arr);    return 0;}print_array_size函數列印出了 8 而不是期望的 20(5 個整數,每個整數 4 字節),因為arr已經從一個指向包含 5 個整數的數組(array)的指針衰退為指向一個整數的指針。
  • 【數組分享】PHP函數array_slice ()分享(2020-11-25)
    array_slice () 從數組中取出一段。 array array_slice ( array $array , int $offset [, int $length = NULL [, bool $preserve_keys = false ]] )array_slice() 返回根據 offset 和 length 參數所指定的 array 數組中的一段序列。
  • 區分JavaScript數組的slice()與splice()
    簡述JavaScript數組中存在兩個看起來十分相似的方法,分別是slice()與splice()。其中,JavaScript的 slice() 方法可從已有的數組中返回選定的元素,該方法不會改變原始數組。
  • 【重學JS系列】slice用法大合集
    >slice 工作原理在深入研究一些更高級的用法之前,讓我們看一下slice方法的基礎知識。如果 end 大於數組長度,slice 也會一直提取到原數組末尾。基本用法我們的前4個例子突出slice的核心功能。用法1:簡單的複製沒有任何參數的slice執行一個簡單的淺拷貝。
  • 從 JS 數組操作到 V8 array.js
    :」function isBiggerThan10(element, index, array) {  return element > 10;}[2, 5, 8, 1, 4].some(isBiggerThan10);  // false[12, 5, 8, 1, 4].some(
  • JavaScript中原生Array數組方法詳解
    這篇文章中,我將盤點下定義在Array原型上的一些常用的方法,並逐一介紹其用法。1.forEach()forEach對數組的所有成員依次執行且只執行一次參數函數,參數函數包含以下3個參數:進一步,可以傳遞一個可選參數用作指定函數裡的這個。
  • C++11學習 - Array的用法與vector用法
    作者丨淡淡_小孩https://blog.51cto.com/13475106/2554602C++11學習 - Array的用法與
  • python基礎-bytes和bytearray的用法
    Python中的序列類型有bytes和bytearray。二進位序列類型的用法比較少見,是python中少用的一種序列類型,對於二進位序列類型,大家基本了解即可。bytes二進位序列類型指定長度的零填充字節對象: bytes(3)二進位字符串對象:bytes(b'abc')bytearray二進位數組指定長度的零填充字節對象: bytearray(3)二進位字符串對象:bytearray(b'abc')實例:print(type("ffff
  • 一文學會JS的數組函數:slice()
    本文結合實例講解slice(),後期將講解他的另兩個兄弟splice () 和split ()。這三個數組的原生函數,或許是因為它們的名字比較相似,會讓初學者有點摸不著頭腦。同時關於這三個函數之間的區別也是面試時的熱門話題!
  • Golang 筆記(三):一種理解 Slice 的模型
    概述Golang 中 slice 極似其他語言中數組,但又有諸多不同,因此容易使初學者產生一些誤解,並在使用時不易察覺地掉進各種坑中。在 Go 語言中,切片(slice)和數組(array)是伴生的,切片基於數組,但更為靈活,因此在 Go 中,作為切片底層的數組反而很少用到。但,要理解切片,須從數組說起。
  • JavaScript Array對象
    array1.concat(array2,array3,…,arrayX)every()檢測數組元素的每個元素是否都符合條件。array.every(function(currentValue,index,arr), thisValue)filter()檢測數組元素,並返回符合條件所有元素的數組。
  • JavaScript 特殊對象 Array-Like Objects 詳解
    我們簡單了解下,主要的原因是 slice 方法只需要參數有 length 屬性即可。首先,slice 方法得到的結果是一個 新的數組,通過 Array.prototype.slice.call 傳入的參數(假設為 a),如果沒有 length 屬性,或者 length 屬性值不是 Number 類型,或者為負,那麼直接返回一個空數組,否則返回 a[0]-a[length-1] 組成的數組。
  • C++語言中std::array的神奇用法總結
    也正因此,使得std::array有很多與其他容器不同的特殊之處,比如:std::array的元素是直接存放在實例內部,而不是在堆上分配空間;std::array的大小必須在編譯期確定;std::array的構造函數、析構函數和賦值操作符都是編譯器隱式聲明的……這讓很s多用慣了std::vector這類容器的程式設計師不習慣,覺得std::array不好用。
  • js中的Array對象屬性和方法整理
    這一點可能聽起來會有點亂,別急,下面我便通過實例對這一特殊的方法作已下講解:  首先,我們要先了解一下類的概念,JavaScript 本身是一種面向對象的語言,它所涉及的元素根據其屬性的不同都依附於某一個特定的類。
  • CVE-2016-4622 Webkit slice學習筆記
    出來的,valueOf的返回值是10,相當於是b=a.slice(0,10)這樣,但是這個操作是在valueOf裡面a.length =0執行之後做的,這樣slice之後就是一個數組越界了。我們給原始碼的patch去掉了arrayProtoFuncSlice函數的length == toLength(exec, thisObj)檢查,它對應的數組類型的slice函數,繼承如下,對應我們的poc。
  • 「語言學習」Go語言之slice特性
    Go語言學習slice介紹和說明golang的數據結構也很多,如List,array,map等,但是有個很特別的數據結構是slice,也叫切片那麼什麼是slice呢?其實slice也算是golang語言特有的數據結構,底層是以數組作為支撐;啥概念呢,就是說在申請一塊內存進行數組的存放的時候,slice就像數組對外開放的一扇窗口,讓你看到想給你看到的內容。
  • Golang slice 使用技巧
    經常遇到一個需求就是拼接 []int 中個各個元素,很多種實現都有人用,都是需要遍歷轉換 int 到 string,但是拼接方法千奇百怪,以下提供兩種方法對比(源碼在GitHub)。github.com/thinkeridea/example/slice 8.364s複製代碼較 + 版本提升接近4倍的性能,這是使用 slice 作為緩衝區極好的技巧,使用非常方便,並不用使用 builder 和 buffer, slice 操作非常的簡單實用。
  • JavaScript 數組中 Slice 和 Splice 的區別(含視頻)
    ()Array.prototype.slice() 是用於提取一個數組,從 start  元素到  end  元素(不包含  end  元素)。不過和切蛋糕不同,從數組中提取元素不會影響原數組,原數組根本就不會變化(像是切不完的蛋糕!)