[toc]
> 数组 API:[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global\_Objects/Array](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array)
# 题1、如何获取数组中元素的个数?
答:length。
比如:
~~~
[1,2,34].length
~~~
# 题2、shift、unshift、pop、push 四个方法是干什么用的?
答:shift:取出数组中的第 1 个元素,并且将这个元素从数组中删除。(弹出)
注意:通过下标 0也可以获取数组中的第一个元素,arr[0],区别是这种不会删除第1个元素
unshift: 从数组的前面向数组中添加一个元素。
比如:[1,2,3].unshift(4) --> [4,1,2,3]
pop:取出数组中最后一个元素,并且将这个元素从数组中删除。(弹出)
注意:通过下标也可以获取数组中最后一企鲜红:arr[arr.length-1],区别是这种不会删除最后一个元素
push:从数组的后面向数组中添加一个元素。
比如:[1,2,3].push(0) --> [1,2,3,0]
# 题3、如何创建一个拥有 10 个元素的数组?
答:let arr = new Array(10)
知识点复习:创建数组至少有两种方法:
~~~
let arr = new Array() // 创建空数组
let arr1 = [] // 创建空数组
~~~
# 题4、如何用数组模拟栈结构?
答:栈(zhan)是一种数据结构,特点:后进先出(LIFO:Last In First Out)。
![](https://img.kancloud.cn/a8/20/a820fd3c2fa59aa8dcb3034a6cff80bb_508x368.png)
可以使用 push 和 pop 实现入栈和出栈操作来模拟栈。
也可以使用 shift 和 unshift 实现入栈和出栈操作来模拟栈。
# 题5、如何用数组模拟队列结构?
答:队列(queue)是一种数组,特点:前进前出(FIFO:Fist In First Out)。
![](https://img.kancloud.cn/26/92/2692b12ca2bedbda28770a2cb1b5176d_728x306.png)
可以使用 push 和 shift 方法实现入队和出队来模拟队列。
也可以使用 unshift 和 pop 方法实现入队和出队来模拟队列。
# 题6、如何获取数组中最后一个元素的值?
答:
方法一、下标 arr[ arr.length - 1] (只获取不会删除最后一个元素)
方法二、arr.pop() (获取并删除最后一个元素)
# 题7、如何把数组转成一个字符串?
答:方法一、join 默认使用,隔开,也可以使用其他字符
方法二、toString , 只能使用 , 隔开
# 题8、如何截取数组,比如:从数组中截取出第 2~4 个元素?
答:方法一、splice [1,2,3,4,5,6,7,8].splice(1, 5) 开始下标 ~ 截取长度 ,修改原数组
方法二、slice , [1,2,3,4,5,6,7,8].slice(1, 5) , 开始下标 ~ 结束下标 ,不修改原数组
# 题9、如何删除数组中的元素?
答:splice。 比如删除第2个元素: arr.splice(1,1)
# 题10、如何对数组进行排序?
答:sort。
sort 可以用来对数组进行排序,但是使用时要注意:默认是 `按字符串` 进行排序的!!
![](https://img.kancloud.cn/1a/6e/1a6e01cf6df970ea262ccb61618fb207_1168x332.png)
如何才能 `按数字` 来进行排序?
答:添加一个回调函数做为参数,这个回调函数有两个参数。
![](https://img.kancloud.cn/35/95/35957d88a1083518401ca43b70b8ce10_996x496.png)
回调函数可以简写为箭头函数:
![](https://img.kancloud.cn/ba/81/ba811a8e4023ebc848d0e0a3b3945539_548x256.png)
# 题11、什么是按引用传值?哪些变量是按引用传值?
答:把一个变量在内存中的地址(引用)传过去。
对象类型(包含数组)的数据都是按引用传值的。
![](https://img.kancloud.cn/e7/d6/e7d6e6a2166f2e634547bd56180466bd_762x510.png)
特点:a和b现在操作的是内存中的同一个数组。
比如:
![](https://img.kancloud.cn/1a/81/1a811963ffd36b56e18e358cee800762_1152x420.png)
总结:
1. 对象和数组在传值是是按引用传值。(传完之后,多个变量名操作的是同一个值)
2. 其他类型(数字、字符串、布尔等)不是按引用传值,其他类型是复制一份然后传值,(传完之后,彼此互不影响,各操作各的)
# 题12、引用的扩展面试题1?
请说出以下代码的输出结果
代码一、
~~~
let a = "abc"
let b = a // 不是引用传值,把 a 复制一份传给 b ,b 和 a 是两个独立的变量
b = 'hello' // b 和 a 是两个独立的变量,修改 b 不影响 a
console.log(a) // 输出 abc
console.log(b) // 输出 hello
~~~
代码二、
~~~
let a = [1,2,3]
let b = a // 数组是按引用传值,把 a 在内存中的地址传给 b ,a 和 b 指向同一个数组
b[1] = 100 // 修改 b 中的值, a 也会改变,因为 a 和 b 指向同一个数组
console.log(a) // 输出 [1,100,3]
console.log(b) // 输出 [1,100,3]
~~~
代码三、
~~~
let a = {name:'tom',age:10}
let b = a // 对象是引用传值,把 a 在内存中的地址传给 b ,a 和 b 指向同一个对象
b.age++ // 修改 b 对象就是修改 a 对象
console.log(a.age) // 输出 11
console.log(b.age) // 输出 11
~~~
# 题13、引用的扩展面试题2?
说出以下代码的输出结果:
代码一、
~~~
let a = 'abc'
function toUp(a) {
a = a.toUpperCase() // 这里的 a 和函数外面的 a 没有任何关系
}
toUp(a) // 不是按引用传递(按值传递),把 a 的值复制一份传到函数中,函数中的变量和 a 完全没有关系
console.log(a) // 输出 abc
~~~
代码二、
~~~
let a = [1,2,3]
function toUp(a) {
a[0] = 100 // 这个a 就是外面的 a ,所以修改这个,就是修改外面的 a
}
toUp(a) // 数组和对象都是按引用传递的,也就是说,函数中的 a 和外面的 a 是同一个 a
console.log(a) // 输出 [100,2,3]
~~~
上面两段代码函数参数中的 a 会给同学生造成误解,以为是同一个 a,其实 函数参数名是任意的,
代码一、
~~~
let a = 'abc'
function toUp(x) {
x = x.toUpperCase() // 这里的 a 和函数外面的 a 没有任何关系
}
toUp(a) // 不是按引用传递(按值传递),把 a 的值复制一份传到函数中,函数中的变量和 a 完全没有关系
console.log(a) // 输出 abc
~~~
代码二、
~~~
let a = [1,2,3]
function toUp(x) {
x[0] = 100 // 这个a 就是外面的 a ,所以修改这个,就是修改外面的 a
}
toUp(a) // 数组和对象都是按引用传递的,也就是说,函数中的 a 和外面的 a 是同一个 a
console.log(a) // 输出 [100,2,3]
~~~
当然还有一种情况:这个情况和引用没有关系,`变量作用域` 的问题:
~~~
let a = 'abc'
function toUp() {
a = a.toUpperCase() // 这个 a 就是外面的 a
}
toUp()
console.log(a) // 输出 ABC
~~~
# 题14、数组是按引用传递的,无法直接复制,那么应该如何复制(克隆)出一个新数组?
答:
方法一、(ES6)可以使用 ES6 中展开(...)运算符。(一维好使)
~~~
let a = [1,2,3,4]
let b = [...a] // 把 a 中的元素展开,赋给 b (复制了一份),a 和 b 是两个独立 的数组
b[0] = 100 // 不影响 a
console.log(a) // [1,2,3,4]
console.log(b) // [100,2,3,4]
~~~
方法二、先转字符串再转回数组( **几维都好使**)
~~~
let a = [1,2,3,4]
let b = JSON.parse(JSON.stringify(a)) // 复制克隆一份
b[0] = 100
console.log(a)
console.log(b)
~~~
方法三、slice(一维好使)
~~~
let a = [1,2,3,4]
let b = a.slice()
~~~
方法四、(ES6)(一维好使)
~~~
let a = [1,2,3,4]
let b = []
Object.assign(b,a) // 把 a 的值克隆一份给b
~~~
# 题15、如何合并两个数组?
答:
方法一、concat
~~~
let a = [1,2,3]
let b = [3,4,5]
let c = a.concat(b)
~~~
方法二、... (ES6)
~~~
let a = [1,2,3]
let b = [3,4,5]
let c = [...a, ...b]
~~~
# 题16、请说出至少 10 个操作数组的方法?
答:concat:合并数组
join:转字符串
map:循环并修改数组中每个元素
filter:过滤数组
find:从数组中查找一个元素
findIndex:从数组中查找一个元素的下标
sort:排序
push:从后面添加元素
pop:从后面“弹出”元素
shift:从前面“弹出”元素
unshift:从前面添加元素
slice:截取数组(不删除)
splice:截取并从数组中删除
reverse:反转数组元素的顺序
toString:转成字符串
toLocaleString:转字符串
forEach:循环数组中的元素
every(判断都满足):循环对数组中每个元素执行一个条件判断,如果所有的判断都返回 true ,那么最终结果是 true ,只要有一个是 false ,结果就是 false
some(判断至少有一个满足):和 every 相反,只要数组中有一个满足条件就返回 true,如果全都不满足条件就返回 false
~~~
some 和 every 和对比
let a = [3,4,5,6,7]
// 含义:every(每一个都满足):是否每一个都大于4
a.every(v=>v>4) // false
// 含义:some(有至少一个满足):是否有至少一个大于4
a.some(v=>v>4) // true
~~~
reduce:循环处理数组中所有的元素,最后可以把每个结果累加到一起,返回最终结果。要会使用这个函数,首先要明确参数的函数。
~~~
let a = [1,2,3,4,5]
// 参数一x、上一次计算的结果
// 参数二y、循环数组时的当前元素
// 参数三z、当前元素的下标
// 返回值:这一次的计算结果,这个结果会被当作下一次循环的累加结果(第一个参数)
// 比较数组中求和:
// 说明: return x+y 含义:上一次的累加结果(x) + 当前值,和做为下一次的累加结果
a.reduce( function(x,y,z){ return x+y })
简写:
a.reduce((a,b)=>a+b)
~~~
# 题17、有一个数组:['abc','bcd','cd'],如何将数组中每个元素的第 1 个字母变大写?
答:
~~~
let a = ['abc','bcd','cd']
// 1. 向字符串对象的原型中添加一个方法,这样所有字符串就可以直接使用这个方法了
String.prototype.ucfirst = function() {
// 转数组
let arr = this.split("") // ['a','b','c']
// 每1个元素大写
arr[0] = arr[0].toUpperCase() // ['A','b','c']
// 转字符
return arr.join("") // 'Abc'
}
// 2.对数组中每个字符串都执行 ucFirst 方法
a = a.map(v=>v.ucfirst())
console.log(a) // ['Abc','Bcd','Cd']
~~~
# 题18、如何对数组去重?能想到几种方法?
答:
方案一、最简单的使用 ES6 中的 Set
使用 ES6 中的 Set 这种数据类型。
Set(集合),特点:没有重复的数据。
思路:1. 先把数组转成一个集合(重复的数据就删除了) ---> let s = new Set( 数组 )
2. 把集合转回数组 ----> [ ...s ]
方案二、(最笨的方法)双向循环,一个一个判断