JS + ES6 高频面试题合集

目录

js数据类型有哪些

基本数据类型

引用数据类型

内置对象有哪些

数组方法有哪些    

数组去重几种方式

        1--双重for循环

        2--indexOf()方法

        3--splice()方法

        4--filter()方法

        5--利用对象属性不能重复的特点

        6--利用Set结构成员不能重复的特点

        7--利用Map结构成员不能重复的特点

数组深拷贝几种方式

        1--for循环

        2--slice方法

        3--concat方法

        4--扩展运算符

对象深拷贝几种方式

        1--for...in...循环

        2--利用JSON,先转JSON字符串在转JSON对象

        3--扩展运算符

        4--Es6中的Object.assign方法

定时器有几种,有什么区别

区别

说下对Promise的理解

一、Promise是什么?

二、Promise是为解决什么问题而产生的?

三、Promise的两个特点

四、Promise的三个缺点

五、Promise的用法

说下对模块导入导出的理解

箭头函数和普通函数的区别

Js实现约瑟夫环问题(m个人留1个或者留n-1个)

数组方法

递归方法

数组排序的几种写法

数组排序方法sort()排序

冒泡排序(两层循环,两两交换)

选择排序

插入排序


js数据类型有哪些

基本数据类型

        Number 数字类型

        String 字符串类型

        Boolean布尔类型

        undefined未定义

        null 空型

        Symbol 唯一值

引用数据类型

        数组

        函数

        对象

        Set

        Map

内置对象有哪些

        Math:数学对象,数学和函数相关的

        Date:日期对象,处理日期和时间

        Array:数组对象

        String:字符串对象

数组方法有哪些    

        1--unshift------添加元素,在数组最前边添加,返回数组的长度

        2--shift------删除元素,在数组最前边删除,返回删除的元素

        3--reverse------翻转数组,返回一个新数组,会改变原来的数组

        4--sort------排序,默认从大到小排序,改变原来的数组

        5--concat------拼接,如果参数是数组,则拼接数组;如果参数是非数组,在末尾添加元素。返回新数组,原数组不变

        6--slice(start,end)------截取,从索引start开始截取,到end结束,包括start不包括end,原数组不变

        7--splice(start,length)------截取,从索引start截取length个元素,原数组改变

        8--indexOf------查询某元素的索引,从前往后查找第一次出现的索引,没有返回-1

        9--lastIndexOf------查询某元素的索引,从后往前查找第一次出现的索引,没有返回-1

        10--join------转化,通过指定分隔符连接数组所有元素,返回字符串,默认用逗号分隔

        11--forEach------遍历数组,没有返回值

        12--some-----判断元素是否满足条件,有一个满足就返回true,都不满足返回false

        13--every-----判断元素是否满足条件,都满足就返回true,有一个不满足返回false

        14--map-----用于数学计算,返回数组

        15--filter------过滤元素,过滤出符合条件的元素,返回数组

数组去重几种方式

        1--双重for循环

var arrOld = [2, 3, 5, 4, 3, 2, 6, 8, 5, 4, 6, 2];
    var arrNew = [];
    var flag = true;
    for (var i = 0; i < arrOld.length; i++) {
      flag = true;
      for (var j = 0; j < arrNew.length; j++) {
        if (arrNew[j] == arrOld[i]) {
          flag = false;
        }
      }
      if (flag) {
        arrNew.push(arrOld[i]);
      }
    }
    console.log(arrNew);

        2--indexOf()方法

function unique(arrOld) {
  var arrNew = [];
  for (var i = 0; i < arrOld.length; i++) {
    if (arrNew.indexOf(arrOld[i]) === -1) {
    arrNew.push(arrOld[i])
    }
  }
  return arrNew;
}

        3--splice()方法

        4--filter()方法

function unique(arrOld) {
  return arrOld.filter(function(item, index, arrOld) {
    return arrOld.indexOf(item,0) === index;
  });
}

        5--includes()方法

function unique(arr) {
  if (!Array.isArray(arr)) {
    console.log('type error!')
    return
  }
  var array =[];
  for(var i = 0; i < arr.length; i++) {
    if( !array.includes( arr[i]) ) {
      array.push(arr[i]);
    }
  }
  return array
}

        6--利用对象属性不能重复的特点

var arrOld = [2, 3, 5, 4, 3, 2, 6, 8, 5, 4, 6, 2];
var arrNew = [];
var obj = {};
for (var i = 0; i < arrOld.length; i++) {
  obj[arrOld[i]] = 1;
}
for (x in obj) {
  arrNew.push(Number(x));
}
console.log(arrNew);

        7--利用Set结构成员不能重复的特点

