《学习JavaScript数据结构与算法》第三章 14000字笔记


前言

本文包括数组操作方法、ES6新增数组功能、JS矩阵、类型化数组等;

前面是基础部分,如果你不是小白的话…个人认为可以从第三章开始?
上一篇:《学习JavaScript数据结构与算法》第二章 ES和TS概述


一、创建 && 初始化数组

使用JavaScript声明, 创建和初始化数组;

使用new关键字,来简单的声明并初始化一个数组。
该种方式的优点是可以直接在声明时就确定好数组的长度:

daysOfweek = new Array(数组长度,数字型);
daysOfweek1 = new Array(7);

或者直接使用中括号形式:

let daysOfweek = [];
let daysOfweek1 = ['one', 'two', 'three'];

get数组长度:

console.log(数组名.length);

二、操作数组

添加元素于末尾

直接将要添加的数组元素赋值给数组的最后一位,但这种方法一次性只能添加一个元素:

let array = ["one", "two", "three"];
array[array.length] = 新数组元素;
console.log(array);

我们在实战中更多使用的方法是push(),这是一个专用于处理数组的方法:

let array = ["one", "two", "three"];
array.push(要添加的元素1, 要添加的元素2, ...);
array.push(array2[3]);

可以一次性将多个元素依次加入数组的末尾.


添加元素于开头

将新的数组元素直接添加至数组开头, 实操一般会使用unshift方法:

let numbers = [3, 4, 55];
numbers.unshift(-2);
unmbers.unshift(-2, 3);
console.log(numbers);

从数组末尾开始删除元素

使用pop()方法来从末尾删除数组中的元素:
书上并没有详细阐述这一方法, 只给了一个实例, 只好跑一下试试,

let numbers = [1, 2, 3, 4, 5];
numbers.pop();
console.log(numbers);

在这里插入图片描述
pop()方法固定只移除最后一个元素, 给参数也没用…


从数组开头开始删除元素

for(let i = 0; i < numbers.length; i++) {
    
    
  numbers[i] = numbers[i + 1];
}

这相当于在数组序列号不变的情况下将所有数组元素整体左移了一个单位,
但在这种情况下输出数组会发现数组的长度依旧未被改变,这说明数组中必然有一个元素的值为undefined:
在这里插入图片描述
可以看到, 我们只是把数组第一位的值用第二位进行了覆盖, 第一位并未被删除.

但在实战中我们一般使用shift方法:

let num = [1, 2, 3];
numbers.shift();

这种方法会直接导致数组长度减小.


在数组任意位置添加或删除元素

删除和添加操作均可使用splice()来完成;
使用splice()方法, 通过指定起始位置和删除个数来完成删除操作;

//注意是从左往右删;
numbers.splice(起始序列号, 删除数量);

使用splice()方法, 通过指定起始位置和传入要添加的元素来完成添加:

//执行添加操作, 第二个参数请传0;
number.splice(起始序列号, 删除数量, 元素1, 元素2, 元素3, ...)

你看可以看到第二个参数依旧是有效的?
我只打算添加元素, 你要删除也可以, 我慷慨的允许你删0个 .


三、二维 多维数组

矩阵, 指包含二维数组在内的数组含有数组的结构;

JavaScript只支持一维数组并不支持矩阵,但是可以用如下数组嵌套的方法来实现矩阵.

构建二维数组

一维数组看作流水线, 把数组元素看作流水线上的一个个商品 ,那么二维数组就是流水线上一组组堆出了高度
的商品:

就像这样:

     ︹  ︹     ︹
     6   4      3
     3   5      9
     2   1      0
[ 3,,, 5,, 6 ]

进行了一个维度的跨升, 但肯定是不能直接在代码里写这么个流水线的, 還是得這麽寫:

[
3, 
[6, 3, 2],
[4, 5, 1],
5,
[3, 9, 0],
6,
]

迭代二维数组

用双层for来进行迭代, 同样的, 三维数组可以使用三层for迭代:

function printMatrix(myMatrix) {
    
    
  for(let i = 0; i < myMatrix.length; i++) {
    
    
    for(let j = 0; j < myMatrix[i].length; j++) {
    
    
      console.log(myMatrix[i][j]);
    }
  }
}

要取到某个元素,可直接使用如下方式:
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這種順序);
但是書上給的示例是數字數組啊 ,這?

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
numbers.reverse();
console.log(numbers);
numbers.sort()
console.log(numbers);

在这里插入图片描述
你可以看到用sort排序後又變成了亂七八糟的樣子, 因爲sort()進行排序時, 發現元素不是字符串, 它會试图把数组元素转换成字符串再进行比较.
那就不用它默認的排序方式了, 反正sort支持傳入compareFunction比較函數,:

