常见面试题

65 阅读6分钟

面试题总结

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方法会依次执行每个元素:

  1. 如果有一个元素满足条件,则返回true,剩余的元素不会在检测。
  2. 如果没有满足条件的元素,则返回false。
  3. 参数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()完全相反:

  1. 检测数组中检测到有一个元素不符合条件,则整个表达式返回false,剩下的不会再检测。
  2. 所有的元素都满足条件,则表达式返回true。
  3. 参数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)
 ]

结果:

image.png

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)  

结果:

image.png

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))

image.png

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)

image.png

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)

image.png

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)

image.png

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);

image.png

3、赋值:

image.png

此例子说明把一个对象赋值给一个变量时,并没有重新创建新的对象,而是把原对象在栈中的地址(非数据)赋值给了新对象,也就是原对象的栈地址,所以原对象和新对象指向同一个地址。修改其中一个,另一个也会变。

以上内容是本人面试时遇见的问题,顺手总结了下,如有什么正确的,欢迎各位大佬一起探讨。