面试题总结
1、js中的map(),some(),every(),filter()的区别
js中的map,some,every,filter几个方法可以对数组中的每个对象进行处理,区别如下:
map():通过指定函数处理数组的每个元素,并返回处理后的数组。
1 var numbers = [4, 9, 16, 25];
2
3 function myFunction() {
4 console.log(numbers.map(Math.sqrt));
5 }
输出结果如下:
2,3,4,5
map()方法将数组中的元素依次传入方法中,并将方法的返回结果组成新数组返回。 可以接收3个参数:currentValue(元素,不能缺少),index(元素对应的索引,可传),arr(元素所属数组,可传);
注意:map不会改变原数组,map不会检查空数组;
some():用于检索数组中元素是否满足指定条件(函数提供)
var ages = [3, 10, 18, 20];
function checkAdult(age) {
return age >= 18;
}
function myFunction() {
console.log(ages.some(checkAdult));
}
输出结果如下:
true
some方法会依次执行每个元素:
- 如果有一个元素满足条件,则返回true,剩余的元素不会在检测。
- 如果没有满足条件的元素,则返回false。
- 参数currentValue(元素,不能缺少),index(元素对应的索引,可传),arr(元素所属数组,可传)。
注意:some不会改变原数组,some不会检查空数组;
every():用于检索数组中所有元素是否满足指定条件(函数提供)
var ages = [32, 33, 16, 40];
function checkAdult(age) {
return age >= 18;
}
function myFunction() {
console.log(ages.every(checkAdult));
}
输出结果如下:
false
every()和some()完全相反:
- 检测数组中检测到有一个元素不符合条件,则整个表达式返回false,剩下的不会再检测。
- 所有的元素都满足条件,则表达式返回true。
- 参数currentValue(元素,不能缺少),index(元素对应的索引,可传),arr(元素所属数组,可传)。
注意:every不会改变原数组,every不会检查空数组;
filter():创建一个新数组,新数组的元素是通过检查组件中符合条件的所有元素。
var ages = [32, 33, 16, 40];
function checkAdult(age) {
return age >= 18;
}
function myFunction() {
console.log(ages.filter(checkAdult));
}
输出结果如下:
32,33,40
filter会根据函数中的筛选条件将返回的结果组成一个新的数组并返回
2、js中深拷贝与浅拷贝的理解及实现方式
基本概念
深拷贝与浅拷贝是针对引用类型,基本类型的存储在栈中。当复制时,栈内存会开辟一个栈内存,所以二者修改时,彼此不会受影响。
1、深拷贝:会开辟新的栈内存,原对象与新对象不共享同一块内存,修改新对象不会影响原对象。 2、浅拷贝:复制的是对象的指针,并没有开辟新的栈内存,原对象喝新对象还共享同一块内存,修改新对象自行会影响原对象。
实现方式
1、浅拷贝的实现方式:
1.1、Object.assign() ===> 可以把任意多个对象的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象
Object.assign()只会拷贝所有属性到新对象中,如果拷贝的是基本类型,修改其中一个对象,不会影响另一个。而如果属性值是对象的话,拷贝的是对象的引用,而非对象本身,所以该方法为浅拷贝。 分两种情况: (1)属性值是基本类型,修改其中一个对象,不会影响另一个对象的该属性:
var iniObj = {
name: "peter",
};
var newObj = Object.assign({}, iniObj);
newObj.name = "lily";
console.log('iniObj.name', iniObj.name); //peter
(2)属性值是引用类型,由于拷贝的是对象的引用,共享相同的地址:
var iniObj = {
info: {
name: "peter",
age: 8
}
};
var newObj = Object.assign({}, iniObj);
newObj.info.name = "lily";
console.log('iniObj.info.name', iniObj.info.name); //lily
1.2、Array.prototype.cancat() ===> 用于合并两个或多个数组。不会更改原数组,返回新数组。Array.prototype.slice() ===> 返回一个新的数组对象,这一对象是由begin和end(不包括end)决定的原数组的浅拷贝。原数组不会被改变
注意:分如下两种情况
(1)对象引用:concat,slice将对象引用(不是实际对象)复制到新数组中。原数组和新数组都引用相同的对象。也就是说如果引用的对象被修改,则更改对于新数组和原始数组是可见的。 (2)数据类型(如:字符串,数字,布尔等基础类型):concat,slice将值复制到新数组中,在新的数组中更改这些值将不会影响另一个数组。
var iniArr = [1,2,{name:"peter"}];
var newArr = iniArr.concat();
newArr[1] = 8;
newArr[2].name = "lily";
console.log('iniArr',iniArr);
结果
[
0: 1
1: 2
2: {
name: "lily"
}
length: 3
__proto__: Array(0)
]
结果:
1.3、引用复制
示例如下:
var iniObj = {
gender: "male",
info: {
name: "peter",
age: 8
},
test: [1, [2, 3]]
};
function shallowCopy(copyObj) {
var obj = {};
for (var i in copyObj) {
obj[i] = copyObj[i];
}
return obj;
}
var newObj = shallowCopy(iniObj);
newObj.gender = "female";
newObj.info.name = "lily";
newObj.test[1] = [4, 5];
console.log('iniObj', iniObj);
console.log('newObj', newObj)
结果:
2、深拷贝的实现方式:
2.1、JSON.parse(JSON.stringify()) ===>利用JSON.stringify()将js对象转为JSON字符串,再使用JSON.parse来还原js对象;产生新的对象,对象会开辟新的栈,实现深拷贝。
需注意如下几种情况:
1、如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;
var test = {
name: 'a',
date: [new Date(1536627600000), new Date(1540047600000)],
};
let b;
b = JSON.parse(JSON.stringify(test))
2、如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;
const test = {
name: 'a',
date: new RegExp('\\w+'),
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)
3、如果obj里有函数,undefined,symbol则序列化的结果会把函数或 undefined丢失;
const test2 = {
name: 'a',
date: function hehe() {
console.log('fff')
},
};
const copyed1 = JSON.parse(JSON.stringify(test2));
test2.name = 'test'
console.error('ddd', test2, copyed1)
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null 5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
function Person(name) {
this.name = name;
console.log(name)
}
const liai = new Person('liai');
const test = {
name: 'a',
date: liai,
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)
2.2、递归方法 ===> 遍历对象、数组,直到里边都是基本数据类型,然后再去复制,即可实现深度拷贝。
var iniObj = {
a: 1,
b: [1, 2, 3],
c: {
d: {
e: 4
}
},
fun: function(){
return 999;
}
};
function deepClone(x) {
console.log(111,x.constructor)
if (x.constructor === Object) {
var obj = {}
for (var k in x) {
console.log(x[k],2222)
obj[k] = deepClone(x[k])
}
return obj
} else if (x.constructor === Array) {
var arr = [];
for (var i = 0; i < x.length; i++) {
console.log(x[i],3333)
arr[i] = deepClone(x[i])
}
return arr
} else {
return x
}
}
var newObj = deepClone(iniObj)
newObj.b[0] = 6;
newObj.c.d.e = 8;
console.log('recursive deepClone,iniObj,newObj',iniObj,newObj);
3、赋值:
此例子说明把一个对象赋值给一个变量时,并没有重新创建新的对象,而是把原对象在栈中的地址(非数据)赋值给了新对象,也就是原对象的栈地址,所以原对象和新对象指向同一个地址。修改其中一个,另一个也会变。
以上内容是本人面试时遇见的问题,顺手总结了下,如有什么正确的,欢迎各位大佬一起探讨。