JavaScript面试题总结系列(三)

3. JavaScript数组

3.1 数组常用方法

  • map: 遍历数组,返回回调函数返回值组成的新数组,不改变原数组
  • forEach:无法break,可以用try/catch中throw new Error来停止
  • filter:过滤
  • some:有一项返回true,则整体为true
  • every:有一项返回false,则整体为false
  • join:通过指定连接符生成字符串
  • push / pop:末尾推入和弹出,改变原数组, 返回推入/弹出项
  • unshift / shift:头部推入和弹出,改变原数组,返回操作项
  • sort(fn) / reverse:排序与反转,改变原数组
  • concat:连接数组,不影响原数组, 浅拷贝
  • slice(start, end):返回截断后的新数组,不改变原数组
  • splice(start, number, value):返回删除元素组成的数组,value为插入项,改变原数组
  • indexOf / lastIndexOf(value, fromIndex):查找数组项,返回对应的下标
  • reduce / reduceRight(fn(prev, cur), defaultPrev):两两执行,prev 为上次化简函数的return值,cur为当前值(从第二项开始)

3.2 判断数组

  • instanceof 方法
    • instanceof 运算符是用来判断一个对象的原型链中是不是能找到类型的 prototype
    var arr = [];
    arr instanceof Array; // true
  • constructor 方法

    constructor属性返回对创建此对象的数组函数的引用,就是返回对象相对应的构造函数
    javascript var arr = []; arr.constructor == Array; // true

  • isArray() 方法
    var a = new Array(123);
    var b = new Date();
    console.log(Array.isArray(a)); // true
    console.log(Array.isArray(b)); // false
  • Object.prototype.toString.call()

    这个在之前讲判断数据类型的文章里说过,这里不再赘述。

3.3 数组的遍历

  • map和forEach的区别
    • map具有返回值,返回一个新的数组,不修改原数组;
      forEach没有返回值,直接修改原数组
    • 从性能上来说,forEach的性能要略好于map。
      测试例子请狠狠点击这里
  • JavaScript数组和对象的遍历方式,以及几种方式的比较

    通常我们会用循环的方式来遍历数组。但是循环是 导致js 性能问题的原因之一。一般我们会采用下几种方式来进行数组的遍历

    • for in循环
    • for循环
    • forEach
      • 这里的 forEach回调中两个参数分别为 value,index
      • forEach 无法遍历对象
      • IE不支持该方法;Firefox 和 chrome 支持
      • forEach 无法使用 break,continue 跳出循环,且使用 return 是跳过本次循环
    • 这两种方法应该非常常见且使用很频繁。但实际上,这两种方法都存在性能问题
    • 在方式一中,for-in需要分析出array的每个属性,这个操作性能开销很大。用在 key 已知的数组上是非常不划算的。所以尽量不要用for-in,除非你不清楚要处理哪些属性,例如 JSON对象这样的情况
    • 在方式2中,循环每进行一次,就要检查一下数组长度。读取属性(数组长度)要比读局部变量慢,尤其是当 array 里存放的都是 DOM 元素,因为每次读取都会扫描一遍页面上的选择器相关元素,速度会大大降低
    • ES6新增的 for of 循环。for in循环通常用来遍历对象的Key,for of通常用来遍历对象的Value。

3.4 数组去重

  • 利用双重循环去判断。声明一个空数组,然后遍历原数组,从原数组第一个元素开始往新的数组中放,如果没有该值,则push进去,否则跳过,最后返回新数组。实现数组去重。
  • 使用ES的Set集合。
    Set集合的特性之一是其集合中没有重复的元素,因此可以利用这一特性来实现数组去重的目的。举例:
var array2 = [...new Set([1,2,3,3,3,4,5])];
console.log(array2); // Array(5) [ 1, 2, 3, 4, 5 ]

