# 深入 JavaScript 数组相关方法 和 API
本章节
我们来学习数组相关的方法,操作数组的方法非常多,我们先学习 ES5 中的一些方法,等学习到 ES6 之后,再学习 ES6 中新增的相关方法。
数组的操作非常重要,我们后期的项目开发或面试中,对于数组的操作那是家常便饭,所以一定要掌握
# 一、数组的头尾操作
首先我们来学习,如何在数组的头部或尾部添加元素或删除元素的操作
实例方法 | 描述 | 是否更改原数组 |
---|---|---|
push() | 将一个或多个元素添加到数组的末尾,并返回该数组的新长度 | 是 |
pop() | 从数组中删除最后一个元素,并返回被删除元素的值 | 是 |
unshift() | 将一个或多个元素添加到数组的头部,并返回该数组的新长度 | 是 |
shift() | 从数组中删除第一个元素,并返回该元素的值 | 是 |
# 1、push()方法
TIP
- 向数组的末尾添加一个或多个元素,每个元素之间用
,
逗号隔开 - 并返回数组的新长度
var arr = [1, 2, 3];
// 向数组的尾部添加元素4
arr.push(4);
// 向数组的尾部添加5,'我' ,同时用变量len接受数据的新长度
var len = arr.push(5, "我");
console.log(arr);
console.log("数组的长度:" + len); // 数组的长度:6
push
方法根据length
属性来决定从哪里开始插入给定的值
# 2、pop() 方法
TIP
- 从数组中删除最后一个元素,并返回该元素(被删除元素)的值
- 当数组为空时,返回值为
undefined
var arr = [12, 23, 56, 11];
var item = arr.pop();
console.log(arr); // 输出删除最后一项后的值 [12, 23, 56]
console.log(item); // 返回被删除元素的值 11
pop
方法根据length
属性来确定最后一个元素的位置
# 3、unshift() 方法
TIP
- 一个或多个元素添加到数组的开头,每个元素之间用
,
逗号隔开 - 并返回该数组的新长度
var arr = [12, 23, 56, 11];
arr.unshift(66);
var len = arr.unshift(0);
console.log(len); // 6
console.log(arr); // [0, 66, 12, 23, 56, 11]
# 4、shift() 方法
TIP
- 从数组中删除第一个元素,并返回该元素的值
- 当数组为空时,返回值为
undefined
var arr = [12, 23, 56, 11];
var item = arr.shift(); // 删除数组中下标为0的项
console.log(item); // 返回被删除的项 12
console.log(arr); // [23, 56, 11]
# 5、案例:筛选数组中所有大于等于 10 的元素
筛选数组中所有大于等于 10 的元素
var arr = [1, 3, 68, 33, 5, 9, 34, 55];
function filter(arr) {
var arr2 = [];
for (var i = 0; i <= arr.length; i++) {
if (arr[i] >= 10) {
arr2.push(arr[i]);
}
}
return arr2;
}
console.log(filter(arr));
# 二、数组中元素的(增、删、改、查)
如果我们要对数组中非头尾的元素,做相关的增、删、改、查操作,就需要用到下面的这些方法
实例方法 | 描述 | 是否更改原数组 |
---|---|---|
slice() | (查询):提取原数组的一部分并返回一个新数组 | 否 |
splice() | (新增):在指定位置插入一项或多项新元素 (删除):在指定位置删除指定项 (更新):在指定位置删除指定项,同时在此处添加一项或多项 | 是 |
# 1、slice()方法
TIP
- 查询原数组中的一部分元素,然后将查询到的元素复制到一个新数组,并将新数组返回
- slice() 方法不会更改原数组
- slice() 中返回的元素是从原数组中浅拷贝过来的。
浅拷贝
- 数组中的值如果是基本类型,直接把值拷贝一份过来
- 如果是引用类型,拷贝的是引用类型的地址,当修改 slice()返回数组中的引用类型值时,原数组也会变
语法:
slice(start, end);
- start 表示查找的开始索引,end 表示查找的结束索引
- start 和 end 可正可负,正数表示从左往右数,下标从 0 开始,负数表示从右往左数,下标从-1 开始
- 查找结果包含 start 部分,不包含 end 部分
- 如果只有一个参数,如 slice(2),表示从下标 2 开始,提取后续所有项元素
- 如果参数只一个,并且是负数,如:slice(-2) 表示提取数组最后 2 个元素
# 1.1、两个参数都为正
TIP
两个参数都为正,如 slice(2,3)
表示从下标 2 开始,提到到下标 3 中间所有元素,不包括下标 3 的元素
var arr = [1, 2, 3, 4, 5, 6];
var arr2 = arr.slice(1, 4); // 从原数组下标 1查找到下标4,将查找到元素作为一新数组返回
console.log(arr); // [1, 2, 3, 4, 5, 6] 并不会改变原数组
console.log(arr2); // [2, 3, 4]
# 1.2、只有一个参数,参数为正
TIP
如果只有一个参数,如 slice(2)
表示从下标 2 开始,提取后续所有项元素
var arr = [1, 2, 3, 4, 5, 6];
var arr1 = arr.slice(2); // 从下标2开始,提取后续所有项元素
console.log(arr1); // [3, 4, 5, 6]
# 1.3、只有一个参数,参数为负
TIP
如果参数只一个,并且是负数,如:slice(-2)
表示提取数组最后 2 个元素
var arr = [1, 2, 3, 4, 5, 6];
var arr1 = arr.slice(-2); // 提取最后2个元素
console.log(arr1); // [5, 6]
# 1.4、两个参数都为负数
TIP
- 如果两个参数都为负数,如:
slice(-3,-2)
表示提取原数组中的倒数第 3 个元素到倒数第二个元素(不包含第二个元素) slice(-2,0)
查询结果为空数组
var arr = [1, 2, 3, 4, 5, 6];
var arr1 = arr.slice(-3, -2); // 提取倒数第3个元素到倒数第2个元素,不包含倒数第二个元素
var arr2 = arr.slice(-3, 0);
console.log(arr1); // [4]
console.log(arr2); // []
# 1.5、slice 提取原数组中数据,是以浅拷贝方式复制过来
var arr = [1, ["A", "B"], 2, 3];
var arr2 = arr.slice(1, 3);
arr2[0][1] = "更"; // 更改arr2中第1项的内容
// 更改arr2中的内容,原数组arr中内容也发生了改变
console.log(arr); // [1, ["A", "更"], 2, 3]
console.log(arr2); // [["A", "更"],2]
记忆准则 slice(start,end) :
- start 和 end 表示查找的起始与结束索引,其值可正可负,正索引是从左往右数,下标从 0 开始,负索引是从右往左数,下标从-1 开始
- 元素查找方向,都是从起始索引往右查找
- 查找的元素,只包含第一个参数索引值,不包含第二个参数索引
- 如果只有一个参数,表示起始位置,从这个位置提取往后所有元素。
- start 的默认值可以理解为 0,end 的默认值可以理解为数组长度-1
arr.slice()
相当于把整个数组浅拷贝一份
# 2、splice()方法
TIP
- splice()方法,可以实现对数组中元素的 删除,添加,更新(删除并替换)操作
- 被删除的元素组成一个数组,被返回,如果没有删除元素,则返回空数组
语法
splice(index, deleteCount, item1, item2, itemN);
index 表示修改的开始位置(下标)
- 如果超过数组长度,表示从数组尾部开始添加内容
- 如果是负值,表示数组尾部开始的第几位(必部第一位表示-1)
deleteCount 表示要移除的数组中元素的个数
如果 deleteCount 是 0 或负数,表示不移除元素
- item1,item2...可选,表示要添加进数组的元素,从 index 位置开始
# 2.1、添加元素
var arr = [1, 2, 3, 4, 5];
// 从下标1位置前面插入两个数
var arr1 = arr.splice(1, 0, "A", "B");
console.log(arr); // [1, 'A', 'B', 2, 3, 4, 5]
console.log(arr1); // []
# 2.2、更新(删除并替换)元素
var arr = [1, 2, 3, 4, 5];
// 从下标1的位置,删除2个元素(包括下标1的元素),然后再加两个元素
var arr1 = arr.splice(1, 2, "A", "B");
console.log(arr); // [1, 'A', 'B', 4, 5]
console.log(arr1); // [2, 3]
# 2.3、删除元素
var arr = [1, 2, 3, 4, 5];
var arr1 = arr.splice(1, 2);
console.log(arr); // [1, 4, 5]
console.log(arr1); // [2, 3]
var arr = [1, 2, 3, 4, 5];
var arr1 = arr.splice(2); // 删除索引2后所有元素,包括索引2位置上元素
console.log(arr); // [1, 2]
console.log(arr1); // [3, 4, 5]
# 2.4、index 下标为负数时
- index 为负数,表示从后往前找元素的位置,从后往前,元素下标从-1 开始
var arr = [1, 2, 3, 4, 5];
// 从倒数第三个元素开始,删除 2个元素(包括倒数第三个)
var arr1 = arr.splice(-3, 2);
console.log(arr); // [1, 2, 5]
console.log(arr1); // [3, 4]
记忆准则:
- index 表索引,索引可正可负,正表示从左往右数,下标从 0 开始,负表示从右往左数,下标从-1 开始
- deleteCount 表示删除长度,0 和负数不删除,如果不写,表示 index 处删除往后所有元素(包括 index 处元素)
- splice 删除元素,包含 index 索引位置元素
# 三、数组与字符串相互转换
实例方法 | 描述 | 是否更改原数组 |
---|---|---|
join() | 将一个数组的所有元素以特定的某种字符连接成一个字符串 并返回这个字符串 | 不会 |
toString() | 将数组转换为字符串,然返回该 字符串 | 不会 |
以下方法为字符串的实例方法
实例方法 | 描述 |
---|---|
split() | 根据指定的分隔符将一个字符串分割成一个字符串数组 |
charAt() | 从一个字符串中返回指定的字符 str.charAt(index) |
# 1、join()方法
TIP
- 将一个数组的所有元素以特定的某种字符连接成一个字符串,并返回这个字符串
- 如果不指定连接字符,则默认以
,
逗号来分隔连接 - 这个方法不会改变原数组,会生成一个新的字符串
var arr = ["a", "b", "c", "d"];
var str = arr.join();
console.log(str); // a,b,c,d
console.log(arr); // ['a','b','c','d']
var str2 = arr.join("");
console.log(str2); // abcd
var str3 = arr.join("**");
console.log(str3); // abcd a**b**c**d
# 2、toString()方法
- 将数组转成以
,
逗号分隔的字符串,同时返回该字符串
var arr = [1, 2, 3];
var str = arr.toString();
console.log(str);
toString
方法在内部调用join()
方法拼接数组中的元素并返回一个字符串,其中包含用逗号分隔的每个数组元素
var arr = [1, 2, 3, 4];
var str = arr.toString();
console.log(str);
// 修改原型上join方法
Array.prototype.join = function () {
var str = "";
for (var i = 0; i < this.length; i++) {
str += this[i] + "*";
}
return str;
};
// 也可以只修改当前实例的join方法 arr.join=function(){ }
console.log(arr.toString());
# 3、split()方法 字符串操作方法
TIP
- 数组的
join()
方法可以使数组转为字符串 - 字符串的
split()
方法可以使字符串转为数组
split()
可以使字符串以某种分隔符进行分隔,分隔后的字符串组成一个数组返回- 分隔符为空字符串,则将原字符串中每个字符的数组形式返回
var str = "a*b*c*d";
var arr1 = str.split("");
var arr2 = str.split("*");
console.log(arr1); // ['a', '*', 'b', '*', 'c', '*', 'd']
console.log(arr2); // ['a', 'b', 'c', 'd']
"a-b-c-d-e-f-g-h".split(""); // ['a', '-', 'b', '-', 'c', '-', 'd', '-', 'e', '-', 'f', '-', 'g', '-', 'h']
"a-b-c-d-e-f-g-h".split("-"); // ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
- 回顾之前讲的
rgb(2,33,55)
颜色的分隔
var rgb = "rgb(2,33,55)";
// \d 表示 0-9之间的任意数字 [^\d] 排除0-9之间的任意数字 +表示前面的字符可以出现任意次
// 只要不是[0-9]之间的单个或多个数字,其它字符都可以做为分隔符来分隔字符串
var rgbArr = rgb.split(/[^\d]+/);
console.log(rgbArr);
# 4、实战应用:获取 url 地址中的参数
var url = "https://www.icodingedu.com/goods/show/42?targetId=71&preview=0";
var obj = {};
var str = url.split("?")[1].split("&");
console.log(str); // ['targetId=71', 'preview=0']
for (let i = 0; i < str.length; i++) {
var param = str[i].split("=");
obj[param[0]] = param[1];
}
console.log(obj); // {targetId: '71', preview: '0'}
# 5、charAt()方法 字符串操作方法
TIP
- charAt() 方法从一个字符串中返回指定的字符
str.charAt(index);
- 一个介于 0 和字符串长度减 1 之间的整数
- 如果没有提供索引,charAt() 将使用 0
- 字符串中的字符从左向右索引,第一个字符的索引值为 0,最后一个字符(假设该字符位于字符串 str 中)的索引值为
str.length - 1
var str = "我爱你!";
console.log(str.charAt(0)); // 我
console.log(str.charAt(1)); // 爱
console.log(str.charAt(2)); // 你
# 6、字符串与数组相似点
TIP
- 字符串也可以像数组一样,使用下标进行访问
- 也可以用 charAt()方法来访问,charAt()
var str = "我爱你!";
console.log(str[0]); // 我
console.log(str[1]); // 我
console.log(str[2]);
- 字符串可以像数组一样,利用 for 循环来遍历
var str = "我爱你!";
for (var i = 0; i < str.length; i++) {
console.log(str[i]);
}
# 四、判断数组中元素方法
实例方法 | 描述 | 是否更改原数组 |
---|---|---|
indexOf() | 返回在数组中可以找到给定元素的第一个索引,如果不存在,则返回 -1。 | 否 |
lastIndexOf() | 返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1 从指定位置开始逆向查找(即从后往前找) | 否 |
includes() | 法用来判断一个数组是否包含一个指定的值, 根据情况,如果包含则返回 true ,否则返回 false | 否 |
# 1、indexOf() 方法
TIP
- 返回在数组中可以找到给定元素的第一个索引,如果不存在,则返回 -1。
- 查找时,使用的是(全等运算
===
)
语法
indexOf(searchElement, fromIndex);
- searchElement 要查找的元素
- fromIndex 开始查找的位置(索引)
- 如果
fromIndex的值 >= 数组长度
,意味着不会在数组中查找,返回-1 - 如果 fromIndex 是一个负数,如
indexOf('a',-2);
表示从倒数第二个元素开始查找,向后查找到数组尾部
- 如果
["A", "B", "C", "D"]
.indexOf("D") // 3 返回D的下标为3
[("A", "B", "C", "D")].indexOf("F") // -1 F不存在返回-1
[("A", "B", "C", "D", "B", "B")].indexOf("B"); // 1 B存在多次,则返回第一次出现位置的下标
var arr = [2, 9, 1, 1, 5];
arr.indexOf(2); // 0
arr.indexOf(1, 3); // 3
arr.indexOf(1, -3); // 2
arr.indexOf(1, -1); // -1
# 2、lastIndexOf()
TIP
- 返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1
- 内容判断使用的 全等
- 从指定位置开始逆向查找(即从后往前找)
语法
lastIndexOf(searchElement);
lastIndexOf(searchElement, fromIndex);
- searchElement 被查找的元素
- fromIndex 查找的的开始下标,默认值为 arr.length-1,(即从后向前查找整个数组)
- 如果 fromIndex 不写、或大于等于数组长度,则整个数组会被查找
- 如果 fromIndex 为负值,从指定位置向前查找
- 如果 fromIndex 负值的绝对值大于数组长度,则数组不会被查找,返回-1
var arr = [1, 3, 4, 5, 3, 8, 1];
var _index = arr.lastIndexOf(3);
console.log(_index); // 4
var _index = arr.lastIndexOf(1);
console.log(_index); // 6
var arr = [1, 3, 4, 5, 3, 8, 1];
var _index = arr.lastIndexOf(1, -1);
console.log(_index); // 6
var _index = arr.lastIndexOf(1, -2);
console.log(_index); // 0
var _index = arr.lastIndexOf(1, -8);
console.log(_index); // -1
# 3、includes() 方法
TIP
- 法用来判断一个数组是否包含一个指定的值
- 根据情况,如果包含则返回
true
,否则返回false
- 查找时,使用的是(全等运算
===
)
语法
includes(searchElement, fromIndex);
- searchElement 需要查找的元素
- fromIndex 表示查找的开始索引。
- 如果
fromIndex超过 >= 数组长度
,直接返回 false - 如果 fromIndex 为负数,从负的索引处一直查找到数组的尾部
- 如果
[11, 22, 33]
.includes("22") // false
[(1, 2, 3, 4)].includes(3); // true
[1, 2, 3, 4].includes(2, -2); // false
[1, 2, 3, 4].includes(4, -2); // true
# 4、应用案例:数组去重
function unique(arr) {
// 如果传入的类型不是数组,啥也不做
if (!Array.isArray(arr)) {
return;
}
var result = [];
for (var i = 0; i < arr.length; i++) {
// if(!~result.indexOf(arr[i]))
if (!result.includes(arr[i])) {
result.push(arr[i]);
}
}
return result;
}
var arr = [
1,
1,
2,
2,
"true",
"true",
true,
true,
undefined,
undefined,
null,
null,
[],
[],
NaN,
NaN,
"NaN",
{},
{},
function () {},
function () {},
];
console.log(unique(arr));
//这种方式,是没有办法过滤 引用类型的,但针对基本数据类型没有问题
# 5、案例:查找元素在数组中所有索引(下标)
TIP
- 利用 indexOf 方法,返回查找到的元素索引
- 如果返回索引不为-1,把索引添加到新数组,
- 则继续从当前索引的下一个索引位置开始查找,找到就返回索引继续查找,找不到则停止查找。
/**
* 找到数组中指定元素的所有索引(下标)
* @param arr 查找的数组
* @param element 查找的元素
*/
function findIndexs(arr, element) {
var indexArr = [];
var _index = arr.indexOf(element);
// 返回索引不是-1,则继续查找 _index!=-1
while (~_index) {
indexArr.push(_index);
_index = arr.indexOf(element, _index + 1);
}
return indexArr;
}
var arr = ["a", "b", "c", "d", "e", "b", "b", "f", "b"];
console.log(findIndexs(arr, "b"));
- 递归方法
function findIndexs(arr, el) {
var arrIndex = []; // 返回字符下标数组
var index = 0; // 每次查找索引
// 立即执行函数
(function findIndex(arr, el) {
index = arr.indexOf(el, index);
if (index !== -1) {
arrIndex.push(index);
findIndex(arr, el, ++index;);
}
})(arr, el);
// findIndex(arr, el);
return arrIndex;
}
var arr = ["a", "b", "a", "c", "c", "b", "c", "a", "a", "d"];
console.log(findIndexs(arr, "a"));
# 五、数组拼接与反转
实例方法 | 描述 | 是否更改原数组 |
---|---|---|
concat() | 方法用于合并两个或多个数组。 此方法不会更改现有数组,而是返回一个新数组 | 否 |
reverse() | 方法将数组中元素的位置颠倒,并返回该数组 该方法会改变原数组。 | 是 |
# 1、concat()方法
TIP
- 法用于合并两个或多个数组
- 此方法不会更改现有数组,而是返回一个新数组
- concat 返回的新数组是元素的浅拷贝
var arr1 = [1, 2, 3, 4];
var arr2 = [5, 6, 7, 8];
var arr3 = [9, 10, 11, 12];
var arr = arr1.concat(arr2, arr3);
console.log(arr); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
console.log(arr1); // [1, 2, 3, 4]
console.log(arr2); // [5, 6, 7, 8]
console.log(arr3); // [9, 10, 11, 12]
var arr1 = [1, 2, [4, 5]];
var arr2 = ["a", "b", "c"];
var arr3 = arr1.concat(arr2);
console.log(arr3); // [1, 2, ['改', 5], 'a', 'b', 'c']
arr3[2][0] = "改";
console.log(arr1); // [1, 2, ['改', 5]]
console.log(arr3); // [1, 2, ['改', 5], 'a', 'b', 'c']
# 2、reverse()方法
TIP
- reverse()方法可以将一个数组中的全部项顺序反过来显示。
- 他会改变原组。
var arr = [1, 2, 3, 4, 5];
arr.reverse();
console.log(arr); // [5,4,3,2,1]
- 将字符串进行倒序显示
var str = "清晨我上马";
var arr = str.split("");
arr.reverse();
var str2 = arr.join("");
console.log(str2); // 马上我晨清
# 六、筛选数组元素
方法 | 描述 | 是否更改原数组 |
---|---|---|
filter() | 返回所有通过函数测试成功的元素 其返回值为测试成功的元素组成的新数组 | 不会 |
find() | 返回数组中满足提供的测试函数的第一个元素的值 否则返回 undefined (opens new window)。 | 不会 |
# 1、filter()方法
返回所有通过函数测试成功的元素,其返回值为测试成功的元素组成的新数组
语法:
// 回调函数
filter(callbackFn);
filter(callbackFn, thisArg);
// 内联回调函数
filter(function (element) {
/* … */
});
filter(function (element, index) {
/* … */
});
filter(function (element, index, array) {
/* … */
});
filter(function (element, index, array) {
/* … */
}, thisArg);
详细解读
callbackFn 用来测试数组中每个元素的函数,返回
true
表示该元素通过测试,保留该元素,false
则不保留callbackFn 函数,接受以下三个参数
- element 数组中当前正在处理的元素
- index 正在处理的元素在数组中的索引
- array 调用了 filter()的数组本身
thisArg 可选,执行 callbackFn 时,用于 this 的值
# 2、找出数组中所有小于 10 的元素
// 筛选出所有小于10的元素
var arr = [1, 22, 5, 12, 45, 9, 10, 33, 8];
var arr2 = arr.filter(function (value) {
return value < 10;
});
console.log(arr2); // [1, 5, 9, 8]
// 返回字符串长度>=6的所有元素
var arr = ["iabc", "iconaff", "bef", "afei", "aiconf"];
var arr2 = arr.filter(function (value) {
return value.length >= 6;
});
console.log(arr2); // ['iconaff', 'aiconf']
# 3、自已封装简单版 filter 方法
// 大于等于10
var arr = [1, 3, 68, 33, 5, 9, 34, 55];
function filter(arr) {
var arr2 = [];
for (var i = 0; i <= arr.length; i++) {
if (arr[i] >= 10) {
arr2.push(arr[i]);
}
}
return arr2;
}
console.log(filter(arr));
/**
* 找到数组中所有满足测试函数条件的元素
* @param arr 查找的数组
* @param fn 测试函数
*/
function filter(arr, fn) {
var arr2 = [];
for (var i = 0; i <= arr.length; i++) {
// 为真,就添加
if (fn(arr[i])) {
arr2.push(arr[i]);
}
}
return arr2;
}
// 定义数组
var arr = [1, 3, 68, 33, 5, 9, 34, 55];
// 筛选出大于等于10的元素
var arr2 = filter(arr, function (value) {
return value >= 10;
});
console.log(arr2);
// 筛选出小于等于10的元素
var arr3 = filter(arr, function (value) {
return value < 10;
});
console.log(arr3);
# 4、find() 方法
返回数组中满足提供的测试函数的第一个元素的值。否则返回
undefined
// 回调函数
find(callbackFn);
find(callbackFn, thisArg);
// 内联回调函数
find(function (element) {
/* … */
});
find(function (element, index) {
/* … */
});
find(function (element, index, array) {
/* … */
});
find(function (element, index, array) {
/* … */
}, thisArg);
详细解读
callbackFn 用来测试数组中每个元素的函数
callbackFn 有三个参数
- element 当前数组中正在遍历到的元素
- index 当前数组中正在遍历到的索引
- array 当前调用 find 方法的数组
执行回调时用作
this
的对象。
// 筛选出小于10的第一个元素
var arr = [1, 22, 5, 12, 45, 9, 10, 33, 8];
var arr2 = arr.find(function (value) {
return value < 10;
});
console.log(arr2); // [1]
// 返回数组中第一个长度大于6的元素
var arr = ["iabc", "iconaff", "bef", "afei", "aiconf"];
var arr2 = arr.find(function (value) {
return value.length >= 6;
});
console.log(arr2); // iconaff
可以自己手动封装一个简单版的 find 方法
# 七、数组排序
方法 | 描述 | 是否更改变原数组 |
---|---|---|
sort() | 用于对数组的元素进行排序 | 是 |
// 无函数
sort();
// 内联比较函数 函数可以是匿名的
sort(function compareFn(a, b) {
/* … */
});
# 1、sort()未指字函数参数
TIP
当 sort 中没有给定参数时,数组中的元素会先被转换为字符串,然后比较的时候是按 Unicode 编码来进行排序
英文字母对应的 Unicode 编码
A~Z
对应 65~ 90 也就是A
的Unicode
编码是65
、Z
的Unicode
编码是90
a~z
对应 97~1220~9
对应 48~57
var arr = ["A", "Z", "b", 1, 80, 9, 100, 70, "a"];
arr.sort();
console.log(arr); // [1, 100, 70, 80, 9, 'A', 'Z', 'a', 'b']
注:
在没有函数做为参数时,这种比较本质上是没有办法做数字数组的排序
# 2、sort()指定函数参数
sort(function compareFn(a, b) {
return a - b;
});
// sort中的函数,也可以是匿名函数
sort(function (a, b) {
return a - b;
});
- compareFn 用来指定按某种顺序进行排列的函数
- a,b 是用于比较的两个元素
- 函数返回值为
a-b
,则数组按升序排序,即从小到大 - 函数返回值为
b-a
,则数组按降序排序,即从大到小
var arr = [1, 4, 90, 2, 5, 80, 100, 28];
arr.sort(function (a, b) {
return a - b;
});
console.log(arr); // [1, 2, 4, 5, 28, 80, 90, 100]
如何理解:a-b 即升序,b-a 即降序
在之前的数组中我们学过冒泡排序,冒泡排序的原则就是如果相邻的两个数(a,b)比较时
- 如果 a-b 大于 0,交换两者位置,即大的数 a 的放后面,小的 b 放前面,升序
- 如果 b-a 大于 0 ,交换两者位置,即大的数 b 放在前面,小的数 a 放在后面,降序
所以你可以把这里的 a 和 b 当成数组成相邻的两个比较元素
详细参考 MDN 文档 (opens new window)
- 如果
compareFn(a, b)
大于 0 , b 会被排列到 a 之前。 - 如果
compareFn(a, b)
小于 0 ,那么 a 会被排列到 b 之前; - 如果
compareFn(a, b)
等于 0 , a 和 b 的相对位置不变。
# 3、冒泡排序法封装成函数
// 封装冒泡排序
function bubbleSort(arr, fn) {
// 数组长度
var len = arr.length;
// 当前是否是有序的
var isSorted;
var flag = 0;
// 有序的边界
var sortBorder = len - 1;
// 外层for控制交换轮数
for (var i = 0; i < len - 1; i++) {
// 内层for控制每一轮,元素交换次数处理
isSorted = true; // 有序标记,每轮开始默认为有序,如果一旦发生交换,就会变成flag=false,无序
for (var j = 0; j < sortBorder; j++) {
if (fn(arr[j], arr[j + 1]) > 0) {
// 交换两元素位置
var tmp; // 用来交换两个变量的中间变量
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
isSorted = false;
// 把无序列表的边界,更新为最后一次交换元素的位置
flag = j;
}
}
if (!isSorted) {
sortBorder = flag;
}
// 这一轮多次交换下来,flag没有变为false,说明没有发生元素交换,此时数组已是有序的
if (isSorted) {
break; // 退出最外层for循环
}
}
}
// 排序数组
var arr = [2, 3, 4, 1, 5, 6, 7, 8, 9, 35, 76];
bubbleSort(arr, function (a, b) {
return a - b; // 升序
});
console.log(arr); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 35, 76]
bubbleSort(arr, function (a, b) {
return b - a; // 降序
});
console.log(arr); // [76, 35, 9, 8, 7, 6, 5, 4, 3, 2, 1]
# 八、数组相关方法总结
总结:
- 有哪些可以操作数组的方法 ?
- 这些方法的作用是什么 ?
- 哪些方法会改变原组数,哪些不会 ?
- (经典面试题)
实例方法 | 描述 | 是否改变原数组 | 返回结果 |
---|---|---|---|
push() | 将一个或多个元素添加到数组的末尾,并返回该数组的新长度 | 会 | 原数组的新长度 |
pop() | 从数组中删除最后一个元素,并返回该元素的值 | 会 | 返回被删除元素 |
unshift() | 将一个或多个元素添加到数组的头部,并返回该数组的新长度 | 会 | 原数组的新长度 |
shift() | 从数组中删除第一个元素,并返回该元素的值 | 会 | 被删除元素 |
slice() | (查询):提取原数组的一部分并返回一个新数组 | 不会 | 将查询到元素组成的新数组返回 |
splice() | (新增):在指定位置插入一项或多项新元素 (删除):在指定位置删除指定项 (更新):在指定位置删除指定项,同时在此处添加一项或多项 | 会 | 返回被删除元素组成的数组 |
join() | 将一个数组的所有元素以特定的某种字符连接成一个字符串 | 不会 | 并返回连接后的字符串 |
indexOf() | 返回在数组中可以找到给定元素的第一个索引,如果不存在,则返回 -1。 | 不会 | 返回元素索引或-1 |
includes() | 法用来判断一个数组是否包含一个指定的值, 根据情况,如果包含则返回 true ,否则返回 false | 不会 | 返回值为 true 或 false |
concat() | 方法用于合并两个或多个数组。 此方法不会更改现有数组,而是返回一个新数组 | 不会 | 将合并成的新数组返回 |
reverse() | 方法将数组中元素的位置颠倒,并返回该数组 该方法会改变原数组。 | 会 | 返回置返后的原数组 |
sort() | 对数组中的元素进行排序(降序或升序) | 会 | 返回排序后的原数组 |
filter | 返回所有通过函数测试成功的元素 其返回值为测试成功的元素组成的新数组 | 不会 | 返回符合要求的所有元素 |
find | 返回数组中满足提供的测试函数的第一个元素的值 否则返回 undefined (opens new window)。 | 不会 | 返回符合要求的第一个元素 |
forEach | forEach() 方法对数组的每个元素执行一次给定的函数 | 会 | 返回值为undefined |
关于操作数组的方法还有很多,我们目前只学习到这些,还有更多的在 ES6 之后再讲
# 九、综合应用案例
TIP
结合本章节所学内容进行综合实践应用。
# 1、求数组中每一项的总和、平均数
// 思路:求数组的和,先定义累加器 sum
var socreArr = [99, 96, 95, 89, 86, 88, 67, 53, 69, 36];
// 累加器
var sum = 0;
// 遍历数组,每遍历一个数字,就要把这个数字累加到累加器中
for (var i = 0; i < socreArr.length; i++) {
sum += socreArr[i];
}
// 数组的总和
console.log(sum); // 778
// 数组的平均数:数组的总和/数组的长度
console.log(sum / socreArr.length); // 77.8
# 2、随机样本:请随机从原来数组中取 3 项
TIP
随机取样,你不能取完样之后,把原数组中的数组给删除了
下面这种方式就是动了原数组
// 数组没有重复项并且不少于三个元素
var arr = [2, 1, 5, 8, 6, 9, 4, 3, 7];
// 结果数组
var result = [];
// 遍历原数组
for (var i = 0; i < 3; i++) {
// 随机选择一项的下标,数组的下标 0 ~ arr.length - 1;
// 之前学过一个 random的公式,[a,b] 区间的随机数是 parseInt(Math.random() * arr.length)
var n = parseInt(Math.random() * arr.length);
// 把这项推入结果数组
result.push(arr[n]);
// 删除这项,防止重复被随机到
arr.splice(n, 1);
}
console.log(result); // [1, 2, 5] 没运行一次,结果都不一样
- 随机取样,不会动原数组
// 不动原数组
function sample(arr, k) {
if (Array.isArray(arr)) {
// 取样长度不能超过数组长度 数组长3,取3,则3%3=0, 所以要3%4
k = k % (arr.length + 1);
// 用来放取样的数组
var result = [];
//
while (result.length < k) {
// 生成数组下标的随机数
var n = (Math.random() * arr.length) >> 0;
// 判断是否在取出的样本中存在
if (!result.includes(arr[n])) {
result.push(arr[n]);
}
}
return result;
}
}
var arr = [2, 1, 5, 8, 6, 9, 4, 3, 7];
console.log(sample(arr, 3));
# 3、随机抽奖
TIP
- 在我们做抽奖效果时,我们肯定是需要控制一,二,三等奖的中奖概率。
- 以下是一次活动中,规定的各种奖项的中奖概率(奖项的中奖机会占总数的百分比)
1 等奖 | 2 等奖 | 3 等奖 | 未中奖 | |
---|---|---|---|---|
概率 | 5% | 10% | 20% | 65% |
奖品 | 平板电脑 | 小米手机 | 电风扇 | 谢谢惠顾 |
解题思路:
- 我们可以设置一个权重数组
[5,10,20,65]
- 然后把权重数组中的中奖概率加起来,做为生成随机数的最大范围
- 然后把生成的随机数添加到数组的尾部,然后再对数组进行升序排序
- 最后确定生成随机数在权重数组中的下标,来确定对应的中奖项
- 如果下标超过原数组长度-1,则以原数组长度-1 为准。
// 随机抽奖
function randomDrawing(prizeWeight, prizes) {
// 输入的不是数组,就直接返回
if (!Array.isArray(prizeWeight) || !Array.isArray(prizes)) return;
// 两数组长度不一样长,或为空时,直接返回
if (
prizeWeight.length == 0 ||
prizes.length == 0 ||
prizeWeight.length !== prizes.length
)
return;
// 计算所有权重之和
var sum = 0;
for (var i = 0; i < prizeWeight.length; i++) {
sum += prizeWeight[i];
}
// 生成0-权重之和之间的随机数(不用加1,从0开始算的)
var random = (Math.random() * sum) >> 0;
// 把生成的随机数合并到原数组中,返回一个新中奖数组
var newArr = prizeWeight.concat(random);
// 对中奖数组进行排序
newArr.sort(function (a, b) {
return a - b;
});
// 找查生成的随机数在中奖数组中的下标
var prizesIndex = newArr.indexOf(random);
// 随机数下标不能超过原数组的长度-1,超过就以原数组length-1为准
randomIndex = Math.min(prizesIndex, prizeWeight.length - 1);
// 取出对应的奖项
return prizes[randomIndex];
}
// 奖品的权重数组
var prizeWeight = [5, 10, 20, 65];
// 对应的中奖奖品
var prizesArr = ["平板电脑", "小米手机", "电风扇", "谢谢惠顾"];
// 随机抽奖100次,看对应每种物品中奖概率
var arr = [0, 0, 0, 0];
for (var i = 0; i < 100; i++) {
var prize = randomDrawing(prizeWeight, prizesArr);
switch (prize) {
case "平板电脑":
arr[0]++;
break;
case "小米手机":
arr[1]++;
break;
case "电风扇":
arr[2]++;
break;
default:
arr[3]++;
}
}
console.log(arr);
# 4、手写 Flatten 方法,实现数组的扁平化 (面试题)
- 方法一
var arr = [1, 2, 3, [4, [5], [6]], 7, 8];
function flatten(arr) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
// 是数组,递归调用
newArr = newArr.concat(flatten(arr[i])); // concat返回新的拼接好的数组
} else {
newArr.push(arr[i]);
}
}
return newArr;
}
console.log(flatten(arr));
- 方法二:forEach+递归
var arr = [1, 2, 3, [4, [5], [6]], 7, 8];
function flatten(arr) {
var newArr = [];
arr.forEach(function (el) {
if (Array.isArray(el)) {
// 是数组,递归调用
newArr = newArr.concat(flatten(el)); // concat返回新的拼接好的数组
} else {
newArr.push(el);
}
});
return newArr;
}
console.log(flatten(arr));
- 方法三
- 循环遍历数组中的每个元素,如果不是数组,就添加到结果数组中
- 如果是数组,就遍历数组中的每个元素,然后添加到结果数组中
var arr = [1, 2, 3, [4, [2, 3]], [6], 7, 8];
function flatten(arr) {
var result = [];
function fn(arr) {
for (var i = 0; i < arr.length; i++) {
var item = arr[i]; // 重点
if (Array.isArray(arr[i])) {
fn(item); // 递归
} else {
result.push(item);
}
}
}
fn(arr);
return result;
}
console.log(flat(arr));
# 十、作业
homework
- 掌握本节课学习的所有数组相关的操作方法
- 自己手动封装简单版的以下方法:
push、pop、shift、unshift、slice、splice、join、indexOf、lastIndexOf、includes、concat、reverse、find
# 十一、总结重难点
TIP
总结本章重难点知识,理清思路,把握重难点。并能轻松回答以下问题,说明自己就真正的掌握了。
用于故而知新,快速复习。
# 1、重点
TIP
课程中讲到的所有数组相关的方法,都必需要掌握
# 2、难点
TIP
- 数组去重
- 手动封装 filter、find、forEach 方法
- 手写 flat 方法
大厂最新技术学习分享群
微信扫一扫进群,获取资料
X