function unique (arr) {
  return Array.from(new Set(arr))
}

        8--利用Map结构成员不能重复的特点

        9--拓展运算符 + Set

[...new Set(arr)]

数组深拷贝几种方式

for循环

var arr1 = [1, 2, 3];
var arr2 = [];
for (var i = 0; i < arr1.length; i++) {
    arr2.push(arr1[i]);
}
arr1[0] = 4;
console.log(arr1); //4, 2, 3
console.log(arr2); //1, 2, 3

slice方法

var arr1 = [1, 2, 3];
var arr2 = arr1.slice(0);
arr1[0] = 4;
console.log(arr1); //4, 2, 3
console.log(arr2); //1, 2, 3

concat方法

var arr1 = [1, 2, 3];
var arr2 = arr1.concat();
arr1[0] = 4;
console.log(arr1); //4, 2, 3
console.log(arr2); //1, 2, 3

扩展运算符

var arr1 = [1, 2, 3];
var [...arr2] = arr1;
arr1[0] = 4;
console.log(arr1); //4, 2, 3
console.log(arr2); //1, 2, 3

对象深拷贝几种方式

for...in...循环

使用JSON暴力转换

        通过JSON.stringify() 和 JSON.parse() 将对象转为字符串之后在转为对象。

var obj = {name:'123'};

var obj2 = JSON.parse(JSON.stringify(obj))

      这种简单粗暴的方式有局限性,当值为undefined、function、symbol会在转换过程中被忽略。

使用解构赋值

var obj = {name:'123',age:13};
var obj2 = {...obj}

        只能深度拷贝对象的第一层,如果对象中的属性也是对象的话,没有办法进行深度拷贝的。

使用对象的合并

        利用Object.assign(), 第一个参数必须是空对象

var obj = {name:'123',age:13};
var obj2 = Object.assign({},obj1);

        只能深度拷贝对象的第一层,如果对象中的属性也是对象的话,没有办法进行深度拷贝的。 

利用循环和递归的方式

function deepClone(obj, newObj) {
    var newObj = newObj || {};
    for (let key in obj) {
        if (typeof obj[key] == 'object') {
            newObj[key] = (obj[key].constructor === Array) ? [] : {}
            deepClone(obj[key], newObj[key]);
        } else {
            newObj[key] = obj[key]
        }
    }
    return newObj;
}

定时器有几种,有什么区别

两种,分别是 setTimeoutsetInterval

区别

        1--setTimeout隔一段时间之后执行,只执行一次; setInterval每个一段时间之后执行一次,执行多次

        2--setTimeout用clearTimeout清;除setInterval用clearInterval清除

说下对Promise的理解

一、Promise是什么?

        Promise是最早由社区提出和实现的一种解决异步编程的方案,比其他传统的解决方案(回调函数和事件)更合理和更强大。

        ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
        ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。

二、Promise是为解决什么问题而产生的?

        promise是为解决异步处理回调金字塔问题而产生的

三、Promise的两个特点

        1、Promise对象的状态不受外界影响

                1)pending 初始状态

                2)fulfilled 成功状态

                3)rejected 失败状态

        Promise 有以上三种状态,只有异步操作的结果可以决定当前是哪一种状态,其他任何操作都无法改变这个状态

        2、Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆,只能由 pending变成fulfilled或者由pending变成rejected

四、Promise的三个缺点

        1)无法取消Promise,一旦新建它就会立即执行,无法中途取消
        2)如果不设置回调函数,Promise内部抛出的错误,不会反映到外部
        3)当处于pending状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成

五、Promise的用法

        1、Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

        2、resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

        3、Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

        then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数

六、Promise.race()方法

        Promise.all( )方法:接受一个数组作为参数,数组的元素是Promise实例对象,当参数中的实例对象的状态都为fulfilled时,Promise.all( )才会有返回。

//创建实例pro1
    let pro1 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('实例1操作成功');
        },5000);
    });
    
    //创建实例pro2
    let pro2 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('实例2操作成功');
        },1000);
    });

    
    Promise.all([pro1,pro2]).then(function(result){
        console.log(result);
    });
    //打印结果:["实例1操作成功", "实例2操作成功"]

        Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。

        我们执行某个操作,这个操作需要得到需要多个接口请求回来的数据来支持,但是这些接口请求之前互不依赖,不需要层层嵌套。这种情况下就适合使用Promise.all( )方法,因为它会得到所有接口都请求成功了,才会进行操作。

