摘要:沒有循環循環次四屬性屬性屬性表示數組元素的數量,的數組元素并不是連續的,有些索引的位置可能沒有元素,所以屬性并不能真正表示元素的數量,其值等于數組最大索引。
一、JS沒有“真正的數組”
像C++,Java這些編程語言中數組元素分配的內存都是連續,這有利于性能提升,但是JS的數組不是這樣的。它使用對象模擬數組,即對象屬性為數字,并含有length屬性。所以JS數組對象的內存不是連續的,同一般對象內存分配。
二、創建數組 2.1 字面量方式var a = [], // 定義空數組 b = [1,true], // 數組元素用逗號隔開 c = [1,,3], // 忽略中間的元素 d = [1,2,]; // 末尾是逗號
注意:
數組元素類型可以各不相同,因為JS數組本質是個對象;
b[1]元素是未定義的,不是取值是undefined的元素;
數組d的長度是2,不是3,因為字面量準許末尾是逗號。
2.2 使用構造函數Arrayvar a = new Array(), // 等價 [] b = new Array(2), // 等價 [,,], 注意這里是兩個逗號哦 c = new Array(1,2), // 等價 [1, 2] d = Array(1,2); // 等價于new Array(1,2)
注意:
使用構造函數Array比較坑的就是不同數量的參數,Array函數的行為不一致。
Array即是構造函數也是工廠函數。即new Array() 等價于直接調用Array();
三、索引和屬性名稱訪問對象的屬性是通過屬性名稱,而訪問數組的元素則是通過索引。索引即為數組元素的下標。索引是32位的正整數,有效取值范圍是[0, 2^32 - 2](因為數組的length屬性也是32位整數,所有下標最大為2^32-2),不在這個范圍的值都不是索引。雖然JS沒有整數類型,但索引的操作都是按照32位正整數方式處理的。數組本質也是對象,也是可以通過屬性名稱的方式訪問數組的屬性。
var a = [1,2], b = { 0: 1, 1: 2 }; console.log(a[1]); // 索引訪問方式 console.log(a["1"]); // 索引訪問方式,會把"1"轉成正整數1 console.log(b[1]); // 屬性名稱訪問方式,會把1轉成字符串“1”
注意:
索引是一種特殊的屬性名稱;
屬性名稱方式會把中括號里的表達式轉成字符串,索引方式會把中括號里的表達式轉成32整數,如果不是合法的索引,則視為屬性名稱,所以JS數組不存在下標越界的問題。
3.2 稀疏數組JS數組元素不一定是連續的,索引位置上沒有元素(沒有元素跟取值是undefined的元素是不同的)的數組叫稀疏數組。
var a = [,], // 定義即為稀疏數組 b = Array(3), // 定義即為稀疏數組 c = [1,2,3]; delete c[1]; // delete操作造成稀疏
注意:
再強調索引位置上沒有元素跟取值是undefined的元素不一樣的(有些數組的方法, 運算符的行為不一樣)。
for(var a in [,]) {console.log(1)} // 沒有循環 for(var a in [1,2]) {console.log(1)} // 循環2次四、length屬性 4.1 length屬性
length屬性表示“數組元素的數量”,JS的數組元素并不是連續的,有些索引的位置可能沒有元素,所以length屬性并不能真正表示元素的數量,其值等于數組最大索引+1。并且length屬性是可寫的
var arr = [1]; arr.length; // 1 arr.length = 3; // 增大length屬性值 arr.length;// 3, 索引1,2位置是未定義的元素。 arr.length = 0; // 減小length屬性值 arr[0]; //undefined4.2 偽數組
行為有點像數組的對象叫偽數組。廣義上只要含有length屬性且length值是在索引有效取值范圍內(或可以通過類型轉換成有效索引)的對象都可以視為偽數組。偽數組可以應用數組的一些方法,也可以反過來定義:可以應用數組方法的對象叫偽數組對象。
var a = {length: 2}; // a是偽數組 Array.prototype.slice.call({length: 2}); // 可以應用slice方法,其結果等價于Array(2)的結果五、方法 5.1 ES3
主要是操作元素相關的方法
1. push/pop 2. unshift/shift之前一直混淆unshift和shift的功能。一般都記得push是向數組尾部插入數據,pop是從數組尾部彈出元素,可以借助push/pop記憶unshift/shift。push名字比pop長,而unshift名字也比shift長。即push和unshift功能相似,并且名字都比對應功能的方法pop/shift名字長。長對長,短對短,估計再也不會混淆unshift和shift方法的功能了。
3. join最近看到某個框架源碼有這么個片段:
var indent = ""; for (i = 0; i < space; i += 1) { indent += " "; }
大概意思就是根據參數space生成指定長度的空格字符串。可以通過join方法改進下哈:
var indent = Array(space + 1).join(" "); // 記得+1,否則字符串長度少1
join方法會把值為undefined/null的數組元素轉成空字符串。
4. reverse 5. sort 6. concat一直以為該方法用于多個數組合并,其實除了的功能外還可以把非數組類型的參數插入返回值數組里。
var a = [1, 2]; a.concat([3, 4]) // [1, 2, 3, 4] a.concat(3, 4) // [1, 2, 3, 4]7. slice 8. splice
splice方法可以實現對數組任意位置,任意數量的元素進行增加,替換,刪除操作。
var a = [1, 2, 3, 4, 5]; // 替換:將元素2,3替換成10,11 a.splice(1, 2, 10, 11) console.log(a) // [1, 10, 11, 4, 5] // 刪除:刪除10,11 a.splice(1, 2) // [1, 4, 5] // 插入:在元素4后面插入元素22,23,24 a.splice(2, 0, 22, 23, 24) console.log(a) // [1, 4, 22, 23, 24, 5]
splice的返回值是被刪除或者替換的元素的集合
大部分情況我們經常對數組的首尾進行添加刪除操作,所以一般使用push/pop, unshift/shift方法多些。
5.2 ES5主要是遍歷和基于遍歷的搜索、診斷相關的方法
1. forEach 2. map 3. filter 4. every/some 5. reduce/reduceRight是否指定初始值循環的次數不一樣的
reduce應用場景很多,認真看下MDN Demos,還有這個面試題
改成純Promise版:
function genTask(action, delay, context) { return function() { return new Promise(resolve => { action && action.call(context); setTimeout(resolve, delay == null ? 0 : (delay * 1000)) }) } } function machine(name) { var tasks = []; tasks.push(genTask(function() { console.log(`start ${name}`) })) function execute() { var self = this; tasks.reduce((promise, task) => { return promise.then(task) }, Promise.resolve()) } function _do(task) { tasks.push(genTask(function() { console.log(`${name} ${task}`) })) return this } function wait(delay) { tasks.push(genTask(() => { console.log(`wait ${delay}s`); }, delay, null)) return this } function waitFirst(delay) { tasks.unshift(genTask(() => { console.log(`wait ${delay}s`); }, delay, null)) return this } return { name: name, execute: execute, do: _do, wait: wait, waitFirst: waitFirst } } machine("ygy") .waitFirst(3) .do("eat") .execute();
Demo 重學 JS:為啥 await 在 forEach 中不生效這里也有個題目可以用reduce實現:
function fetch(x) { return new Promise((resolve, reject) => { console.log(x) setTimeout(() => { resolve(x) }, 500 * x) }) } async function test() { let arr = [3, 2, 1] await arr.reduce(async (promise, item) => { await promise; console.log(item) return await fetch(item); }, Promise.resolve()) console.log("end") } test();6. indexOf/lastIndexOf
采用絕對相等(===)的判斷邏輯。
7. Array.isArray 注意:forEach, map, filter, every/some, reduce/reduceRights, indexOf/lastIndexOf都會有遍歷數組的行為,可以根據不同的需求選用不用的遍歷方法,并且都不會遍歷數組中被刪除或從未被賦值的元素,見稀疏數組;
有人嘗試把async函數作為上述數組具有遍歷功能的回調函數,但可能得到意想不到的結果,比如這個重學 JS:為啥 await 在 forEach 中不生效。不僅僅是forEach,其他的遍歷方法也都只處理同步代碼。
異步函數的返回值是個Promise對象,相當于這些遍歷方法實際在操作Promise對象。
5.3 ES6主要是添加了新的功能,讓數組使用的更加方便
1. copyWithin 2. entries 3. fill 3. find 4. findIndex功能類似ES5點indexOf,參數不同,是indexOf的加強版:更靈活,使用回調函數可以更靈活的控制相等判斷邏輯。
5. includes判斷數組是否包含指定的元素,在此之前我們一般借助indexOf方法的返回只是否為-1判斷元素是否存在():
var a = [1, 2, 4]; a.indexOf(1) !== -1 // true, 存在 a.indexOf(6) !== -1 // false, 不存在
ES6引入includes方法專門用來判斷元素是否存在,并且采用的是0值相等的等值判斷算法,indexOf方法采用的絕對相等算法。
var a = [1, NaN]; a.includes(NaN) // true, 存在 a.indexOf(NaN) !== -1 // false, 不存在6. keys 7. values 8. [Symbol.iterator] 9. [Symbol.species] 10. Array.of
我們都知道Array的構造函數根據的參數數量的不同具有不同的行為:
Array(7); // 一個參數表示數組的長度:構建長度為7的數組 Array(1, 2, 3); // 多個參數表示數組的元素:構建數組為[1, 2, 3]
而Array.of方法統一了這種行為,都是用來根據元素構建數組:
Array.of(7); // [7] Array.of(1, 2, 3); // [1, 2, 3]11. Array.from 參考
MDN Array
MDN Array.prototype
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92569.html
摘要:新建數組新建數組的方法有三種方法一方法二方法三新增是中新增的將一組值轉換為數組的方法,該方法的出現時為了彌補構造函數因為參數不同導致的不同行為。 原文鏈接:http://mrzhang123.github.io/2016/08/03/js-Array 在ECMAScript中最常用的類型之一就是Array類型,Array類型的方法也有很多,所以在這篇文章中,梳理一下Array類型的方法...
閱讀 2423·2019-08-29 13:53
閱讀 2507·2019-08-29 11:32
閱讀 3047·2019-08-28 17:51
閱讀 3776·2019-08-26 10:45
閱讀 3492·2019-08-23 17:51
閱讀 2983·2019-08-23 16:56
閱讀 3336·2019-08-23 16:25
閱讀 3085·2019-08-23 14:15