3.5 数组乱序

  • 思路:使用sort()排序,或者使用洗牌算法。
  • 实现:
    • 创建一个数组
    let arr = Array(100000).fill(0).map((item, index) => index + 1);
      1. 直接利用sort进行排序,有漏洞,大部分元素位置没有移动
    arr.sort((a, b) => (Math.random() > 0.5 ? -1 : 1));
    console.log(arr);
      1. 经典洗牌算法实现
    function shuffle(array) {
        let arrayLength = array.length,
            randomIndex, //随机数
            tempItem; //临时存储元素
        for (let i = arrayLength - 1; i >= 0; i--) {
            randomIndex = Math.floor(Math.random() * (i + 1));
            tempItem = array[randomIndex];
            array[randomIndex] = array[i];
            array[i] = tempItem;
        }
        return array;
    }
    console.log(shuffle(arr));

3.6 数组的扁平化(数组降维/多维转一维)

3.6.1 概念

数组的扁平化是指将一个多为数组变为一维数组。例如:

[1, [2, 3, [4, 5]]] ------> [1, 2, 3, 4, 5]

3.6.2 实现

  • ES10 数组新特性: Array.prototype.flat()方法

    flat()方法按照一个可以指定的嵌套深度的参数来递归遍历数组,并将所有元素以及遍历到的子数组里面的元素合并返回一个新数组。

    var arr = [true, function(){}, [{}, [2]]];
    var newArr = arr.flat(2);
    console.log(newArr); // ?[true, ?, {…}, 2]
  • 递归。如下:
    function flatten(arr) {
        var res = [];
        arr.map(item => {
            if(Array.isArray(item)) {
                res = res.concat(flatten(item));
            } else {
                res.push(item);
            }
        });
        return res;
    }
  • 扩展运算符
    function flatten(arr) {
        while(arr.some(item=>Array.isArray(item))) {
            arr = [].concat(...arr);
        }
        return arr;
    }
  • reduce

    使用数组的reduce方法,遍历数组中的每一个项,如果某一项是数组,则继续遍历,否则concat(连接数组);
    举例:
    javascript function flatten(arr) { return arr.reduce((result, item)=> { return result.concat(Array.isArray(item) ? flatten(item) : item); }, []); } var arr = [false, function(){}, [{}, [2]]]; var arr2 = [1, [2,3],4,[5,[6,7]]]; var newArr = flatten(arr); var newArr2 = flatten(arr2); console.log(newArr); // [false, ?, {…}, 2] console.log(newArr2); // [1, 2, 3, 4, 5, 6, 7]

3.6.3 总结

实现数组扁平化的方法有很多,但是最核心的一点就是,遍历数组,拿到数组的每一项,如果是数组,再继续遍历,直到每一项不再是数组则停止。然后将这些项放到一个新的数组,最后将这个数组返回。数组降维,或者多维转一维都和数组扁平化是一个道理。


参考链接:
http://blog.poetries.top/FE-Interview-Questions/review/#_23-%E6%95%B0%E7%BB%84-array
https://www.cnblogs.com/chris-oil/p/8743509.html
https://www.cnblogs.com/owenzh/p/11058708.html https://juejin.im/post/5d391d336fb9a07ebe750343https://juejin.im/post/59716f15f265da6c4c500fc7
https://juejin.im/post/5adc8e396fb9a07aa0479725 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat

原文地址:https://www.cnblogs.com/zxxsteven/p/11725505.html

时间: 2024-11-09 02:05:05

JavaScript面试题总结系列(三)的相关文章

JavaScript面试题总结系列(九)

