数组
存储有序的集合:数组又叫 双端队列(deque),支持队列+堆的功能
声明
let arr = new Array();
let arr = [];
数组可以存储任何类型的元素。
// 混合值
let arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ];
// 获取索引为 1 的对象然后显示它的 name
alert( arr[1].name ); // John
// 获取索引为 3 的函数并执行
arr[3](); // hello
队列(queue)
push在末端添加一个元素.shift取出队列首端的一个元素,整个队列往前移,这样原先排第二的元素现在排在了第一。
栈
push在末端添加一个元素.pop从末端取出一个元素.
shift
取出数组的第一个元素并返回它:
let fruits = ["Apple", "Orange", "Pear"];
alert( fruits.shift() ); // 移除 Apple 然后 alert 显示出来
alert( fruits ); // Orange, Pear
unshift
在数组的首端添加元素:
let fruits = ["Orange", "Pear"];
fruits.unshift('Apple');
alert( fruits ); // Apple, Orange, Pear
push 和 unshift 方法都可以一次添加多个元素:
let fruits = ["Apple"];
fruits.push("Orange", "Peach");
fruits.unshift("Pineapple", "Lemon");
// ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]
alert( fruits );
数组是对象
arr[0]实际上是来自于对象的语法,与obj[key]相同,arr是对象,而数字用作键(key)- 数组可以动态添加属性,但是会失去原来针对有序集合的一些优化。变成普通对象处理
let fruits = []; // 创建一个数组
fruits[99999] = 5; // 分配索引远大于数组长度的属性
fruits.age = 25; // 创建一个具有任意名称的属性
性能
push/pop效率更高,shift/unshift 慢
循环
for..of 只循环值
let fruits = ["Apple", "Orange", "Plum"];
// 遍历数组元素
for (let fruit of fruits) {
alert( fruit );
}
for..in 处理正常数组信息,还会遍历出其他信息
length
不是数组里元素的个数,而是最大的数字索引值加一
let fruits = [];
fruits[123] = "Apple";
alert( fruits.length ); // 124
设置length 会改变总长度,而且不可逆
let arr = [1, 2, 3, 4, 5];
arr.length = 2; // 截断到只剩 2 个元素
alert( arr ); // [1, 2]
arr.length = 5; // 又把 length 加回来
alert( arr[3] ); // undefined:被截断的那些数值并没有回来
new Array()
创建一个 指定了长度,却没有任何项 的数组,默认都是 undefined
let arr = new Array(2); // 会创建一个 [2] 的数组吗?
alert( arr[0] ); // undefined!没有元素。
alert( arr.length ); // length 2
toString
let arr = [1, 2, 3];
alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true
- 数组没有
Symbol.toPrimitive,也没有valueOf,它们只能执行toString进行转换. - 所以这里
[]就变成了一个空字符串,[1]变成了"1",[1,2]变成了"1,2"
alert( [] + 1 ); // "1"
alert( [1] + 1 ); // "11"
alert( [1,2] + 1 ); // "1,21"
比较数组
- 仅当两个对象引用的是同一个对象时,它们才相等
==。 - 如果
==左右两个参数之中有一个参数是对象,另一个参数是原始类型,那么该对象将会被转换为原始类型,转换规则如 对象 — 原始值转换 一章所述。 - ……
null和undefined相等==,且各自不等于任何其他的值。 - 严格比较
===更简单,因为它不会进行类型转换。
数组是不同的对象。所以它们不相等
alert( [] == [] ); // false
alert( [0] == [0] ); // false
- 原始类型和数组对象进行比较。数组
[]被转换为原始类型以进行比较,被转换成了一个空字符串''。 - 接下来的比较就是原始类型之间的比较.
alert( 0 == [] ); // true
alert('0' == [] ); // false
// 上面等价于 在 [] 被转换为 '' 后
//上面等价于
alert( 0 == '' ); // true,因为 '' 被转换成了数字 0
alert('0' == '' ); // false,没有进一步的类型转换,是不同的字符串
总结
数组是一种特殊的对象,适用于存储和管理有序的数据项。
- 声明:
// 方括号 (常见用法)
let arr = [item1, item2...];
// new Array (极其少见)
let arr = new Array(item1, item2...);
调用 new Array(number) 会创建一个给定长度的数组,但不含有任何项。
length属性是数组的长度,准确地说,它是数组最后一个数字索引值加一。它由数组方法自动调整。- 如果我们手动缩短
length,那么数组就会被截断。 我们可以通过下列操作以双端队列的方式使用数组: push(...items)在末端添加items项。pop()从末端移除并返回该元素。shift()从首端移除并返回该元素。unshift(...items)从首端添加items项。 遍历数组的元素:for (let i=0; i<arr.length; i++)— 运行得最快,可兼容旧版本浏览器。for (let item of arr)— 现代语法,只能访问 items。for (let i in arr)— 永远不要用这个。
比较数组时,不要使用 == 运算符(当然也不要使用 > 和 < 等运算符),因为它们不会对数组进行特殊处理。它们通常会像处理任意对象那样处理数组,这通常不是我们想要的。
但是,我们可以使用 for..of 循环来逐项比较数组。
问题:输入数字求和
- 使用 prompt 向用户索要值,并存在数组中。
- 当用户输入了非数字、空字符串或者点击“取消”按钮的时候,问询结束。计算并返回数组所有项之和。
function sumInput() {
let totalArr = []
let bol = true
while(true){
let input = prompt("请输入数字")
let bol = ( input === '' || input === null || !isFinite(input) )
if(bol) {
break
}
totalArr.push(+input)
}
let total = 0
for val of totalArr{
total += val
}
return total
}
sumInput()
问题:求数组的连续子数组,返回最大和
找出所有项的和最大的 arr 数组的连续子数组,返回最大和
alert( getMaxSubSum([-1, 2, 3, -9,11]) ); // 5
alert( getMaxSubSum([-1, 2, 3, -9, 11]) ); // 11
alert( getMaxSubSum([-2, -1, 1, 2]) ); // 3
alert( getMaxSubSum([1, 2, 3]) ); // 6
//方案1 双循环
let a = [-1, 2, 3, -9, 11]
function getMaxSubSum(a) {
// let a = [-1, 2, 3, -9, 11]
let maxSum = 0
let maxI = 0
for (let i = 0; i < a.length; i++) {
let nowArrSum = 0
for(let j = i; j < a.length; j++) {
nowArrSum += a[j]
maxSum = Math.max(maxSum,nowArrSum)
}
}
console.log("maxSum",maxSum)
return maxSum
}
getMaxSubSum(a)
//优化方案
let a = [-1, 2, 3, -9,11]
getMaxSubSumFirst(a)
function getMaxSubSumFirst(arr) {
let sumMax = 0
let tempTotal = 0
for (let i = 0; i <arr.length; i++) {
let temp = arr[i]
if (temp < 0) {
tempTotal = 0
} else {
tempTotal += temp
sumMax = Math.max(sumMax,tempTotal)
}
}
console.log("getMaxSubSumFirst",sumMax)
return sumMax
}
数组方法
添加删除
splice
改变原有数组
let arr = ["I", "go", "home"];
delete arr[1]; //变成 ["I", undefined, "home"]; 依然占用位置,长度3
arr.splice(start[, deleteCount, elemInsert1, ..., elemInsertN])
//只删除
let arr = ["I", "study", "JavaScript"];
arr.splice(1, 1); // 从索引 1 开始删除 1 个元素 结果为 ["I", "JavaScript"];长度2
//只新增
let arr = ["I", "study", "JavaScript"];
arr.splice(2, 0, "complex", "language");// 从索引 2 开始 删除 0 个元素,然后插入 "complex" 和 "language"
alert( arr ); //结果 ["I", "study", "complex", "language", "JavaScript"]
允许负向索引,注意负不从0开始,从-1开始
let arr = [1, 2, 5];
arr.splice(-1, 0, 3, 4);// 从索引 -1(尾端前一位) ,删除 0 个元素,然后插入 3 和 4
alert( arr ); // [1,2,3,4,5]
slice
它会返回一个新数组,原数组不变化。将所有从索引 start 到 end(不包括 end)的数组项复制到一个新的数组。start 和 end 都可以是负数,在这种情况下,从末尾计算索引。
arr.slice([start], [end])
let arr = ["t", "e", "s", "t"];
alert( arr.slice(1, 3) ); // e,s(复制从位置 1 到位置 3 的元素)
alert( arr.slice(-2) ); // s,t(复制从位置 -2 到尾端的元素)
concat
arr.concat(arg1, arg2...) 创建一个新数组,拼接内容arg1可以是数组[]或者值val
let arr = [1, 2];
alert( arr.concat([3, 4]) ); // 1,2,3,4
alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6
它只复制数组中的元素。其他对象整体添加
let arr = [1, 2];
let arrayLike = {
0: "something",
length: 1
};
alert( arr.concat(arrayLike) ); // 1,2,[object Object]
特殊情况:Symbol.isConcatSpreadable 属性
let arr = [1, 2];
let arrayLike = {
0: "something",
1: "else",
[Symbol.isConcatSpreadable]: true,
length: 2
};
alert( arr.concat(arrayLike) ); // 1,2,something,else
搜索元素/遍历
forEach
方法允许为数组的每个元素都运行一个函数
arr.forEach(function(item, index, array) {
// ... do something with item
});
indexOf/lastIndexOf 和 includes
等同于string字符串的操作
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
//带有from的查找
let arr = [1, 0, false, 0, false];
alert( arr.indexOf(0,2) ); // 3 如果从第三个位置开始查找,则是第二个0,位置为3
不严格判断使用 includes
const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1(应该为 0,但是严格相等 === equality 对 NaN 无效)
alert( arr.includes(NaN) );// true(这个结果是对的)
find 和 findIndex
let result = arr.find(function(item, index, array) {
// 如果返回 true,则返回 item 并停止迭代
// 对于假值(falsy)的情况,则返回 undefined
});
filter
find是匹配一个,filter匹配多个,返回数组
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
let someUsers = users.filter(item => item.id < 3);
alert(someUsers); // [ {id: 1, name: "John"}, {id: 2, name: "Pete"},]
转换数组
map
它对数组的每个元素都调用函数,并返回结果数组。
let result = arr.map(function(item, index, array) {
// 返回新值而不是当前元素
})
sort()
排序,改变原有数组
let arr = [ 1, 2, 15 ];
// 该方法重新排列 arr 的内容
arr.sort(); //这里默认会转化所有数据为字符串,默认按第一位在unicode编码表的顺序比较
alert( arr ); // 1, 15, 2 所以排序变成 第一位优先比较
自定义排序方法sort(fn)
function compareNumeric(a, b) {
if (a > b) return 1; // 如果第一个值比第二个值大
if (a == b) return 0; // 如果两个值相等
if (a < b) return -1; // 如果第一个值比第二个值小
}
let arr = [ 1, 2, 15 ];
arr.sort(compareNumeric);
alert(arr); // 1, 2, 15
//优化1 比较函数只需要返回一个正数表示“大于”,一个负数表示“小于”。
let arr = [ 1, 2, 15 ];
arr.sort(function(a, b) { return a - b; });
alert(arr); // 1, 2, 15
//优化2
arr.sort( (a, b) => a - b );
语言的比较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
reverse 倒叙
let arr = [1, 2, 3, 4, 5];
arr.reverse();
alert( arr ); // 5,4,3,2,1
split 拆分
let names = 'Bilbo, Gandalf, Nazgul';
let arr = names.split(', ');
for (let name of arr) {
alert( `A message to ${name}.` ); // A message to Bilbo(和其他名字)
}
//长度限制,2之后的不处理
let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);
alert(arr); // Bilbo, Gandalf
//参数为空字符串,为单字拆解
let str = "test";
alert( str.split('') ); // t,e,s,t
//参数为undefined 或不输入,内容不变
let str = "test";
alert( str.split('') ); // test
join 粘合
let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
let str = arr.join(';'); // 使用分号 ; 将数组粘合成字符串
alert( str ); // Bilbo;Gandalf;Nazgul
reduce/reduceRight
根据数组计算单个值。
let value = arr.reduce(function(accumulator, item, index, array) {
// ...
}, [initial]);
该函数一个接一个地应用于所有数组元素,并将其结果“搬运(carry on)”到下一个调用。
accumulator—— 是上一个函数调用的结果,第一次等于initial(如果提供了initial的话)。item—— 当前的数组元素。index—— 当前索引。arr—— 数组本身。- initial 为初始传入值:
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
alert(result); // 15
//可以省略初始值0 //如果没有初始值,那么 reduce 会将数组的第一个元素作为初始值,并从第二个元素开始迭代。
let result = arr.reduce((sum, current) => sum + current);
reduceRight 是从右边到左边遍历
Array.isArray
alert(Array.isArray({})); // false
alert(Array.isArray([])); // true
通用参数thisArg
find,filter,map,除了 sort 是一个特例,都有附加参数 thisArg。
arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// thisArg 是可选的最后一个参数
我们使用 army 对象方法作为过滤器,thisArg 用于传递上下文(passes the context):
let army = {
minAge: 18,
maxAge: 27,
canJoin(user) {
return user.age >= this.minAge && user.age < this.maxAge;
}
};
let users = [
{age: 16},
{age: 20},
{age: 23},
{age: 30}
];
// 找到 army.canJoin 返回 true 的 user,这里使用的是army的上下文
let soldiers = users.filter(army.canJoin, army);
alert(soldiers);//结果 [ {age: 20},{age: 23}]
练习
//题目1 实现如下效果
camelize("list-style-image") == 'listStyleImage';
camelize("-webkit-transition") == 'WebkitTransition';
function camelize(str) {
return str.split('-').map((item,index)=> {
return index === 0 ? item:item[0].toUpperCase() + item.slice(1)
}).join("")
}
//题目2 实现如下效果
let arr = [5, 3, 8, 1];
filterRangeInPlace(arr, 1, 4); // 删除了范围在 1 到 4 之外的所有值
alert( arr ); // [3, 1]
function filterRangeInPlace(arr,start,end) {
for( let i = arr.length-1; i>=0; i--){
if ( arr[i] >= start && arr[i] <= end ){
}else {
arr.splice(i,1)
}
}
}
//题目3 实现如下效果
let arr = ["HTML", "JavaScript", "CSS"];
let sorted = copySorted(arr);
alert( sorted ); // CSS, HTML, JavaScript
alert( arr ); // HTML, JavaScript, CSS (no changes)
//方法1
function copySorted(arr) {
copy = arr.map((item)=>item)
return ["HTML", "JavaScript", "CSS"].sort( (a,b) => a > b?1:-1)
}
//方法2
function copySorted(arr) {
return arr.slice().sort()
}
//题目4 实现如下效果
let calc = new Calculator;
alert( calc.calculate("3 + 7") ); // 10
calc.addMethod("*", (a, b) => a * b);
calc.addMethod("/", (a, b) => a / b);
calc.addMethod("**", (a, b) => a ** b);
let result = calc.calculate("2 ** 3");
alert( result ); // 8
//方法1 方法定义
function Calculator() {
this.methods = {
"-": (a, b) => a - b,
"+": (a, b) => a + b
};
this.calculate = function(str) {
let split = str.split(' '),
a = +split[0],
op = split[1],
b = +split[2];
if (!this.methods[op] || isNaN(a) || isNaN(b)) {
return NaN;
}
return this.methods[op](a, b);
};
this.addMethod = function(name, func) {
this.methods[name] = func;
};
}
//方法2 类定义
class Calculator {
constructor(){
this.symbolObj = {"+":(a, b) => a + b}
}
calculate(params) {
let [num1,symbol,num2] = params.split(" ")
if(this.symbolObj[symbol]) {
return this.symbolObj[symbol](Number(num1),Number(num2))
}
}
addMethod(symbol,fn) {
this.symbolObj[symbol] = fn
}
}
//题目4 随机排列数组
//编写函数 shuffle(array) 来随机排列数组的元素。
//多次运行 shuffle 可能导致元素顺序的不同。例如:
let arr = [1, 2, 3];
shuffle(arr);// arr = [3, 2, 1]
shuffle(arr);// arr = [2, 1, 3]
shuffle(arr);// arr = [3, 1, 2]
//方法1
function shuffle(arr) {
arr.sort((a,b) => Math.random() - 0.5)
}
//方法2 Fisher — Yates 算法 /高纳德( Knuth)随机置乱算法
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1)); // 从 0 到 i 的随机索引
// 交换元素 array[i] 和 array[j]
// 我们使用“解构分配(destructuring assignment)”语法来实现它
// let t = array[i]; array[i] = array[j]; array[j] = t
[array[i], array[j]] = [array[j], array[i]];
}
}
// 所有可能排列的出现次数
let count = {
'123': 0,
'132': 0,
'213': 0,
'231': 0,
'321': 0,
'312': 0
};
for (let i = 0; i < 1000000; i++) {
let array = [1, 2, 3];
shuffle(array);
count[array.join('')]++;
}
// 显示所有可能排列的出现次数
for (let key in count) {
alert(`${key}: ${count[key]}`);
}
//题目5 实现数组去重
let strings = ["Hare", "Krishna", "Hare", "Krishna",
"Krishna", "Krishna", "Hare", "Hare", ":-O"
];
alert( unique(strings) ); // Hare, Krishna, :-O
//方法1
function unique(arr) {
let resList = []
for (item of arr) {
if (resList.indexOf(item) < 0) {
resList.push(item)
}
}
return resList
}
//方法2
function unique(arr) {
let set = new Set(arr)
return Array.from(set)
}
//方法3
function unique(arr) {
arr.sort()
for (let index = arr.length -1; index >0; index--) {
const element = arr[index];
if(arr[index] === arr[index - 1]){
arr.splice(index,1)
}
}
return arr.reverse()
}
//题目6 从数组创建键(值)对象
let users = [
{id: 'john', name: "John Smith", age: 20},
{id: 'ann', name: "Ann Smith", age: 24},
{id: 'pete', name: "Pete Peterson", age: 31},
];
let usersById = groupById(users);
/*
输出
usersById = {
john: {id: 'john', name: "John Smith", age: 20},
ann: {id: 'ann', name: "Ann Smith", age: 24},
pete: {id: 'pete', name: "Pete Peterson", age: 31},
}
*/
//方法1
function groupById(users) {
const obj = {}
for( item of users) {
obj[item.id] = item
}
return obj
}
//方法2
function groupById(users) {
return users.reduce((preObj,obj)=>{
preObj[obj.id] = obj
return preObj
},{})
}