//比較函數使用了箭頭函數: (a, b) => {}

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
numbers.reverse();
numbers.sort();
console.log(numbers.sort((a, b) => a - b));

/
//sort()會根據返回值的正負來判讀值之間的大小關係,相當於如下;

function compare() {
    
    
  if (a < b) {
    
    
    return -1;
  }
  if (a > b) {
    
    
    return 1;
  }
return 0;
}
numbers.sort(compare);  //向sort傳入比較函數作參;

這樣即實現了將反序數組重新撥正的目的;


值匹配搜索: indexOf && lastindexOf

先來看indexOf吧:
indexOf() 返回與參數匹配的第一個元素的索引.

console.log(numbers.indexOf(10)); //返回9, 因爲值10所在的索引為9;
console.log(numbers.indexOf(100)); //返回-1, 因爲數組裏壓根沒有100;

對於 indexOf() 就是能找到的就會輸出目標數組元素的索引號, 找不到不存在的就輸出-1;

再來看lastIndexOf():
lastIndexOf返回與參數匹配的最後一個元素的索引.
意思就是:如果針對一個數組進行的查找產生了多個滿足條件的結果,豈會需選取最靠右的結果的索引號作爲返回值;

numbers.push(10);
console.log(numbers.lastIndexOf(10));  //10
console.log(numbers.lastIndexOf(100));  //-1

輸出數組為字符串: toString && join

兩個方法都可以將數組轉換爲字符串, 但是…還是有點區別,那就是
toString是直接將數組元素硬性轉換為字符串, 説白了就是直接把外面的中括號去掉直接讓裏面的數組元素和逗號暴露出來;

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(numbers.toString());

然後就直接變成這樣了, 這樣一般都沒法直接用, 還得拆吧拆吧之類的處理一下…
在这里插入图片描述
然後就是join了, 這個方法特點就是你可以指定各個數組元素拆分後以什麽東西進行連接(原本在數組裏不是逗號嘛), 然後拆開之後你可以把它變成"1QAQ2QAQ3QAQ4"或者"1@2@3"之類的這種 (這其實有點像PHP裏的那個implode方法)…
示例:

const numberString = numbers.join("QAQ");
console.log(numbersString);

在这里插入图片描述


五、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等)執行循环, 为每个不同属性的值执行语句

let numbers = [3, 4, 7, 6];
for (const n of numbers) {
    
    
  console.log(n);
}

公式:

let 數組名 = [元素1, 元素2, 元素3, 元素4];
for (const key名稱 of 數組名) {
    
    
  console.log(key名稱);
}

在这里插入图片描述
你可以看到在for…of語句中,key不再是for中的索引號, 而是各個數組元素;


@@iterator对象

ES6为Array类增加了一个@@iterator属性, 需要通过Symbol.iterator来访问.

let numbers = [1, 2, 3, 4, 5];
let iterator = numbers[Symbol.iterator]();
console.log(iterator.next().value);
console.log(iterator.next().value);
console.log(iterator.next().value);
console.log(iterator.next().value);
console.log(iterator.next().value);

形成對next()的反復调用,依次得到数组中的值:
在这里插入图片描述


取出索引號與值的键值对:entries()

在上面的表中我提到了这个entries(), 其返回包含数组所有键值对的@@iterator.

let aEntries = number.entries();
console.log(aEntries.next().value);
console.log(aEntries.next().value);
console.log(aEntries.next().value);

這樣寫自然是不好看的,那麽:
示例:

let array = [1, 2, 3];
aEntries = array.entries();
for(const n of aEntries) {
    
    
  console.log(n);
}

公式:

let 數組名 = [元素1, 元素2, 元素3];
自定義名 = 數組名.entries();
for(const key名稱 of 自定義名) {
    
    
  console.log(key名稱);
}

在这里插入图片描述
“使用集合、字典、散列表等數據結構時, 能夠取出鍵值對是十分有用的”


返回包含索引號的對象:keys()

keys返回包含數組索引號的@@iterator:
示例:

let array = [5, 4, 3, 2, 1];
const aKeys = array.keys();
for (let i = 0; i <= array.length;i++) {
    
    
  console.log(aKeys.next());
}

公式:

let 數組名 = [元素1, 元素2, 元素3, 元素4, 元素5];
const 自定義名 = 數組名.keys();
for (let i = 0; i <= 數組名.length;i++) {
    
    
  console.log(自定義名.next());
}

