数组面试题

1、如何判断数组是数组类型?

  • Array.isArray() 最靠谱的判断数组的方法

    在调用这个方法之前重写了Object.prototype.toString方法或修改constructor对象,不影响判断的结果

    Object.prototype.toString = ()=>{
        console.log('Hello Howard');
    }
    const a = [];
    Array.isArray(a);//true
    
    a.constructor = b.constructor;
    Array.isArray(a);//true
    

    兼容写法

    if (!Array.isArray) {
      Array.isArray = function(arg) {
        return Object.prototype.toString.call(arg) === '[object Array]';
      };
    }
    
  • 验证构造函数

    • instanceof判断:判断某个构造函数的prototype属性所指向的對象是否存在于要检测对象的原型链上
    object instanceof constructor
    
    const a = [];
    const b = {};
    console.log(a instanceof Array);//true
    console.log(a instanceof Object);//true,在数组的原型链上也能找到
    Object构造函数
    
    

    ​ 验证不够严格。 即使对象创建时不是使用数组创建的,但是只要原型链上有数组类型

    function Test(){}
    Test.prototype = Array.prototype;
    let test = new Test();
    test instanceof Array;//true
    
    • constructor:实例化的数组拥有一个constructor属性,这个属性指向生成这个数组的方法

      obj.constructor == Array
      
      const a = [];
      console.log(a.constructor);//function Array(){ [native code] }
      
  • 验证原型对象

    • getPrototypeOf: 这是Object自带的一个API,作用是获取一个对象的原型对象
      • Object.getPrototypeOf(obj) == Array.prototype
    • isPrototypeOf:每个对象都有一个isPrototypeOf的API,继承自Object.prototype
      • var bool = Array.prototype.isPrototypeOf(obj)
  • 检查内部属性class + call或apply

    每个对象中记录对象创建时使用的类型的属性,一旦对象被创建,class属性就无法被修改。获得class的唯一的办法就是调用Object.prototype中的toString()方法

    const a = ['Hello','Howard'];
    Object.prototype.toString.call(a);//"[object Array]"
    Object.prototype.toString.apply(a);//"[object Array]"
    
    

2、数组与字符串

  • 由数组转换为字符串,使用join;
  • 由字符串转换为数组,使用split
// join - 由数组转换为字符串,使用join
console.log(['Hello', 'World'].join(','));    // Hello,World
// split - 由字符串转换为数组,使用split
console.log('Hello World'.split(' '));    // ["Hello", "World"] 

3、查找元素

indexOf : arr.indexOf(searchElement[, fromIndex])
searchElement:需检索的元素
fromIndex:可选整数参数,规定在数组中开始检索的位置

// indexOf - 查找元素
console.log(['abc', 'bcd', 'cde'].indexOf('bcd'));  // 1

4、数组连接

使用concat之后会生成一个新的数组

var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
var array3 = array1.concat(array2); // 实现数组连接之后,会创建出新的数组
console.log(array3);

5、类列表操作

push/pop/shift/unshift

var array = [2, 3, 4, 5]; // 添加到数组尾部
array.push(6);
console.log(array); // [2, 3, 4, 5, 6] 

// 添加到数组头部
array.unshift(1);
console.log(array); // [1, 2, 3, 4, 5, 6] 

// 移除最后一个元素
var elementOfPop = array.pop();
console.log(elementOfPop); // 6
console.log(array); // [1, 2, 3, 4, 5] 

// 移除第一个元素
var elementOfShift = array.shift();
console.log(elementOfShift); // 1
console.log(array); // [2, 3, 4, 5] 

6、splice方法

主要两个用途:

  • 从数组中间位置添加和删除元素
  • 从原有数组中,获取一个新数组

需提供如下参数

  • 起始索引(也就是你希望开始添加元素的地方)
  • 需要删除的元素的个数或是提取的元素的个数(添加元素时该参数设置为0)
  • 想要添加进数组的元素
var nums = [1, 2, 3, 7, 8, 9];
nums.splice(3, 0, 4, 5, 6);
console.log(nums);  // [1, 2, 3, 4, 5, 6, 7, 8, 9] 
// 紧接着做删除操作或者提取新的数组
var newnums = nums.splice(3, 4);
console.log(nums);  // [1, 2, 3, 8, 9]
console.log(newnums);   // [4, 5, 6, 7] 

7、排序

reverse:数组反转

