前端总结·基础篇·JS(二)数组深拷贝、去重以及字符串反序和数组(Array)
前端总结系列
- 前端总结·基础篇·CSS(一)布局
- 前端总结·基础篇·CSS(二)视觉
- 前端总结·基础篇·CSS(三)补充
- 前端总结·基础篇·JS(一)原型、原型链、构造函数和字符串(String)
- 前端总结·基础篇·JS(二)数组深拷贝、去重以及字符串反序和数组(Array)
- 前端总结·基础篇·JS(三)arguments、callee、call、apply、bind及函数封装和构造函数
- 前端总结·基础篇·JS(四)异步请求及跨域方案
- 前端总结·工具篇·管理(一)常用模块化方案
目录
这是《前端总结·基础篇·JS》系列的第二篇,主要总结一下JS数组的使用、技巧以及常用方法。
一、数组使用
1.1 声明数组
1.2 访问数组
1.3 类型检测
二、常用技巧
2.1 数组去重
2.2 数组深拷贝
2.3 字符串反序
三、方法列表
3.1 存取
3.2 字符串
3.3 修改
3.4 ES5
3.5 ES2015(ES6)
3.6 ES2016
一、数组使用
数组不是基本数据类型,但是非常常用,所以提前总结。
基本数据类型是String,Number,Boolean,null,undefined。
- Boolean只有两个状态,true/false
- null一般是开发者定义
- undefined是JS引擎抛出的异常,通常在发现调用未声明的变量时抛出
1.1 声明数组
数组用中括号括起来,不同的值用逗号隔开。数组内的值有字符串需要用单引号或者双引号引起来。
var arr = [] // 声明一个空数组
var arr = [3,6,8,9] // 声明一个包含值3,6,8,9的数组
var arr = new Array(3,6,8,9) // 创建一个数组对象
1.2 访问数组
下标访问
数组的下标是从0开始的,最后一个值的下标是数组长度减去1。访问数组可以直接访问具体下标的一个值,也可以直接访问整个数组的值。
var arr = [1,2,3,4]
console.log(arr) // 直接访问数组 | [1, 2, 3, 4]
console.log(arr[0]) // 通过下标访问 | 1
console.log(arr.length) // 访问数组的长度 | 4
遍历访问
如果需要逐个的访问数组中的值,你需要对数组进行遍历。可以用一般的for循环、for in或者ES6中的for of。
var arr = [1,2,3,4]
// for
for(var x = 0; x < arr.length; x++){
console.log(arr[x]) // 通过下标访问
}
// for in
for(x in arr){
console.log(arr[x]) // 通过下标访问
}
// for of (ES6)
for(x of arr){
console.log(x) // 不需要通过下标访问
}
1.3 类型检测
检测一个对象有以下方法。更精确的检测数组请见请见jwalden
* typeof [] // object
* [] instanceof Array // false
* Array.isArray([]) // true | 通常用来检测数组
* Array.isArray(Array.prototype) // true | 这是一个例外情况
二、常用技巧
2.1 数组合并
可以用concat/push合并。concat返回合并后的新数组,push保存在原数组里。
-
感谢园友 @loveyatou 提醒。在push方法中把apply写成了call,导致结果不符合预期。现已修正。 2017/03/17 13:45
-
PS:call和apply都是用来改变函数作用域的,用法基本一致,唯独传递参数的方法不一致。call是逐个输入,逗号隔开。apply是全部放在一个数组内。
var a = [1,2,3]
var b = [4,5,6]
// concat 方法
var c = a.concat(b)
console.log(c) // [1, 2, 3, 4, 5, 6]
// push 方法
Array.prototype.push.apply(a,b)
console.log(a) // [1, 2, 3, 4, 5, 6]
2.2 数组深拷贝
我们可以根据concat/slice方法返回新数组的特性来进行深拷贝,也可以直接用循环语句暴力深拷贝。
- 把数组赋值给一个变量,只是复制了一个指向数组的指针(又叫引用)
- 当我们改变原数组的值,新数组的值也跟着改变
- 为了避免这样,你需要对数组进行深拷贝
// 深拷贝前
var arr = [1,2,3,4]
var newArr = arr // 把原数组赋值给新数组
arr.pop() // 删除原数组的一个值
console.log(arr) // 测试原数组的值 [1, 2, 3]
console.log(newArr) // 测试新数组的值 [1, 2, 3]
// 深拷贝后
var arr = [1,2,3,4]
var newArrCopy = arr.concat() // 开始深拷贝,此处也可以使用arr.slice()
arr.pop() // 删除原数组的一个值
console.log(arr) // 测试原数组的值 [1, 2, 3]
console.log(newArrCopy) // 测试新数组的值 [1, 2, 3, 4]
// 暴力深拷贝
var arr = [1,2,3,4]
var newArr = [] // 为了使用数组的push方法,一定要定义数据类型为数组
for (var x = 0; x < arr.length; x++){
newArr.push(arr[x]) // 用循环逐个把值保存在新数组内
}
arr.pop() // 删除原数组的一个值
console.log(arr) // 测试原数组的值 [1, 2, 3]
console.log(newArr) // 测试新数组的值 [1, 2, 3, 4]
2.3 数组去重
排序后去重,更多请见脚本之家
var newArr = arr.sort() // 排好序再比较
var newArrSaved = [newArr[0]] // 初始化为数组,用来存储最后的数据
for(var i =2;i<arr.length;i++){ // 从第二个开始
if(newArr[i]!==newArr[i-1]) // 判断当前值和上一个值是否一致
newArrSaved.push(newArr[i]) // 不一致则存在newArrSaved
}
2.4 字符串反序
首先将字符串序列化成数组,通过数组方法对数组反序,最后把数组转换成字符串。
'I See U.'.split('').reverse().join('') // ".U eeS I"
三、方法列表
ES5和ES6部分就当作参考手册吧,有时间再琢磨一下应用场景。
以下实例在此基础上进行
arr = [1,2,3,4]
3.1 存取
push在尾部添加,pop从尾部删除。
尾存取
push(element1, ..., elementN)
* arr.push(8) // [1, 2, 3, 4, 5]
pop()
* arr.pop() // [1, 2, 3, 4]
首存取
unshift在首部添加,shift从首部删除。
unshift(element1, ..., elementN)
* arr.unshift(5) // [5, 1, 2, 3, 4]
shift
* arr.shift() // [1, 2, 3, 4]
3.2 转换
字符串
join默认以逗号分隔连接的字符串,传入参数可以进行自定义分割方式。
toString()
* arr.toString() // "1,2,3,4"
toLocalString()
* arr.toLocalString() // 和上面的效果一致,此方法仅在特殊语言中需要
join(separator)
* arr.join() // "1,2,3,4"
* arr.join('') // "1234"
* arr.join('-') // "1-2-3-4"
3.3 修改
排序
reverse把数组颠倒,sort对数组排序。sort默认按照unicode排序,传入function(a,b)可以自定义排序。更多请见MDN(内有对象按属性值排序的方法)
reverse()
* arr.reverse() // [4, 3, 2, 1]
sort()
* arr.sort() // [1, 2, 3, 4] 这个已经排好了序,所以不变
连接
concat连接数组,并返回一个新的数组。
concat(value1[, value2[, ...[, valueN]]])
arr.concat(9) // [1, 2, 3, 4, 9]
切割
切割比上面的稍微复杂点
-
slice为提取元素,splice为删除指定范围元素并添加新元素。slice的第二个参数,是结束的位置标记,不会包括在返回值内。
-
slice的返回值是提取元素组成的新数组。splice的返回值是删除元素组成的新数组,原始元素被修改。
-
slice在IE<9下使用时,会出现一些问题,需要使用腻子脚本。详见MDN
slice(begin,end)
* arr.slice(1,3) // [2, 3]
splice(start, deleteCount, item1, item2, ...)
* arr.splice(1,3,5) // [2, 3, 4] 返回值
* console.log(arr) // [1, 5] 原始元素被修改
3.4 ES5
需要精确检测数组请见jwalden
数组检查
Array.isArray(obj) // 检查是否为数组,返回值为true/false,兼容IE9+
* Array.isArray([1,2,3]) // true
* Array.isArray('123') // false
* Array.isArray(Array.prototype) // true
arr.every(callback[, thisArg])
* function isNotZero(element, index, array){return element!==0}
* arr.every(isNotZero) // true
arr.map(callback[, thisArg]) // 每个值都调用一次函数并且返回新数组
* function twice(element, index, array){return element*element}
* arr.map(twice) // [1, 4, 9, 16]
arr.reduce(callback,[initialValue]) // 将多维数组转为一维数组
arr.some(callback[, thisArg]) // 测试元素是否通过指定测试
arr.indexOf(searchElement[, fromIndex = 0]) // 返回满足条件的第一个索引,不存在返回-1
* arr.indexOf(3) // 2
arr.lastIndexOf(searchElement[, fromIndex = arr.length - 1]) // 由后向前查找
arr.forEach(callback[, thisArg]) // 对数组每个元素执行一次函数
3.5 ES2015(ES6)
Array.of(element0[, element1[, ...[, elementN]]]) // 创建新数组
* Array.of(3) // [3]
* Array(3) // [undefined × 3]
* Array.of([1,2,3]) // [Array[3]]
* Array([1,2,3]) // [Array[3]]
Array.from(arrayLike[, mapFn[, thisArg]]) // 从类数组对象或可遍历对象中创建数组
* Array.from('berg') // ["b", "e", "r", "g"]
arr.copyWithin(target, start, end) // 选定数组值,在一定范围内全部粘贴选定值
* arr.copyWithin(1,2,3) // [1, 3, 3, 4]
arr.entries() // 返回新的数组迭代器(一定得复制给变量再迭代)
* var newArr = arr.entries()
* newArr.next().value // [0, 1]
* newArr.next().value // [1, 2]
* newArr.next().value // [2, 3]
* newArr.next().value // [3, 4]
arr.keys() // 返回新的数组迭代器
arr.fill(value, start, end) // 用指定值填充一定范围数组
* arr.fill(0,1,3) // [1, 0, 0, 4]
arr.filter(callback[, thisArg]) // 将满足条件的元素返回成新数组
* function isNotZero(element){return element!==0}
* arr.filter(isNotZero) // [1, 2, 3, 4]
arr.find(callback[, thisArg]) // 返回满足条件的第一个值,不存在返回undefined
* function isNotZero(element){return element!==0}
* arr.find(isNotZero) // 1
arr.findIndex(callback[, thisArg]) // 返回满足条件的第一个元素的索引
* function isNotZero(element){return element!==0}
* arr.findIndex(isNotZero) // 0
3.6 ES2016
arr.includes(searchElement, fromIndex) // 判断数组是否包含指定值,返回true/false
arr.includes(2) // true
总结
- 上一周一直在忙活Note这个小项目,现在已经上线了。以后会逐步增大项目复杂度。并且尝试用Vue和RN来分别对项目进行重构,后端则逐步接入Node.js和mongoDB。兼容性支持IE9及以上,根据IE7/8访客基数决定是否兼容。
- 接下来的时间就安心的总结JS和更博客吧。关于Note开发过程中遇到的问题,以及解决方案,等整理好了再发出来。
文章主要参考的站点
ES5
前端兼容性方案