在这里插入图片描述
至於後面那個done,它在英文中有"已完成"的意思,放在這裏即"是否已完成",
如果它的值為false説明這個數組還沒被迭代完成, 你可以看到, 儅數組迭代到了末尾,done變爲true;
“一旦沒有可迭代的值, aKeys.next()就會返回一個value屬性為undedined, done屬性為true的對象”;


返回包含元素值的對象:values()

values()與keys()則是截然相反, 它不返回索引號而是返回元素的值:

示例:

        let array = [5, 4, 3, 2, 1];
        const aKeys = array.values();
        for (let i = 0; i <= array.length; i++) {
    
    
            console.log(aKeys.next());
        }

在这里插入图片描述
done的作用同keys(), 用以判定當前數組是否迭代完成;


查找滿足函數條件的值: find() && findIndex()

這倆方法都能接收回調函數, 作用是去搜索能滿足回調函數條件的值,并且返回這些值.
但是也有些區別, 我們先來看find()吧:
find()返回找到的第一個滿足函數條件的值,重點在值.
而findIndex()返回的是滿足條件的值的索引號(見示例末行);
示例:

//我這裏把回調函數寫外面了,像addEventListener那樣寫裏面也可以的.
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];

function multipleOf13(element) {
    
    
  return (element % 13 == 0);
}

console.log(numbers.find(multipleOf13)); //13
//因爲數組裏滿足13倍數的就只有13一個, 所以直接返回了元素值13.


console.log(numbers.findIndex(multipleOf13)); //12
//數組裏滿足13倍數的只有13一個, 所以直接返回值13的索引號12.

根據已有數組創建新的數組: from()

from()方法根據已有的數組創建一個新的數組, 比如要複製numbers數組如下:

let numbers = [5, 4, 3, 2, 1];
let numbers2 = Array.from(numbers);  
//from()利用numbers數組生成新數組numbers2
console.log(numbers2);

在这里插入图片描述
但這樣直接全盤搬過來似乎意義不大, 大多數時候我們需要進行篩選來創建新的數組, 這下第二個參數位就能派上用場了, 它支持傳入一個用於過濾值的函數:

let evens = Array.from(numbers, i => (i % 2 == 0));
//from()利用numbers數組生成新數組evens;
//並根據數組元素的奇偶來決定填充的布爾值;
console.log(evens);

在这里插入图片描述


依據傳入的參數創建新數組: Array.of()

Array.of 方法根據傳入的參數創建一個新的數組:

let numbers3 = Array.of(1);
let numbers4 = Array.of(1, 2, 3, 4, 5);
//相當於let numbers4 = [1, 2, 3, 4, 5];
console.log(numbers3);
console.log(numbers4);

你也可以向其内部傳入數組作爲參數! 還記得前面説到的展開運算符嗎?

let numbers4 = [1, 2, 3, 4];
let numbersCopy = Array.of(...numbers4);
//相當於let numbersCopy = Array.of(1, 2, 3, 4);
console.log(numbersCopy);

在这里插入图片描述


使用靜態值來填充數組: fill()

fill()可以讓你的數組充滿某個值, 你也可以用索引號界定範圍, 讓某個範圍的元素全部爲某個值;

數組名.fill(要填充的值, 開始処索引號(可選), 結束処索引號(可選));

不過要記著開始索引號對應的值會遭到填充, 但是結束処對應的值不會遭到填充, 比如開始処3, 結束処5, 那被填充的索引只有3,4.

let numbersCopy = Array.of(1, 2, 3, 4, 5, 6);
numbersCopy.fill(0, 2, 4);
console.log(numbersCopy);

在这里插入图片描述


變數組某一段與另一段相同: copyWithin()

copyWithin()方法複製(注意是複製,不會對受複製者有影響)數組中一系列元素到指定的位置進行替換.
原理: 將參數二及其右邊的數組元素複製, 用於替換參數一及其右邊的相應個數的數組元素, 一定不會改變數組長度;

下面我先只改變參數一即插入點:

let copyArray = [1, 2, 3, 4, 5, 6];
copyArray.copyWithin(1, 4);  //從索引1開始替換;
console.log(copyArray);  //(6) [1, 5, 6, 4, 5, 6]
let copyArray = [1, 2, 3, 4, 5, 6];
copyArray.copyWithin(0, 4);  //從索引0開始替換;
console.log(copyArray);  //(6) [5, 6, 3, 4, 5, 6]

公式:

let 數組名 = [元素1, 元素2, 元素3, 元素4, 元素5, 元素6];
數組名.copyWithin(替換起始點, 複製起始點);  //兩個點都包含本點;

檢測數組中是否存在某元素: includes()