// 反转数组
var array = [1, 2, 3, 4, 5];
array.reverse();
console.log(array); // [5, 4, 3, 2, 1]

sort:按照字典顺序对元素进行排序,在调用方法时传入一个大小比较函数,排序时,sort()方法将会根据该函数比较数组中两个元素的大小

var compare = function(num1, num2) {
    return num1 > num2;
};
nums.sort(compare);
console.log(nums);  // [1, 2, 3, 4, 100, 200] 

var objInArray = [
    {
        name: 'king',
        pass: '123',
        index: 2
    },
    {
        name: 'king1',
        pass: '234',
        index: 1
    }
];
// 对数组中的对象元素,根据index进行升序
var compare = function(o1, o2) {
    return o1.index > o2.index;
};
objInArray.sort(compare);
console.log(objInArray[0].index < objInArray[1].index); // true

8、for of, for in 和 forEach用法及其区别

  • for in:用于遍历数组或者对象的属性
    • 缺点:键名是字符串;会遍历对象本身的所有可枚举属性和从它原型继承而来的可枚举属性,仅迭代对象本身的属性,要结合hasOwnProperty()来使用;
let aArray = ['a',123,{a:'1',b:'2'}];
aArray.name = 'demo';
for(let index in aArray){
    console.log(index); //0,1,2,name也被循环出来了
}
var obj = {
    name:"echolun",
    age:"24",
    sex:"male"
},
objName=[], //用来装对象属性名
objVal=[];  //用来装对象属性值
Object.prototype.game="lastgame";
for(var i in obj){
    if(obj.hasOwnProperty(i)) {
        objName.push(i);
        objVal.push(obj[i]);
    }
}
console.log(objName,objVal);
}
  • for of:可迭代对象(Array,Map,Set,String,TypedArray,arguments)上创建一个迭代循环
for(var value of aArray){
    console.log(value); // 'a',123,{a:'1',b:'2'}
}
var student={
    name:'wujunchuan',
    age:22,
    locate:{
    country:'china',
    city:'xiamen',
    school:'XMUT'
    }
}
for(var key of Object.keys(student)){
    //使用Object.keys()方法获取对象key的数组
    console.log(key+": "+student[key]);
}
  • for of 与 for in 区别
    • 推荐在循环对象属性的时候,使用for...in,在遍历数组的时候的时候使用for...of。

    • for...in循环出的是key,for...of循环出的是value

    • for...of是ES6新引入的特性。修复了ES5引入的for...in的不足

    • for...of不能循环普通的对象,需要通过和Object.keys()搭配使用

  • forEach:用于遍历数组,在运行途中无法跳出循环,break和return不起作用,空数组无法执行回调函数

9、数组去重

  • ES6 Set去重: 无法去掉“{}”空对象
var set = new Set([1, 2, 3, 3, 4]); //先把数组转化为set集合
Array.from(set)  //输出[1,2,3,4]  通过Array.from这个方法把集合在转化为数组
[...new Set(arr)] //简化写法
  • 利用for嵌套for,然后splice去重(NaN和{}没有去重)
function unique(arr){            
        for(var i=0; i
  • 利用indexOf去重: 新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组(NaN和{}没有去重)
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array .indexOf(arr[i]) === -1) {
            array .push(arr[i])
        }
    }
    return array;
}
var arr = [false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
//[false, undefined, null, NaN, NaN, "NaN", 0, "a", {}, {}]   NaN和{}没有去重
  • 利用sort(): 根据排序后的结果进行遍历及相邻元素比对(NaN和{}没有去重)
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return;
    }
    arr = arr.sort()
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}
     var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
//(14) [0, 1, 15, NaN, NaN, "NaN", {}, {}, "a", false, null, "true", true, undefined]
  • 利用filter({}没有去重)
function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {}, {}]
  • 利用hasOwnProperty (所有的都可以去重)