9. JavaScript - ES6+介绍 一.var let const 区别: 使用 var 声明的变量,可以变量提升: 使用let. const声明的变量,没有变量提升: 二. 临时死区(Temporal Dead Zone) 与var不同,let和const声明的变量不会被提升到作用域顶部.因此,如果在声明之前访问这些变量,就会引发错误. 例如: if (condition) { console.log(typeof value); // 引用错误 let value = "blue&

JavaScript面试题总结系列(四)

4.JavaScript原型和原型链 4.1 概念介绍 原型和原型对象 原型也是对象,叫原型对象.所以,原型和原型对象是一回事,只不过有人说的简洁,有人说的是全称. ? 原型链 原型(原型对象).构造函数和(对象)实例构成了原型链.原型链的核心就是依赖对象的__proto__指向,当检索依赖对象本身不存在的属性时,就会一层一层地向上去查找创建对象的构造函数,一直找到Object,就没有__proto__的指向了. ? 构造函数 首字母大写的函数.例如: ```javascript functio

JavaScript面试题总结系列(五)

5.JavaScript继承 继承是什么 A对象通过继承B对象,就能直接拥有B对象的所有属性和方法. 方式一.原型链继承 核心:子类型的原型为父类型的一个实例对象 // 创建一个父类(注意其本质仍然是一个构造函数) function Parent() { this.name='kevin'; } // 创建父类上的一个共享方法 Parent.prototype.getName = function(){ console.log(this.name); } // 创建子类 function Chi

JavaScript面试题总结系列(六)

6.JavaScript作用域 作用域 概念 简单地说,作用域就是在上下文中,变量与函数的可访问范围.它可以分为块级作用域和函数作用域. 作用 作用域控制着变量与函数的可见性和生命周期. 作用域链 概念 作用域链可以理解为一组对象列表,包含父级和自身的变量对象,因此我们便能通过作用域链访问到父级里声明的变量或者函数. 作用 作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的 组成 由两部分组

JavaScript面试题总结系列(七)

7.JavaScript闭包 概念 闭包是函数,是能够读取其他函数内部变量的函数.例如:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包. 特性 函数内部再嵌套函数 内部函数可以引用外层函数的参数和变量 内部函数的参数和变量不会被垃圾回收机制回收 作用 读取函数内部的变量 让这些变量始终保持在内存中,实现缓存 避免全局变量的污染 使用闭包主要是为了设计私有的方法和变量,即封装对象的私有属性和私有方法,实现封装 利用闭包可以突破作用链域 缺点 消耗内存.

理解javascript原型和作用域系列(8)——简述【执行上下文】

什么是“执行上下文”(也叫做“执行上下文环境”)?暂且不下定义,先看一段代码: 第一句报错,a未定义,很正常.第二句.第三句输出都是undefined,说明浏览器在执行console.log(a)时,已经知道了a是undefined,但却不知道a是10(第三句中). 在一段js代码拿过来真正一句一句运行之前,浏览器已经做了一些“准备工作”,其中就包括对变量的声明,而不是赋值.变量赋值是在赋值语句执行的时候进行的.可用下图模拟: 这是第一种情况. 下面还有.先来个简单的. 有js开发经验的朋友应该

Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送

Android高效率编码-第三方SDK详解系列(三)--JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送 很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送,写这个系列真的很要命,你要去把他们的API文档大致的翻阅一遍,而且各种功能都实现一遍,解决各种bug各种坑,不得不说,极光推送真坑,大家使用还是要慎重,我们看一下极光推送的官网 https://www.jpush.cn/common/ 推送比较使用,很多软件有需要,所以在这个点拿出来多讲讲,我们本节

Javascript面试题解析

Javascript的一些面试题让很多同学感到头疼,下面就根据兄弟连教育(www.lampbrother.net)毕业学员面试遇到的面试题,给大家做一个简单的分享,希望对初入职场的你们有一些帮助:Javascript面试题解析. 第一题 /* 解析: + 优先级大于 ? 此题等价于: 'Value is true' ? 'Something' : 'Nothing' 所以结果是:'Something' */ var val = 'smtg'; console.log('Value is ' +

javascript 面试题

面试题: 1.下面代码运行结果 var bl = false; $.ajax(url, { //... success: function(){ bl = true; } }); while ( !bl ) { alert( bl ); } alert( bl ); // 结果大家自己去想,这道题是面试官临时写的,简单直接,很好的考察你是否理解javascript中的运行机制,很棒的一道面试题 2.扩展Array原型,编写一个去除数组重复项的方法 // 算法一,两层循环,性能差 Array.pr