只能檢測有還是沒有, 返回布爾值,;
公式:

數組名.includes(要查找的值);

示例:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(numbers.includes(15));
console.log(numbers.includes(3));

六、类型化数组

ES6的一大壯舉是給JavaScript這種弱類型語言加上了類型這一概念并且付諸實踐, 而類型數組即是這次技術革命的結晶之一…

“類型數組用於存儲單一類型的數據, 它的語法是let myArray = new TypedArray(length), 其中TypedArray需要替換爲下表所列之一.”

不过,我去查了很多资料,这个东西现在跟前端的关联也有但说实话个人认为它应该是在数据处理方面,在进制和字节层面上使用,就平常写个页面之类的是用不上了。
而且涉及的知识面比较广,这里我不过于深究防止误导,只说一下相关的一些概念和名词(这些书上都没写)

公式:

let [自定義名] = new [數組類型]Array([數組長度]);

示例:

let length = 5;
let int16 = new Int16Array(length);

不説這麽多了, 上面説要看表, 那我先把表放下吧:

類型數組 數據類型
Int8Array 8位二進制補碼整數
Uint8Array 8位無符號整數
Uint8ClampedArray 8位無符號整數(對, 沒寫錯)
Int16Array 16位二進制補碼整數
Uint16Array 16位無符號整數
Int32Array 32位二進制補碼整數
Uint32Array 32位無符號整數
Float32Array 32位IEEE浮點數
Float64Array 64位IEEE浮點數

注意你不能再使用new Array()的那种方式填充类型数组:

//错误示范, 这样是填不进去的, 括号里最多写一个参数来规定length;
 let int16 = new Int16Array(5, 4, 3, 2, 1);
 

而是应该:

let f64a = new Float64Array(8);
f64a[0] = 10;
f64a[1] = 20;
f64a[2] = f64a[0] + f64a[1];
console.log(f64a);

在这里插入图片描述

“使用WebGL API、进行位操作、处理文件和图象时, 类型数组都可以大展拳脚。 它用起来和普通数组毫无二致,本章所学的数组方法和功能都可以用于类型数组。” 其实出自书中的这句话有错误。

首先在类型数组上调用 Array.isArray() 会返回false。此外,并不是所有可用于正常数组的方法都能被类型化数组所支持(如 push 和 pop),我也亲手试了下确实不能用,MDN尤其强调不要把普通数组和类型数组混为一谈。

DataView:
是一种底层接口,它提供可以操作ArrayBuffer中任意数据的API。这对操作不同类型数据的场景很有帮助,例如:类型化数组视图都是运行在本地字节序模式,可以通过使用 DataView 来控制字节序。

ArrayBuffer 对象:
用来表示通用的、固定长度的原始二进制存储空间(或者说…二进制数据缓冲区? ), 它是一个字节数组(在某些语言中称作byte array)。我们不能直接操作 ArrayBuffer 中的内容,而是要通过 类型数组 或 DataView 对象来操作,这两种方法我会各举一个例子,它们会将ArrayBuffer中的数据表示为特定的格式,由此来读写缓冲区的内容。
前端方面只要是处理大数据或者想提高数据处理性能,一定少不了 ArrayBuffer.

看下这个ArrayBuffer, 他后面那个小括号里的数字就是指生成出了一个多少字节的存储空间:

var buffer = new ArrayBuffer(16);

为了读写这段内容,需要为它创建一个视图,视图将把 ArrayBuffer 这个二进制存储空间内的数据格式化为一个(就本例来说)32位的有符号整数数组:
这里使用类型数组方式进行读取:

var int32View = new Int32Array(buffer);

现在即可以访问普通数组的形式进行访问:

for (var i = 0; i < int32View.length; i++) {
    
    
  int32View[i] = i * 2;
}

或者
使用DataView的方式进行读取:

var buf = new ArrayBuffer(32);
//DataView的创建,需要提供ArrayBuffer实例作为参数:
var dataView = new DataView(buf);
//然后以不带符号的8位整数格式读取第一个元素:
dataView.getUint8(0) // 0

以不带符号的8位整数格式,读取第一个元素得到0,因为ArrayBuffer对象内默认所有位都是0:
在这里插入图片描述
可以点这看看大佬写的

总结

下一篇:《学习JavaScript数据结构与算法》第四章 栈
我寫了14000個字, 14000個字啊(震声)!
你看我这么耐心的整理(雖然寫了很多廢話), 它现在比原作都長!
這很累人的!

所以, 所以給它点個贊好嗎?

猜你喜欢

转载自blog.csdn.net/qq_52697994/article/details/120845432