概述
- 数组是值的有序集合,其中值称作‘元素’,对应位置称作‘索引’
- 数组继承Array.prototype中的属性
- 数组是对象的特殊形式,数组索引实际上和碰巧是整数的属性名差不多
创建数组
- 数组直接量
var arr = [1,2,'a'];
var arr1 = [[1,{x:1,y:2}],[2,{x:3,y:4}]];
// 可以包含对象直接量或其他数组直接量。
var arr2 = [1, , ,2, ,'a', ,0];
//稀疏数组 arr2[2] = undefined arr.length = 8
//如果省略数组直接量中的某个值,省略的元素将被赋予undefined值
var arr3 = [ , , ];
//两个元素undefined 数组直接量结尾的逗号可选择
- 调用构造函数
=1= 调用时没有参数
var a = new Array();
//相当于var a = [];
=2= 调用时有一个数值参数(一个数,整数),它指定数组长度
var a = new Array(10);
=3= 显示指定两个以上的数组元素或非数值元素
var a = new Array('test');
var a = new Array(5,4,3,2,1,'testing');
- 比较
var a = [8];
var b = new Array(8);
console.log(a);
console.log(b);
console.log(a.length);
console.log(b.length);
- 删除
=1= delete
var a = [1,'a',3, ,'b',6];
console.log(a,a.length);
delete a[1];
// 删除盒子里的东西,盒子还在
console.log(1 in a);
console.log(a,a.length);
// 从数组中删除一个元素,它就变成稀疏数组
-----------------------------------------------
=2= 新的期望数组长度
var a = [1,2,3,4,5,6,7];
console.log(a,a.length);
a.length = 4;
console.log(a,a.length);
判断数组的方法:
- instanceof
- object.prototype.toString (跨域常用)
- constructor
数组读写
var a = [1,2];
console.log(a[9]);
//溢出读没意义
var b = ['a','b'];
b[10] = 9;
console.log(b);
//可以溢出写
- 数组是特殊的对象,索引都是属性名。 数组[索引] -> 对象[属性]
- 但只有0~2e32-2之间的整数属性名才是索引。
- 所有的数组都是对象,可以为其创建任意名字的属性。
- 但如果使用的属性是数组的索引,数组的特殊行为就是将根据需要更新length属性值。
- 可以使用负数或非整数来索引数组。这时候数值转化为字符串,字符串作为属性名使用,此时当做对象的属性看待。
- 特别的,使用 非负整数的字符串,则会当做数组索引。
function roc(a,prop){
console.log(a[prop]);
console.log(a);
console.log(a.length);
}
var a = [];
a[1] = 1;
roc(a,1.000);
a[-1.23] = true;
roc(a,-1.23);
a['1000'] = 0;
roc(a,'1000');
稀疏数组
- 稀疏数组就是包含从0开始的不连续索引的数组。
a = new Array(5); // 数组没有元素,但是数组长度是5
a = []; // 创建一个空数组,数组长度是0
a[1000] = 0; // 赋值添加一个元素,但是设置长度为1001
- 当省略数组直接量中的值时(使用连续逗号),这时多得到的数组也是稀疏数组,省略掉的值是不存在的。
- 通过调用构造函数创建数组,创建的是稀疏数组,数组元素根本不存在。
var a1 = [, , , ];
var a2 = new Array(3);
var a3 = [undefined, , , ];
console.log(a1);
console.log(a2);
console.log(a3);
console.log(0 in a1);
console.log(0 in a2);
console.log(0 in a3);
数组长度
数组的特殊行为
- 当索引i大于或等于现有数组的c长度时,length属性的值将设置为i + 1;
- 设置length属性为一个小于当前长度的非负整数n时,当前数组中索引值大于或等于n的元素将从中删除。
添加和删除数组元素
- 为新索引赋值
var a = [];
a[2] = 9
push()方法(改变原数组)
从尾部加入元素
- 作用:尾部插入元素,增加数组长度
- 参数:推入的值
返回值:数组长度
组合使用push()pop()可以实现先进后出的栈
var arr = [1];
console.log(arr,arr.push(2,3,4));
// [1,2,3,4] 4
//模拟源码实现push功能
var arr = [1, 2];
Array.prototype.myPush = function() {
for (var i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i];
}
return this.length;
}
arr.myPush('a', 'b', 'c');
arr.myPush('d', 'e');
pop()方法(改变原数组)
弹出最后一个元素的值
- 作用:删除数组最后一个元素,减小数组长度
- 返回值:弹出的值
var arr = [1,2,3,4];
console.log(arr,arr.pop());
// [1,2,3] 4
//模拟源码实现pop功能
Array.prototype.myPop = function() {
var val = this[this.length - 1];
this.length -= 1;
return val;
}
arr.myPop();
var arr = [1, 2, 3, 4];
var a = arr.pop();
console.log(a, arr, arr.length);
// 使用pop方法,数组长度减少
unshift()方法 (改变原数组)
头部插入一个元素
- 参数:插入元素
返回值:数组长度
与splice()一样,参数是一次性插入的,插入顺序和在参数列表中的参数一致。
var arr = [1, 2, 3];
arr.unshift('a', 'b', 'c');
var arr = [1, 2, 3];
Array.prototype.myUnshift = function() {
var newArr = [];
for (var i = 0; i < arguments.length + this.length; i++) {
newArr[i] = i < 2 ? arguments[i] : this[i - arguments.length];
//新数组 = 新增的类数组 + 原数组
//new[i = 0] = arguments[0]
//new[i = 1] = arguments[1]
//new[i = 2] = arr[i - arguments.length]
//new[i = 3] = arr[i - arguments.length]
//i < 2 + 3
}
for (var i = 0; i = newArr.length; i++) {
this[i] = newArr[i];
}
return this.length;
}
// arr.myUnshift('a', 'b');
shift()方法 (改变原数组)
弹出头部第一个元素
- 返回值:弹出元素
var arr = ['a','b','c'];
console.log(arr);
console.log(arr,arr.shift());
//shift同样改变数组长度
splice()方法(改变原数组)
指定位置插入删除
- 作用:指定位置 插入、删除元素。
- 参数:
- 第一个参数:插入/删除起始位置
- 第二个参数:指定从起始位置删除元素的个数(0不删 | 不填全删)
- 第三个参数:新插入的元素 本身(若插入元素是数组,区别于concat(),会插入数组本身)
- 返回值:被删除的元素数组
var a = [1,2,3,4,5,6,7,8];
a.splice(4);//第二个参数没填,起始位置后全删
a.splice(1,2);//第三个参数没填,仅删除元素
a.splice(1,1);//在原数组上改变
var a = [1,2,3,4,5];
a.splice(2,0,'a','b');//不删除
a.splice(2,2,[1,2],3);//插入数组本身
数组遍历
- for
var arr = [1, 2, 3, 4, 5, 6, 7, 8];
var keys = Object.keys(arr);
var values = [];
for (var i = 0 , len = keys.length; i < len; i++) {
//优化,数组的长度只查询一次
var key = keys[i];
values[i] = arr[key];
}
console.log(values);
探究
Object.keys()方法 (传入对象、数组 返回属性名、索引)
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)。
var arr = [1, , 3, 4, , 6, 7, 'a'];
var roc = Object.keys(arr); //返回索引,是一个数组
console.log(roc);
var newArr = [];
for (var i = 0, len = roc.length; i < len; i++) {
// 此处遍历len = 6 , i = 0 ~ 5
newArr[i] = arr[i];
// nA[0]=arr[0]=1
// nA[1]=arr[1]=(empty->undefined)
// nA[2]=arr[2]=3
// why不直接将原数组的索引对应值直接赋值新数组
}
console.log(newArr);
console.log(1 in newArr);
newArr[2] = 100; // 猜测正确,空赋值给新数组,变成undefined,但此时该索引上存在元素
console.log(newArr);
console.log(arr);
console.log(1 in arr); // 原本就是空数组
var arr = [1, , 3, 4, , 6, 7, 'a'];
var roc = Object.keys(arr);
console.log(roc);
var newArr = [];
for (var i = 0, len = roc.length; i < len; i++) {
var rocs = roc[i];
newArr[i] = arr[rocs];
}
console.log(newArr);
console.log(1 in newArr);
newArr[2] = 100;
console.log(newArr);
console.log(arr);
console.log(1 in arr);
// === 过滤稀疏数组,遍历后得到 ===
forEach
- 从头至尾遍历数组,为每个数组调用指定函数
- 可以传三个参数:数组元素,索引,数组本身
- 如果只关心数组元素的值,可以只写一个参数
var data = [1, 2, 3, 4, 5];
var sum = 0;
data.forEach(function(value) {
sum += value;//将每个值累加到sum上
});
console.log(sum);
data.forEach(function(v, i, a) {
a[i] = v + 1; //每个元素的值自加1
});
console.log(data);
var a = [1, 2, 3, 4, 5, 6];
a.forEach(function(ele, index,arr) {
console.log(ele, index,arr);//输出每个元素及对应的索引
})
//模拟源码
Array.prototype.myForEach = function(func) {
for (var i = 0; i < this.length; i++) {
func(this[i], i);
}
}
arr.myForEach(function(ele, index) {
console.log(ele, index);
})
filter()方法(条件遍历)
- 根据逻辑条件返回调用数组子集,对数组元素进行筛选
- 会跳过稀疏数组中缺少的元素。它的的返回数组总是稠密的。
var a = [5, 4, 3, 2, 1];
b = a.filter(function(ele) {
return ele > 3 //筛选元素大于3的元素
});
console.log(b);
c = a.filter(function(ele, index) {
return index % 2 == 0 //筛选索引为偶数的元素
})
console.log(c);
var arr = [1, , 3, 4, , 6];
var newArr = arr.filter(function(ele, index) {
return true;//压缩稀疏数组空缺,返回稠密数组
// return false;
})
var arr1 = arr.filter(function(ele, index) {
if (ele > 3) {
return true
} else {
return false
}
})
数组排序
- sort()方法(值转换成->ASCII码 | 传参)
参数是一个比较函数
有关规则:大的、返回值1、置后 小的、返回值-1、置前
var arr = [100, 2, 0, 106, 7, 8];
arr.sort(); //(其实,在使用sort()进行排序的时候会调用toString()函数将其值转换成字符串在进行比较,是按ASCII进行比较的)
console.log(arr);
arr.sort(function(first, last) {
return first - last; //正序
// return last - first; //倒序
// return Math.random() - 0.5; //乱序
// return -1;//ASC码正序
// return 1; //ASC码倒序
});
console.log(arr);
var arr = [{name: 'tyx',search: 24005},
{name: 'hx',search: 35637},
{name: 'jj',search: 19800},
{name: 'po',search: 59043}];
arr.sort(function(a, b) {
return a.search - b.search;
})
var a = ['ant', 'Bug', 'cat', 'Dog'];
a.sort();
console.log(a);
a.sort(function(f, l) {
var a = f.toLocaleLowerCase();
var b = l.toLocaleLowerCase();
if (a < b) return -1;
if (a > b) return 1;
return 0;
})
console.log(a);
- reverse()方法(逆序排序)
var arr = [1, 2, 3, 4, 5, 6];
arr.reverse();//逆序排序
console.log(arr);
arr.sort(function(f, l) {
return f - l
});
console.log(arr);
arr.sort(function(f, l) {
return l - f
});
console.log(arr);
数组方法
改变原数组的方法:
reverse() sort()
push() pop() unshift() shift() splice()
不改变原数组的方法:
join() concat() slice() toString()
map() reduce() every() some() filter()
indexOf() forEach()
join()
将数组中所有元素连接在一起,返回生成字符串,可以指定字符分割生成字符串
var a = ['a','b','c'];
a.join();
a.join(" ");
a.join("");
var b = new Array(10);
b.join("-");
concat()
合并数组,创建一个新数组 = 原数组 + 参数
如果参数自身是数组,则连接数组中的元素
concat()不会递归扁平化数组中的数组
var a = [1,2,3];
a.concat(4,5);//a[i] + 4,5
a.concat([4,5]);//连接数组元素
a.concat([4,5],[6,7]);
a.concat(4,[5,[6.7]]);//不会递归扁平化数组
slice()(不改变原数组)
返回指定数组的一部分。
含有两个参数:开始位置 结束位置(不包含结束位置的参数)
若一个参数,表示从这个参数开始到结束
若参数为负值,表示相对于最后一个元素的位置
-1表示最后一个元素
var a = [1,2,3,4,5];
a.slice(0,3);
a.slice(3);
a.slice(1,-1);
a.slice(-3,-2);//相当于(3,4)
toString()和toLocaleString()
数组 -> 字符串
array.toString(); [return string]
- 作用:将数组的每个元素 转化为字符串
返回值:字符串
- 相当于不使用任何参数调用 join()方法
- 本地化版本,使用本地化分隔符连接
var a = [1, 2, 3, 4, 5, 'a', '8'];
console.log(a.toString());
console.log(a.join());
map()
对数组每个元素加工
array.map(function(ele){
return newArray;
})
- 作用:将每个元素传递给指定函数,经过运算或加工
- 参数:callback(currentValue) 操作函数
返回值:一个新数组
- 如果是稀疏数组,返回的也是对应的稀疏数组(相同长度和缺失元素)
var a = [1,2,3];
a.map(function(x){return x*x;}) // 1 4 9
["1","2","3"].map(parseInt); //1,NaN,NaN
通常使用parseInt时,只需要传递一个参数。但实际上,parseInt可以有两个参数,第二个参数是进制数,可以通过语句“alert(parseInt.length) === 2”来验证。
map()方法在调用callback函数时,会给它传递三个参数:当前正在遍历的元素、元素索引、原数组本身。第三个参数parseInt会忽视,但第二个参数不会,也就是说,parseInt把传过来的索引值当成进制数来使用,而parseInt的第二个参数的范围为2~36(不包含2但包含36),如果省略该参数或者其值为0,则数字将以10为基数来解析;如果小于2或者大于36,parseInt()将返回NaN,所以最终返回了[1,NaN,NaN]。
实际上,这个小例子可以分解成这样理解:
parseInt("1",0); //基数为0,以十进制来解析,返回1
parseInt("2",1) //基数为1,小于2,返回NaN
parseInt("3",2) //基数为2,小于2,返回NaN
every()和 some()
判断数组元素是否全部或部分满足一定条件
array.every(function(currentValue[元素值必须],index[可选],arr[可选]), thisValue)
array.every(function(ele){
return boolean;
});
- 作用:对数组元素逻辑判定,是否满足一定条件
- 参数:callback回调函数(数组元素值)
- 返回值:布尔值
- every()针对所有元素判定为true,函数才返回true
- some()至少含有一个元素判定为true,函数返回true
- 根据惯例,在空数组上调用,every()返回true,some()返回false
var a = [1, 2, 3, 4, 5];
a.every(function(ele) {
return ele < 10 // true
})
a.every(function(ele) {
return ele % 2 == 0 // false
})
a.some(function(ele) {
return ele % 2 == 0; //是否存在一个元素被2整除
}) // true
a.some(isNaN); // 是否存在一个元素是非数字 false
reduce()和 reduceRight()
数组前后项做运算 返回累计值
array.reduce(function(ele){
return result;
});
- 作用:让数组 前一项 与 后一项 进行 某种运算,并 累计最终值
- 两个参数 :第一个是操作函数,用某种方法把两个值化简成一个值并返回。第二个是参数是传给函数一个初始值(可选)
- 当不传第二个参数时,将使用数组的第一个元素作为初始值
返回值:操作函数执行后的累计值(一个运算结果)
- 关于操作函数的参数有两个:前一项和后一项
var a = [1, 2, 3, 4, 5];
a.reduce(function(x, y) {
return x + y;
//reduce两个参数:操作函数(两个参数:前一项;后一项);初始值
}, 0) //15
a.reduce(function(x, y) {
return x * y //数组求积
}, 1) // 120
a.reduce(function(x, y) {
return (x > y) ? x : y; //求最大值
})
//5
indexOf()和 lastIndexOf()
匹配 给定值与数组元素
Array.indexOf (search, location) [return index]
String.indexOf (search, location)
- 作用:正向或逆向 根据给出值 匹配 数组 元素
- 两个参数:搜索值,搜索起始位置 (默认从头开始 [0],负数表示相对于数组末位的偏移量)
- 返回值:第一个匹配元素的索引,或整个数组不匹配返回-1
var a = [0, 1, 2, 1, 0];
a.indexOf(1); // 正向从头搜索值1 返回值 1 表示索引
a.lastIndexOf(1); // 逆向从头搜索值1 返回值3
a.indexOf(3);// 返回值-1
'btn mbtn'.indexOf('m');
- 搜索指定的值,并返回包含所有匹配的数组索引的一个数组
function findAll(matchingArr, search) { // 匹配数组,搜索值
var resultArr = [], //创建结果数组
len = matchingArr.length, //记录数组长度
position = 0; //记录检索位置
while (position < len) { // 索引对应,开始循环遍历
position = matchingArr.indexOf(search, position);
//第一次匹配索引更新到position
if (position === -1) break;
resultArr.push(position); //将返回值压入结果数组
position = position + 1; //让索引从下一位开始,此时pos并不是pos++,而是根据上面的方法返回值索引,下一次从该值继续检索
}
return resultArr;
}
var arr = ['a', 2, 'a', 'aa', 4, 'g', 7, 4, 5, 'a', 6, 9, 'a', 6, 4, 3, 'a', '7', 'a'];
findAll(arr, 'a');
比较各个函数
判断数组类型
- Array.isArray();
console.log(Array.isArray([]));
console.log(Array.isArray({}));
数组特性
- 当有新的元素添加到列表,会自动更新length
- 设置length为一个较小值会截断数组
- 从Array.prototype中继承有用方法
- 类属性是‘Array’
类数组
- 类数组[] 当一个对象中的属性名是数字(类似数组索引),且有属性length时,作类数组。 {2:,3:,length:2} = []
var obj = {
2: 'a',
3: 'b',
push: Array.prototype.push,
splice: Array.prototype.splice
};
console.log(obj);
var obj = {
2: 'a',
3: 'b',
length: 2,
push: Array.prototype.push,
splice: Array.prototype.splice
};
console.log(obj);
- 可以利用属性名[索引]模拟数组的特性
- 可以动态的增长length属性
- 如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充
var obj = {
2: 'a',
3: 'b',
length: 2,
push: Array.prototype.push,
splice: Array.prototype.splice
};
obj.push('c'); // obj[2] = 'a' <- 'c' length++
console.log(obj);
obj.push('d'); // obj[3] = 'b' <- 'd' length++
console.log(obj);
//涉及预编译、push方法 实现源码原理
var arr = [1, 2];
Array.prototype.myPush = function() {
for (var i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i];
}
return this.length;
}
arr.myPush('a', 'b', 'c');
arr.myPush('d', 'e');
- 类数组对象没有继承自Array.prototype,而是Object.prototype那就不能在它们上面直接调用数组方法。但是可以间接的使用Function.call方法调用:
function roc(x){
console.log(x);
}
var a = {'0':'a','1':'b','2':'c',length:3};
roc(a);
var b = Array.prototype.join.call(a,'+');
roc(b);
var c = Array.prototype.splice.call(a,1,0,'d');
roc(a);
roc(c);
var d = Array.prototype.map.call(a,function(x){
return x.toUpperCase();
})
roc(d);
作为数组的字符串
- ES5中,字符串的行为类似只读数组。
- 可以用charAt()方法、[ ] 来访问单个字符
var s = test;
s.charAt(0);
s[1];
- 字符串的行为类似于数组,通用的数组方法可以应用到字符串上
- 注意,字符串是不可变值,当作数组时它们是只读的。
- push,sort,reverse,splice修改字符串无效,导致错误,却没有提示
s = 'JavaScript';
Array.prototype.join.call(s,' ');
Array.prototype.filter.call(s,function(x){
return x.match(/[^aeiou]/); //只匹配非元音字母
}).join(' ');
数组去重
var arr = [10, 8, 9, 2, 8, 9, 10, 1, 2, 1, 2, 3, 9, 9, 10, 3, 5, 5];
Array.prototype.unique = function() {
var memory = {};
var newArr = [];
for (var i = 0; i < this.length; i++) {
if (!memory[this[i]]) {
newArr.push(this[i]);
memory[this[i]] = true;
}
}
return newArr;
}
var newArr = arr.unique();