function removeDuplicates(arr){  
    var i, len=arr.length;  
    var array=[], obj={};  
    for(i=0; i
  • 利用includes:检测数组是否有某个值({}没有去重)
//和indexOf 去重方法类似,只是if()里改为!array.includes(arr[i])

10、数组随机排序

  • 法一、
    • 遍历数组,每次循环都随机一个在数组长度范围内的数,并交换本次循环的位置和随机数位置上的元素
  • 法二、
    • 申明一个新的空数组,利用while循环,如果数组长度大于0,就继续循环;
    • 每次循环都随机一个在数组长度范围内的数,将随机数位置上的元素push到新数组里,
    • 并利用splice(对splice不太理解的同学可以看这里)截取出随机数位置上的元素,同时也修改了原始数组的长度;
  • 法三、利用传入sort排序中的比较函数
    • 如果 compareFunction(a, b)的返回值 小于 0 ,那么 a 会被排列到 b 之前;
    • 如果 compareFunction(a, b)的返回值 等于 0 ,那么a 和 b 的相对位置不变;
    • 如果 compareFunction(a, b)的返回值 大于 0 ,那么b 会被排列到 a 之前;
 function randomSort3(arr){
    arr.sort(function(a,b){
        return Math.random() - 0.5;
    });
    return arr;
}

11、JavaScript数组迭代(遍历)方法

forEach: 对数组中每一项运行给定函数。该函数没有返回值

var arr = [1, 2, 3];
var obj = {name: 'zhang'};

arr.forEach(function (element, index, array) {
  console.log(element, index, array, this)
}, obj)

// output
1 0 [1, 2, 3] {name: "zhang"}
2 1 [1, 2, 3] {name: "zhang"}
3 2 [1, 2, 3] {name: "zhang"}

以下方法有返回值(有返回值的方法,callback需要有return值)
map: 对数组中每一项运行给定函数。返回每次函数调用的结果组成的函数

var data = [1, 2, 3];

var arrayOfSquares = data.map(function (element) {
  return element * element;
});

console.log(arrayOfSquares); //[1, 4, 9]

filter: 对数组中的每一项运行给定函数。返回该函数会返回 true 的项组成的数组

var arr = [0, 1, 2, 3];

var newArr = arr.filter(function (element, index, array) {
  return e;
})

var newArr2 = arr.filter(function (element, index, array) {
  return e>=2; 
})

console.log(newArr); // [1, 2, 3]
console.log(newArr2); // [2, 3]

some: 对数组中每一项运行给定函数。如果函数对 任一项返回 true,则返回 true

function isBigEnough(element, index, array) {
  return element >= 4;
}
var passed = [1, 2, 3].some(isBigEnough);
var passed2 = [1, 2, 3, 4].some(isBigEnough);

console.log(passed); // false
console.log(passed2); // true

every: 对数组中的每一项运行给定函数。如果函数对每一项都返回 true,则返回 true

function isBigEnough(element, index, array) {
  return element >= 3;
}
var passed = [2, 3, 4].every(isBigEnough);
var passed2 = [3, 4, 5].every(isBigEnough);

console.log(passed); // false
console.log(passed2); // true

find / findIndex: 用于找出第一个符合条件的数组成员返回undefined/-1

var value = [1, 5, 10, 15].find(function(element, index, array) {
  return element > 9;
});
var value2 = [1, 5, 10, 15].find(function(element, index, array) {
  return element > 20;
});

console.log(value); // 10
console.log(value2); // undefined

以上方法中的callback函数会被依次传入三个参数:
数组当前项的值
数组当前项的索引
数组对象本身
还可以传入第二个可选参数,callback函数里的this将指向这个参数。默认this指向全局对象(在浏览器是为window),严格模式下是undefined

reduce / reduceRight: 让数组中的前项和后项某种计算,并累计最终值/从最后一个值开始计算的

var sum = [1, 2, 3].reduce(function(a, b) {
    return a + b;
});
console.log(sum); // 6

其中callback可以依次接受四个参数:
accumulator上一次调用回调返回的值,或者是提供的初始值(initialValue)
currentValue数组中正在处理的元素
currentIndex数组中正在处理的元素索引,如果提供了initialValue ,从0开始;否则从1开始。
array数组对象本身
reduce / reduceRight 方法中,第二个参数(initialValue)是可选的;其值用于第一次调用callback的第一个参数。

var sum = [0,1,2,3,4].reduce(function(accumulator, currentValue, currentIndex, array){
  console.log(accumulator, currentValue, currentIndex, array)
  return accumulator + currentValue;
}, 10);
console.log(sum);

// output
10 0 0 [0, 1, 2, 3, 4]
10 1 1 [0, 1, 2, 3, 4]
11 2 2 [0, 1, 2, 3, 4]
13 3 3 [0, 1, 2, 3, 4]
16 4 4 [0, 1, 2, 3, 4]
20

参考文章:JavaScript数组迭代(遍历)方法

你可能感兴趣的:(数组面试题)