七、Promise.all()方法

        Promise.race()方法:它的参数要求跟Promise.all( )方法一样,不同的是,它参数中的promise实例,只要有一个状态发生变化(不管是成功fulfilled还是异常rejected),它就会有返回,其他实例中再发生变化,它也不管了

//初始化实例pro1
    let pro1 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('实例1操作成功');
        },4000);
    });

    //初始化实例pro2
    let pro2 = new Promise(function(resolve,reject){
        setTimeout(function () {
            reject('实例2操作失败');
        },2000);
    });

    Promise.race([pro2,pro1]).then(function(result){
        console.log(result);
    }).catch(function(error){
        console.log(error);
    });
    //打印结果:实例2操作失败

         由于pro2实例中2000毫秒之后就执行reject方法,早于实例pro1的4000毫秒,所以最后输出的是:实例2操作失败。

说下对模块导入导出的理解

模块化的初衷

        现在的web系统越来越庞大、复杂,需要团队分工,多人协作,大型系统的javascript文件经常存在复杂的依赖关系,后期的维护成本会越来越高。

         JavaScript模块化正式为了解决这一需求而诞生。

        1--导出: 选择性地给其他模块暴露(提供)自己的属性和方法,供其他模块使用。

        2--导入: 可以根据需要,引入其他模块的提供的属性或者方法,供自己模块使用。

        3--批量导出: export {name,age}   批量导入import {name,age} from “url”;

        4--整体导入: import * as obj from “url”   变量作为obj的属性

        5--重命名导出入的变量:import {旧名字 as 新名字} from “url”;

        6--默认导出:export default 变量 (导出没有名字的变量,只能有一个)

        7--在导入时 import 自定义名 from ”url”   自定义名字不用大括号包住

        8--注意

               1. 声明的变量,对外都是只读的。但是,如果模块B导出的是对象类型的值,就可修改。

               2. 导入不存在的变量,会报错。

箭头函数和普通函数的区别

        1--箭头函数更简洁,不需要function,需要在()后加=> 如果只有一个参数可以省略(),如果函数只有一个return语句,可以省略return 和 {}

        2--箭头函数都是匿名函数,普通函数有匿名函数和具名函数

        3--箭头函数没有arguments,caller,callee;普通函数调用后会有argument对象

        4--箭头函数不能作为构造函数,不能使用new;普通函数可以作为构造函数创建对象实例

        5—箭头函数没有原型prototype属性

        6--箭头函数的this永远指向父作用域,箭头函数通过call和apply调用,不会改变this指向,只会传入参数。普通函数的this指向可以通过call和apply改变。

        7--箭头函数不能作为Generator函数,不能使用yield关键字

        8--箭头函数返回对象时,要加一个小括号

        9--箭头函数在ES6 class中声明的方法为实例方法,不是原型方法

Js实现约瑟夫环问题(m个人留1个或者留n-1个)

数组方法

         function countOff(num, m) {

      let players = [];

      for (let i = 1; i <= num; i++) {

        players.push(i);

      }

      let flag = 0;

      while (players.length > 1) {

        // 剩下一人,结束条件

        let outPlayerNum = 0,

          len = players.length;

        for (let i = 0; i < len; i++) {

          flag++;

          if (flag === m) {

            flag = 0;

            console.log("出局:" + players[i - outPlayerNum]);

            players.splice(i - outPlayerNum, 1);

            outPlayerNum++;

          }

        }

      }

      console.log("剩下:" + players[0]);

    }

递归方法

function countOff(N, M) {

      if (N < 1 || M < 1) {

        return;

      }

      let source = [];

      for (let i = 1; i <= N; i++) {

        source.push(i);

      }

      // const source = Array(...Array(N)).map((_, i) => i + 1);

      let index = 0;

      while (source.length > 1) {

        // 剩下一人,结束条件

        index = (index + M - 1) % source.length;

        console.log("出局:" + source[index]);

        source.splice(index, 1);

      }

      console.log("剩下:" + source[0]);

    }

数组排序的几种写法

数组排序方法sort()排序

冒泡排序(两层循环,两两交换)

选择排序

         首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

        再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

        重复第二步,直到所有元素均排序完毕。

插入排序

        将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。

        从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

猜你喜欢

转载自blog.csdn.net/Wr2138/article/details/127975296