数组
数组,存储有序的集合。
数组,可以存储任何类型的元素。
创建数组
创建一个空数组有两种语法:
// 创建空数组
let arr = new Array();
let arr = [];
// 创建并初始化
let arr = new Array("a", "b");
let arr = ["a", "b"];
// new Array() 使用单个数字参数来指定数组长度
let arr = new Array(3);
alert(arr.length); // 3
可以通过下标替换或新增元素:
let fruits = ["Apple", "Orange", "Plum"];
fruits[2] = "Pear"; // 现在变成 ["Apple", "Orange", "Pear"]
fruits[3] = 'Lemon'; // 现在变成 ["Apple", "Orange", "Pear", "Lemon"]
// 新增下标可以不连续
fruits[6] = "grape"; // 现在变成["Apple", "Orange", "Pear", "Lemon", "", "", "grape"]
数组的栈方法和队列方法
JavaScript中的数组,既可以用作队列,也可以用作栈。
在计算机科学中,允许这样的操作的数据结构被称为双端队列(deque)。
队列(queue):FIFO(First-in-First-out) 先进先出
栈:LIFO(Last-in-First-out) 后出先进
pop/push方法(栈方法)
pop
取出并返回数组的最后一个元素
let fruits = ["apple", "orange", "pear"];
alert(fruits.pop()); // pear
alert(fruits); // apple, orange
push
在数组末端添加元素(可以添加多个)
let fruits = ["apple", "orange", "pear"];
fruits.push("grape");
alert(fruits); // apple, orange, pear, grape
shift/unshift方法(队列方法)
shift
取出并返回数组的第一个元素
let fruits = ["apple", "orange", "pear"];
alert(fruits.shift()); // apple
alert(fruits); // orange, pear
unshift
在数组的首端添加元素(可以添加多个)
let fruits = ["orange", "pear"];
fruits.unshift("apple"); //
alert(fruits); // apple, orange, pear
性能
结论:pop/push
方法运行的比较快,而shift/unshift
比较慢
原因:shift/unshift
除了添加或移除首端元素之外,还需要对其他所有元素重新编号。pop/push
不需要移动任何东西,
遍历数组
for循环
运行的最快,可兼容旧版本浏览器。
let fruits = ["apple", "orange", "pear"];
for (let i = 0; i < fruits.length; i++) {
alert(fruits[i]);
}
for...of方法
for...of
不能获取当前元素的索引,只是获取元素值。
let fruits = ["apple", "orange", "pear"];
for (let fruit of fruits) {
alert(fruit);
}
forEach(func)方法
let fruits = ["apple", "orange", "pear"];
fruits.forEach((item, index, array) => {
alert(`${item} is at index ${index} in ${array}`);
})
语法:
*arr*.forEach( callback( currentValue [, index ,array] )[, thisArg] )
依次向callback
函数传入3个参数:
- 数组当前项的值
- 数组当前项的索引(可选)
- 数组对象本身(可选)
如果
thisArg
参数有值,则每次callback
函数被调用时,this
都会只想thisArg
参数;如果省略了thisArg
参数,或值为null
或undefined
,this
则只想全局对象。
关于 "length"
length
有两点需要注意:
- 它实际上不是数组里的元素的个数,而是最大的数字索引值加1。
例如:一个数组只有一个元素,但是这个元素的索引值很大,那么这个数组的
length
也会很大。
let fruits = [];
fruits[123] = "apple"
alert(fruits.length); // 124
length
是可写的。 如果我们手动增加它,则不会发生任何事。但是如果我们减少它,数组就会被截断。该过程是不可逆的。
常用方法
arr.splice()
语法:
arr.splice(start [, deleteCount, elem1, ... , elemn ])
它从索引start
开始(允许负向索引),删除deleteCount
个元素,并在当前位置插入elem1, ..., elemn
。最后返回已被删除的元素的数组。
- 删除元素
let arr = ["I", "study", "JavaScript"];
let result = arr.splice(1, 1);
alert(arr); // I, JavaScript
alert(result); // study
- 替换元素
let arr = ["I", "study", "JavaScript", "right", "now"];
let result = arr.splice(0, 3, "Let's", "dance");
alert(arr); // Let's, dance, right, now
alert(result); // I, study, JavaScript
- 新增元素
let arr = ["I", "study", "JavaScript"];
arr.splice(2, 0, "complex", "language");
alert(arr); // I, study, complex, language, JavaScript
arr.slice()
语法:
arr.slice([start, end]);
它返回一个新数组,将所有从索引start
到end
(不包含end
)的数组项复制到一个新的数组(不影响原数组)。(允许负向索引)
let arr = ["I", "study", "JavaScript", "right", "now"];
alert( arr.slice(0,3) ); // I, study, JavaScript
alert( arr.slice(-2) ); // right, now
alert( arr.slice() );// I, study, JavaScript,right, now
arr.concat()
arr.concat(arg1, arg2 ...)
它返回一个新数组,其中包含来自于其他数组和其他项的值
如果参数argN
是一个数组,那么其中的所有元素都会被复制;否则,将复制参数本身。
let arr = [1, 2];
let arrayLike = {
0: "something",
length: 1
};
alert(arr.concat(arrayLike)); // 1,2,[object Object]
console.log(arr.concat(arrayLike));
但是,如果类似数组的对象具有Symbol.isConcatSpreadable
属性,那么它就会被concat
当作一个数组来处理:
let arr = [1, 2];
let arrayLike = {
0: "something",
1: "else",
length: 2,
[Symbol.isConcatSpreadable]: true
};
alert(arr.concat(arrayLike)); // 1,2,something,else
查找元素
arr.indexOf()/arr.lastIndexOf()和arr.includes()
arr.indexOf(item [,from])
——从索引from
开始搜索item
,如果找到则返回索引,否则返回-1
。arr.lastIndexOf(item [,from])
——从右向左搜索arr.includes(item [,from])
——从索引from
开始搜索item
,如果找到则返回true
,否则返回false
let arr = [1, 0, false];
alert(arr.indexOf(0)); // 1
alert(arr.indexOf(false)); // 2
alert(arr.indexOf(null)); // -1
alert(arr.includes(1)); // true
❗ 这些方法使用的是严格相等===
。所以如果我们搜索false
,会精确到的确是false
而不是数字0
。
与
indexOf/lastIndexOf
不同的是,includes
能正确处理NaN
const arr = [Nan] alert(arr.indexOf(NaN)) // -1 alert(arr.includes(NaN)) // true
arr.find()/arr.findIndex()
主要用来查找对象数组中具有特定条件的对象。 语法:
let result = arr.find(function(item, index, array){}[, thisArg]);
依次对数组中的每个元素调用该函数,如果它返回true
,则搜索停止并返回item
。如果没有搜索到,则返回undefined
。
例如,
let user = [
{ id: 1, name: "John" },
{ id: 2, name: "Tom" },
{ id: 3, name: "Jack" }
];
let users = user.find(item => item.id == 1);
alert(users.name); // John
arr.findIndex()
方法与arr.find()
方法基本是一样的,但它返回找到元素的索引,而不是元素本身。未找到任何内容时返回-1
.
let user = [
{ id: 1, name: "John" },
{ id: 2, name: "Tom" },
{ id: 3, name: "Jack" }
];
let users = user.findIndex(item => item.id == 1);
alert(users); // 0
arr.filter()
find
方法搜索的是使函数返回true
的第一个元素。
filter
方法返回所有匹配元素组成的数组。
let result = arr.filter(function(item, index, array){}[, thisArg]);
例如:
let users = [
{id:1, name:"John"},
{id:2, name:"Tom"},
{id:3, namd:"Lily"}
];
let user = users.filter(item => item.id < 3 )
alert(user);
alert(user.length); // 2
转换数组
arr.map()
arr.map
方法是最有用和经常使用的方法之一。
它对每个数组的每个元素都调用函数,并返回结果数组。
let result = arr.map(function(item, index, arr){
// 返回新值而不是当前元素
}[, thisArg]);
例如:
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
alert(lengths); // 5,6,7
arr.reverse()
arr.reverse
方法用于颠倒arr
中元素的顺序。
let arr = [1, 2, 3, 4, 5];
arr.reverse();
alert(arr); // 5,4,3,2,1
arr.reduce()/arr.reduceRight()
reduce()
方法对数组中的每个元素执行一个你提供的函数,将其结果汇总为单个返回值。
arr.reduce(callback(accumulator, item [, index [,array]]) [, initial])
callback
执行数组中每个值的函数,包含四个参数:
accumulator
——是上一个函数调用的结果,第一次等于initial
item
——当前的数组元素
index
——当前索引(可选)
array
——数组本身(可选)
initial
——作为第一次调用callback
函数时的第一个参数的值。如果没有提供初始值,则将使用数组中的第一个元素。在没有初始值的空数组上调用reduce将报错。
let arr = [1, 2, 3, 4, 5]
let result = arr.reduce((sum, item) => sum + item, 0);
alert(result); // 15
如果没有初始值,那么reduce
会将数组的第一个元素作为初始值,并从第二个元素开始迭代。
let arr = [1, 2, 3, 4, 5]
let result = arr.reduce((sum, item) => sum + item);
alert(result); // 15
arr.reduceRight
和arr.reduce
方法的功能一样,只是遍历从右到左。
数组排序sort(fn)
sort()
方法是对数组进行原位排序,更改元素的顺序,并返回数组。
arr.sort([compareFunction(firstEl, secondEl)])
compareFunction
可选
用来指定按某种顺序进行排列的函数。如果省略,元素按照字符串的各个字符的Unicode位点进行排序。
firstEl
第一个用于比较的元素。
secondEl
第二个用于比较的元素。
如果指明了compareFunction
,那么数组会按照调用该函数的返回值排序。即a
和b
是两个将要被比较的元素:
- 如果
compareFunction(a, b)
< 0 ,那么a会被排列到b之前 - 如果
compareFunction(a, b)
> 0 ,那么b会被排列在a之前。 - 如果
compareFunction(a, b)
= 0 ,a和b的相对位置不变。 compareFunction(a, b)
必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。
let arr = [1, 15, 2];
arr.sort((a, b) => a - b);
alert(arr); // 1,2,15
使用
localeCompare
比较字符串
默认情况下,它通过字母的的代码比较字母。对于许多字母,最好使用str.localeCompare方法正确地对字母进行排序。
let countries = ['Österreich', 'Andorra', 'Vietnam']; alert(countries.sort((a, b) => a > b ? 1 : -1)); // Andorra, Vietnam, Österreich(错的) alert(countries.sort((a, b) => a.localeCompare(b))); // Andorra,Österreich,Vietnam(对的!)
数组-字符串转换
arr.split()
使用指定的分隔符字符串将一个String对象分割成子字符串数组。
str.split([separator [, limit]]);
separator
指定表示每个拆分应发生的点的字符串。
limit
一个整数,限定返回的分割片段数量。
例如:
let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(", ", 2);
for(let name of arr){
alert(`${name}`); // Bilbo Gandalf
}
如果分隔符为空字符串,则将str
原字符串中每个字符的数组形式返回。
let str = "test";
alert(str.split("")); // t,e,s,t
arr.join()
将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。
arr.join([separator])
- 如果缺省参数,数组元素用
,
分隔 - 如果参数为空字符串,则所有元素之间都没有任何字符。
- 如果数组元素为
undefined
,null
,它会被转换为空字符串。
例如:
let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
let str = arr.join(";");
alert(str); // Bilbo;Gandalf;Nazgul
判断数组
Array.isArray
判断数组,返回布尔值。
Array.isArray(value);
例如:
alert(Array.isArray([])); // true
alert(Array.isArray({})); // false
数组是基于对象的,不构成单独的语言类型。 所以
typeof
不能帮助从数组中区分出普通对象。
可迭代对象(Iterable object)
可以应用for...of
的对象被称为可迭代的。
数组是可迭代的。但不仅仅是数组,很多其他内建对象也都是可迭代的,例如字符串也是可迭代的。
字符串是可迭代的
数组和字符串是使用最广泛的内建可迭代对象。
对于一个字符串,for...of
遍历他的每个字符:
for(let char of "apple"){
alert(char); // a p p l e
}
原文链接:zh.javascript.info/array zh.javascript.info/array-metho…
Symbol.iterable
从技术上来说,可迭代对象必须实现Symbol.iterable
方法。Symbol.iterable
方法会被for...of
自动调用,也可以直接调用它。
通过自己创建一个对象,我们可以轻松地掌握可迭代的概念。
let range = {
from: 1,
to: 5,
};
接下来,我们为对象添加一个名为Symbol.iterable
的方法。
let range = {
from: 1,
to: 5,
};
range[Symbol.iterator] = function () {
return {
current: this.from,
last: this.to,
next() {
if (this.current <= this.last) {
return { done: false, value: this.current++};
} else {
return { done: true };
}
}
}
}
for (let num of range) {
alert(num);
}
- 当
for...of
循环启动时,它会调用这个方法(如果没找到,就会报错)。这个方法必须返回一个迭代器(iterator) ——一个有next
方法的对象。 - 从此开始,
for...of
仅适用于这个被返回的对象。 - 当
for...of
循环希望取得下一个数值,他就调用这个对象的next()
方法。 next()
方法返回的结果的格式必须是{done: Boolean, value: any}
,当done=true
时,表示迭代结束,否则value
是下一个值。
显示调用迭代器
let str = "hello";
let iterator = str[Symbol.iterator]();
while(true){
let result = iterator.next();
if(result.done) break;
alert(result.value);
}
可迭代(Iterable)和类数组(array-like)
- Iterable,是实现了
Symbol.iterator
方法的对象。 - Array-like,是有索引和
length
属性的对象。
我们可能会遇到可迭代对象或类数组对象,或两者兼有。
⭐ 字符串是可迭代的(for...of
对他们有效),又是类数组的(它们有数值索引和length
对象)
⭐ 上面例子中的range
对象,是可迭代的,但不是类数组对象,它没有索引属性和length
属性
⭐ 下面例子中的对象是类数组的,但不是可迭代的
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
// Uncaught TypeError: arrayLike is not iterable
for(let item of arrayLike){
alert(item);
}
可迭代对象和类数组对象通常都不是数组,它们没有push
,pop
等方法。
Array.from
Array.from
方法,将可迭代对象或类数组对象转化为真正的数组,然后我们就可以对它应用数组的方法。
Array.from(obj [, mapFn, thisArg])
obj
:想要转换成数组的类数组对象或可迭代对象
mapFn
:如果指定了该参数,新数组中的每个元素会执行该回调函数
thisArg
:执行回调函数mapFn
时this
对象。
例1:
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike);
for(let item of arr){
alert(item); // Hello World
}
alert(arr.pop()); // World
例2:
接着上文举的range
的例子
let arr = Array.from(range);
alert(arr); // 1,2,3,4,5
// 数组的toString转化方法生效
例3:
接着使用range
可迭代对象
let arr = Array.from(range, num => num * num);
alert(arr); // 1,4,9,16,25
例4:
let str = '𝒳😂';
let chars = Array.from(str);
alert(chars[0]); // 𝒳
alert(chars[1]); // 😂
alert(chars.length); // 2
例5:
function slice(str, start, end){
return Array.from(str).slice(start, end).join("");
}
let str = "𝒳😂𩷶";
alert( slice(str, 1, 3) ); // 😂𩷶
// 原生方法不支持识别代理对(UTF-16 扩展字符)
alert( str.slice(1,3) ); // 乱码