前言
本文包括数组操作方法、ES6新增数组功能、JS矩阵、类型化数组等;
前面是基础部分,如果你不是小白的话…个人认为可以从第三章开始?
上一篇:《学习JavaScript数据结构与算法》第二章 ES和TS概述
一、创建 && 初始化数组
使用JavaScript声明, 创建和初始化数组;
使用new关键字,来简单的声明并初始化一个数组。
该种方式的优点是可以直接在声明时就确定好数组的长度:
1 | daysOfweek = new Array(数组长度,数字型); |
或者直接使用中括号形式:
1 | let daysOfweek = []; |
get数组长度:
1 | console.log(数组名.length); |
二、操作数组
添加元素于末尾
直接将要添加的数组元素赋值给数组的最后一位,但这种方法一次性只能添加一个元素:
1 | let array = ["one", "two", "three"]; |
我们在实战中更多使用的方法是push(),这是一个专用于处理数组的方法:
1 | let array = ["one", "two", "three"]; |
可以一次性将多个元素依次加入数组的末尾.
添加元素于开头
将新的数组元素直接添加至数组开头, 实操一般会使用unshift方法:
1 | let numbers = [3, 4, 55]; |
从数组末尾开始删除元素
使用pop()方法来从末尾删除数组中的元素:
书上并没有详细阐述这一方法, 只给了一个实例, 只好跑一下试试,
1 | let numbers = [1, 2, 3, 4, 5]; |
pop()方法固定只移除最后一个元素, 给参数也没用…
从数组开头开始删除元素
1 | for(let i = 0; i < numbers.length; i++) { |
这相当于在数组序列号不变的情况下将所有数组元素整体左移了一个单位,
但在这种情况下输出数组会发现数组的长度依旧未被改变,这说明数组中必然有一个元素的值为undefined:
可以看到, 我们只是把数组第一位的值用第二位进行了覆盖, 第一位并未被删除.
但在实战中我们一般使用shift方法:
1 | let num = [1, 2, 3]; |
这种方法会直接导致数组长度减小.
在数组任意位置添加或删除元素
删除和添加操作均可使用splice()来完成;
使用splice()方法, 通过指定起始位置和删除个数来完成删除操作;
1 | //注意是从左往右删; |
使用splice()方法, 通过指定起始位置和传入要添加的元素来完成添加:
1 | //执行添加操作, 第二个参数请传0; |
你看可以看到第二个参数依旧是有效的?
我只打算添加元素, 你要删除也可以, 我慷慨的允许你删0个 .
三、二维 多维数组
矩阵, 指包含二维数组在内的数组含有数组的结构;
JavaScript只支持一维数组并不支持矩阵,但是可以用如下数组嵌套的方法来实现矩阵.
构建二维数组
一维数组看作流水线, 把数组元素看作流水线上的一个个商品 ,那么二维数组就是流水线上一组组堆出了高度
的商品:
就像这样:
1 | ︹ ︹ ︹ |
进行了一个维度的跨升, 但肯定是不能直接在代码里写这么个流水线的, 還是得這麽寫:
1 | [ |
迭代二维数组
用双层for来进行迭代, 同样的, 三维数组可以使用三层for迭代:
1 | function printMatrix(myMatrix) { |
要取到某个元素,可直接使用如下方式:
array[2][1];
四、JavaScript数组方法参考
方法 | 说明 |
---|---|
concat | 连接两个或者更多数组 |
foreach | 对数组中每个元素执行函数,无返回值 |
every | 对数组中每个元素执行函数, 如果每个元素的执行都返回true,every()返回true; |
filter | 对数组中每个元素执行函数, 返回由所有执行时返回true的元素组成的数组 |
map | 对数组中每个元素执行函数, 返回所有执行结果组成的数组 |
some | 对数组中每个元素执行函数, 只要有一个元素返回true,some()就返回true |
join | 将所有数组元素链接为一个字符串 |
indexOf | 返回找到的第一个与给定参数相等的数组元素的索引号 |
lastIndexOf | 返回找到的所有与给定参数相等的数组元素中索引号最大的那个, 即返回所有与参数相等的元素中最靠右的那个 |
reverse | 颠倒数组元素的顺序,索引号不变 |
slice | 依据传入的索引值将将索引范围内的元素作为新数组返回 |
sort | 按照英文字母顺序对数组进行排序, 支持传入【指定排序方法的函数】作为参数 |
toString | 将数组转换为字符串返回 |
valueOf | 将数组转换为字符串返回, 与toSting相似 |
这其中的一些方法在函数式编程中十分有用; | |
这些我就不多记了, 都能查到. |
數組反排序: reverse()
對數組使用reverse()方法可以獲得一個數組的反序形態, 這個沒什麽好説的,
但是書上在對數組使用了reverse()之後還進行了一次sort排序, 我先去查了一下sort的特性,如果调用 sort() 方法时没有传递参数, 则按字母顺序对数组中的元素进行排序(就是按abcde這種順序);
但是書上給的示例是數字數組啊 ,這?
1 | let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; |
你可以看到用sort排序後又變成了亂七八糟的樣子, 因爲sort()進行排序時, 發現元素不是字符串, 它會试图把数组元素转换成字符串再进行比较.
那就不用它默認的排序方式了, 反正sort支持傳入compareFunction比較函數,:
1 | //比較函數使用了箭頭函數: (a, b) => {} |
這樣即實現了將反序數組重新撥正的目的;
值匹配搜索: indexOf && lastindexOf
先來看indexOf吧:
indexOf() 返回與參數匹配的第一個元素的索引.
1 | console.log(numbers.indexOf(10)); //返回9, 因爲值10所在的索引為9; |
對於 indexOf() 就是能找到的就會輸出目標數組元素的索引號, 找不到不存在的就輸出-1;
再來看lastIndexOf():
lastIndexOf返回與參數匹配的最後一個元素的索引.
意思就是:如果針對一個數組進行的查找產生了多個滿足條件的結果,豈會需選取最靠右的結果的索引號作爲返回值;
1 | numbers.push(10); |
輸出數組為字符串: toString && join
兩個方法都可以將數組轉換爲字符串, 但是…還是有點區別,那就是
toString是直接將數組元素硬性轉換為字符串, 説白了就是直接把外面的中括號去掉直接讓裏面的數組元素和逗號暴露出來;
1 | let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; |
然後就直接變成這樣了, 這樣一般都沒法直接用, 還得拆吧拆吧之類的處理一下…
然後就是join了, 這個方法特點就是你可以指定各個數組元素拆分後以什麽東西進行連接(原本在數組裏不是逗號嘛), 然後拆開之後你可以把它變成”1QAQ2QAQ3QAQ4”或者”1@2@3”之類的這種 (這其實有點像PHP裏的那個implode方法)…
示例:
1 | const numberString = numbers.join("QAQ"); |
五、ES6数组的新功能
新增方法 | 说明 |
---|---|
@@iterator | 返回一个包含数组键值对的迭代器对象,可以通过数组同步调用来得到数组元素的键值对 |
copyWithin | 复制数组中一系列元素到同一数组指定的起始位置 |
entries | 返回包含数组所有键值对的@@iterator(即索引號與值的键值对) |
keys | 返回包含数组所有索引号的@@iterator(即包含索引號的對象) |
values | 返回包含数组中所有值的@@iterator(即包含值的對象) |
includes | 速查, 数组中是否包含某个元素,返回布尔值(這是出自ECMAS7的方法) |
find | 可以注册回调函数, 其会根据回调函数规定的条件从数组中查找元素, 如果找到该元素则会返回. |
findIndex | 可以注册回调函数, 其会根据回调函数规定的条件从数组中查找元素, 找到后会返回其索引号 |
fill | 使用静态值填充数组 |
from | 根据已有数组创建一个新数组 |
of | 根据传入的参数创建一个新数组 |
使用for…of来进行遍历
for…of 對可迭代对象(包括 Array, Map, Set, String, TypedArray, arguments等)執行循环, 为每个不同属性的值执行语句
1 | let numbers = [3, 4, 7, 6]; |
公式:
1 | let 數組名 = [元素1, 元素2, 元素3, 元素4]; |
你可以看到在for…of語句中,key不再是for中的索引號, 而是各個數組元素;
@@iterator对象
ES6为Array类增加了一个@@iterator属性, 需要通过Symbol.iterator来访问.
1 | let numbers = [1, 2, 3, 4, 5]; |
形成對next()的反復调用,依次得到数组中的值:
取出索引號與值的键值对:entries()
在上面的表中我提到了这个entries(), 其返回包含数组所有键值对的@@iterator.
1 | let aEntries = number.entries(); |
這樣寫自然是不好看的,那麽:
示例:
1 | let array = [1, 2, 3]; |
公式:
1 | let 數組名 = [元素1, 元素2, 元素3]; |
“使用集合、字典、散列表等數據結構時, 能夠取出鍵值對是十分有用的”
返回包含索引號的對象:keys()
keys返回包含數組索引號的@@iterator:
示例:
1 | let array = [5, 4, 3, 2, 1]; |
公式:
1 | let 數組名 = [元素1, 元素2, 元素3, 元素4, 元素5]; |
至於後面那個done,它在英文中有”已完成”的意思,放在這裏即”是否已完成”,
如果它的值為false説明這個數組還沒被迭代完成, 你可以看到, 儅數組迭代到了末尾,done變爲true;
“一旦沒有可迭代的值, aKeys.next()就會返回一個value屬性為undedined, done屬性為true的對象”;
返回包含元素值的對象:values()
values()與keys()則是截然相反, 它不返回索引號而是返回元素的值:
示例:
1 | let array = [5, 4, 3, 2, 1]; |
done的作用同keys(), 用以判定當前數組是否迭代完成;
查找滿足函數條件的值: find() && findIndex()
這倆方法都能接收回調函數, 作用是去搜索能滿足回調函數條件的值,并且返回這些值.
但是也有些區別, 我們先來看find()吧:
find()返回找到的第一個滿足函數條件的值,重點在值.
而findIndex()返回的是滿足條件的值的索引號(見示例末行);
示例:
1 | //我這裏把回調函數寫外面了,像addEventListener那樣寫裏面也可以的. |
根據已有數組創建新的數組: from()
from()方法根據已有的數組創建一個新的數組, 比如要複製numbers數組如下:
1 | let numbers = [5, 4, 3, 2, 1]; |
但這樣直接全盤搬過來似乎意義不大, 大多數時候我們需要進行篩選來創建新的數組, 這下第二個參數位就能派上用場了, 它支持傳入一個用於過濾值的函數:
1 | let evens = Array.from(numbers, i => (i % 2 == 0)); |
依據傳入的參數創建新數組: Array.of()
Array.of 方法根據傳入的參數創建一個新的數組:
1 | let numbers3 = Array.of(1); |
你也可以向其内部傳入數組作爲參數! 還記得前面説到的展開運算符嗎?
1 | let numbers4 = [1, 2, 3, 4]; |
使用靜態值來填充數組: fill()
fill()可以讓你的數組充滿某個值, 你也可以用索引號界定範圍, 讓某個範圍的元素全部爲某個值;
1 | 數組名.fill(要填充的值, 開始処索引號(可選), 結束処索引號(可選)); |
不過要記著開始索引號對應的值會遭到填充, 但是結束処對應的值不會遭到填充, 比如開始処3, 結束処5, 那被填充的索引只有3,4.
1 | let numbersCopy = Array.of(1, 2, 3, 4, 5, 6); |
變數組某一段與另一段相同: copyWithin()
copyWithin()方法複製(注意是複製,不會對受複製者有影響)數組中一系列元素到指定的位置進行替換.
原理: 將參數二及其右邊的數組元素複製, 用於替換參數一及其右邊的相應個數的數組元素, 一定不會改變數組長度;
下面我先只改變參數一即插入點:
1 | let copyArray = [1, 2, 3, 4, 5, 6]; |
1 | let copyArray = [1, 2, 3, 4, 5, 6]; |
公式:
1 | let 數組名 = [元素1, 元素2, 元素3, 元素4, 元素5, 元素6]; |
檢測數組中是否存在某元素: includes()
只能檢測有還是沒有, 返回布爾值,;
公式:
1 | 數組名.includes(要查找的值); |
示例:
1 | let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; |
六、类型化数组
ES6的一大壯舉是給JavaScript這種弱類型語言加上了類型這一概念并且付諸實踐, 而類型數組即是這次技術革命的結晶之一…
“類型數組用於存儲單一類型的數據, 它的語法是let myArray = new TypedArray(length), 其中TypedArray需要替換爲下表所列之一.”
不过,我去查了很多资料,这个东西现在跟前端的关联也有但说实话个人认为它应该是在数据处理方面,在进制和字节层面上使用,就平常写个页面之类的是用不上了。
而且涉及的知识面比较广,这里我不过于深究防止误导,只说一下相关的一些概念和名词(这些书上都没写)
公式:
1 | let [自定義名] = new [數組類型]Array([數組長度]); |
示例:
1 | let length = 5; |
不説這麽多了, 上面説要看表, 那我先把表放下吧:
| 類型數組 | 數據類型 |
|–|–|
| Int8Array | 8位二進制補碼整數 |
| Uint8Array | 8位無符號整數 |
| Uint8ClampedArray | 8位無符號整數(對, 沒寫錯) |
| Int16Array | 16位二進制補碼整數 |
| Uint16Array | 16位無符號整數 |
| Int32Array | 32位二進制補碼整數 |
| Uint32Array | 32位無符號整數 |
| Float32Array | 32位IEEE浮點數 |
| Float64Array | 64位IEEE浮點數 |
注意你不能再使用new Array()的那种方式填充类型数组:
1 | //错误示范, 这样是填不进去的, 括号里最多写一个参数来规定length; |
而是应该:
1 | let f64a = new Float64Array(8); |
“使用WebGL API、进行位操作、处理文件和图象时, 类型数组都可以大展拳脚。 它用起来和普通数组毫无二致,本章所学的数组方法和功能都可以用于类型数组。” 其实出自书中的这句话有错误。
首先在类型数组上调用 Array.isArray() 会返回false。此外,并不是所有可用于正常数组的方法都能被类型化数组所支持(如 push 和 pop),我也亲手试了下确实不能用,MDN尤其强调不要把普通数组和类型数组混为一谈。
DataView:
是一种底层接口,它提供可以操作ArrayBuffer中任意数据的API。这对操作不同类型数据的场景很有帮助,例如:类型化数组视图都是运行在本地字节序模式,可以通过使用 DataView 来控制字节序。
ArrayBuffer 对象:
用来表示通用的、固定长度的原始二进制存储空间(或者说…二进制数据缓冲区? ), 它是一个字节数组(在某些语言中称作byte array)。我们不能直接操作 ArrayBuffer 中的内容,而是要通过 类型数组 或 DataView 对象来操作,这两种方法我会各举一个例子,它们会将ArrayBuffer中的数据表示为特定的格式,由此来读写缓冲区的内容。
前端方面只要是处理大数据或者想提高数据处理性能,一定少不了 ArrayBuffer.
看下这个ArrayBuffer, 他后面那个小括号里的数字就是指生成出了一个多少字节的存储空间:
1 | var buffer = new ArrayBuffer(16); |
为了读写这段内容,需要为它创建一个视图,视图将把 ArrayBuffer 这个二进制存储空间内的数据格式化为一个(就本例来说)32位的有符号整数数组:
这里使用类型数组方式进行读取:
1 | var int32View = new Int32Array(buffer); |
现在即可以访问普通数组的形式进行访问:
1 | for (var i = 0; i < int32View.length; i++) { |
或者
使用DataView的方式进行读取:
1 | var buf = new ArrayBuffer(32); |
以不带符号的8位整数格式,读取第一个元素得到0,因为ArrayBuffer对象内默认所有位都是0:
可以点这看看大佬写的
总结
下一篇:《学习JavaScript数据结构与算法》第四章 栈
我寫了14000個字, 14000個字啊(震声)!
你看我这么耐心的整理(雖然寫了很多廢話), 它现在比原作都長!
這很累人的!
所以, 所以給它点個贊好嗎?