JavaScript前端面试题

  1. 一、JavaScript的组成(弱类型脚本编程语言)
    1. 1、ECMAScript(核心):
    2. 2、DOM(文档对象模型):
    3. 3、BOM(浏览器对象模型):
  2. 二、JS的基本数据类型和引用数据类型有哪些?有什么区别?
    1. 1、基本数据类型:
    2. 2、引用数据类型:
    3. 3、区别:
  3. 三、ES6语法你知道哪些,分别怎么用?
    1. 1、let 与 const:
    2. 2、箭头函数:
    3. 3、函数的默认参数:
    4. 4、解构赋值:
    5. 5、模板字符串:
    6. 6、扩展运算符(…):
    7. 7、对象的简易写法:
    8. 8、Map、Set 和 Array.from:
    9. 9、Symbol 数据类型(枚举):
    10. 10、export 与 import:
    11. 11、Promise 对象:
    12. 12、class继承:
    13. 13、导入导出规则:
  4. 四、let && const && var的区别是什么?
    1. 1、区别:
  5. 五、解释什么是回调函数?有哪些场景可以使用回调函数?并提供一个简单的例子
    1. 1、解释:
    2. 2、使用场景:
    3. 3、例子(在fn1函数外面打印msg):
  6. 六、Promise、Promise.all、Promise.race 分别怎么用?有什么优缺点?(详解)是同步执行还是异步执行?那么 then 方法呢?
    1. 1、Promise
    2. 2、Promise.all
    3. 3、Promise.race
    4. 4、优点:
    5. 5、缺点:
    6. 5、同步还是异步:
  7. 七、Async / Await怎么用?有什么优点和缺点?如何捕获异常?如何通过同步的方式实现异步?
    1. 1、怎么用(如何通过同步的方式实现异步)?
    2. 2、优点:
    3. 3、缺点:
    4. 4、如何捕获异常?
  8. 八、详解JavaScript中作用域和作用域链?
    1. 1、全局作用域:在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
    2. 2、局部作用域:
    3. 3、块级作用域:
    4. 3、作用域链:
  9. 九、JavaScript闭包是什么?有什么优缺点?为什么要用它?用let是怎么实现的?举例说明?
    1. 1、闭包作用(优点):
    2. 2、闭包形成:
    3. 4、缺点:
    4. 5、应用场景:
    5. 6、let实现闭包:
  10. 十、JavaScript中常见的内存泄漏有哪些?垃圾回收机制是什么
    1. 1、常见的内存泄露:
    2. 2、垃圾回收机制
  11. 十一、JavaScript如何实现深拷贝和浅拷贝?有什么区别?
    1. 1、理解(区别):
    2. 2、实现浅拷贝:
    3. 3、实现深拷贝:
  12. 十二、JavaScript中常用的数组操作方法有哪些?
    1. 2、push && pop 方法:会改变原数组
    2. 4、shift && unshift 方法:会改变原数组
    3. 5、splice 方法:会改变原数组,返回被截取的数组
    4. 6、sort 方法:会改变原数组,返回排序后的数组
    5. 7、concat 方法:不会改变原数组,返回新的数组
    6. 9、join 方法:不会改变原数组,返回新的字符串
    7. 10、indexOf && lastIndexOf() 方法:
    8. 11、reverse 方法:
    9. 12、includes(值)方法:
    10. 13、Array.from(数据)方法:
    11. 14、Array.of() 方法:
    12. 14、map() 方法:
    13. 15、set() 方法:
    14. 16、filter() 方法:
    15. 17、some() 方法:
    16. 18、every() 方法:
    17. 19、reduce() && reduceRight() 方法:
    18. 21、valueOf() 方法:不会改变原数组
    19. 22、forEach() 遍历数组:
    20. 23、find() && findIndex() 方法:
  13. 十三、JavaScript中如何实现数组去重?
    1. 1、set && […new Set(arr)] 去重:
    2. 2、for 嵌套 for,然后splice去重:
    3. 3、indexOf 去重:
    4. 4、sort() 去重:
    5. 5、includes 去重:
    6. 6、利用 hasOwnProperty 去重:
    7. 7、filter 去重:
    8. 8、Map数据结构去重:
    9. 9、reduce && includes:
  14. 十四、JavaScript中扁平化数组方法有哪些?
    1. 1、ES6 的flat():
    2. 2、扩展运算符(…):
    3. 3、JSON 方法:
    4. 4、递归:
    5. 5、reduce() 递归:
  15. 十五、判断数组类型的方法有哪几种?请分别介绍它们之间的区别和优劣?
    1. 1、Object.prototype.toString.call():
    2. 2、 instanceof:
    3. 3、Array.isArray():
    4. 4、性能:
    5. 5、typeof:
  16. 十六、JavaScript中对this理解?有哪几种方法可以改变this指向?call 和 apply 哪个性能更好一些?
    1. 1、this 理解:
    2. 2、改变this指向的方法:
    3. 3、call 和 apply性能比较:
  17. 十七、localStorage 与 sessionStorage 与 cookie 的区别总结?
    1. 1、localStorage:
    2. 2、sessionStorage:
    3. 3、cookie:
  18. 十八、JavaScript中事件有哪三个阶段?事件冒泡如何阻止它?
    1. 1、捕获阶段:
    2. 2、目标阶段:
    3. 3、冒泡阶段:
    4. 4、阻止事件冒泡:
  19. 十九、同步和异步的区别?异步解决方案的发展历程以及优缺点?
    1. 1、同步和异步的区别:
    2. 2、回调函数解决(callback):
    3. 3、Promise 解决:
    4. 4、Generator 解决:
    5. 5、 Async/await 解决:
  20. 二十、模块化的引入与导出 (commonJS规范 和ES6规范)
    1. 1、commonJS 规范:
    2. 2、ES6 规范:
  21. 二十一、函数防抖和函数节流怎样实现?分别的应用场景?有什么区别?如何实现?
    1. 1、函数防抖理解:
    2. 2、函数防抖应用场景:
    3. 3、函数节流理解:
    4. 4、函数节流的应用场景:
    5. 5、相同点和区别:
  22. 二十二、JavaScript常用设计模式有哪几种?
    1. 1、发布订阅模式:
    2. 2、单例模式:
    3. 3、构造函数模式:
    4. 4、工厂模式:
    5. 5、模块模式:
    6. 6、混合模式:
  23. 二十三、JavaScript绑定事件的几种方法?
    1. 1、直接绑定在 dom 上:
    2. 2、使用 onclick:
    3. 3、使用 addEventListener:
  24. 二十四、for、for…of、for…in、forEach有什么区别?
    1. 1、区别:
    2. 2、for循环:
    3. 3、forEach循环:
    4. 4、for in 循环:
    5. 5、for of 循环:
  25. 二十五、JavaScript中常见字符串的操作方法?
    1. 1、length:
    2. 2、indexOf() && lastIndexOf():
    3. 3、split():
    4. 4、substring():
    5. 5、substr():
    6. 6、slice():
    7. 7、concat():
    8. 8、trim():
    9. 9、replace():
    10. 10、search():
    11. 11、toUpperCase():
    12. 12、toLowerCase():
    13. 13、charAt() && charCodeAt():
  26. 二十六、如何在 JavaScript 中比较两个对象?
  27. 二十七、JavaScript中的递归有什么用?怎么实现?
    1. 1、递归概述:
    2. 2、递归写法:
    3. 3、递归求和:
    4. 4、递归求质数:
  28. 二十八、JavaScript中什么是面向对象编程?面向对象的创建方式有哪几种?
    1. 1、面向对象编程概念:
    2. 2、面向对象编程的特点:
    3. 3、面向对象编程优缺点:
    4. 4、例子:
  29. 二十九、面向对象创建有哪几种方式?
    1. 1、字面量创建:
    2. 2、内置构造函数(实例化创建):
    3. 3、工厂模式创建:
    4. 4、自定义构造函数创建:
    5. 5、原型创建:
  30. 三十、如何实现一个 new(构造函数)?new操作符具体干了什么呢?
    1. 1、什么是构造函数:
    2. 2、构造函数new实现:
    3. 3、过程:
  31. 三十一、如何定义一个类?怎么实例化这个类(new)?
    1. 1、构造函数方式:
    2. 2、原型方式:
  32. 三十二、JavaScript中class是什么?有什么用?
    1. 1、class是什么:
  33. 三十三、JavaScript如何实现继承?继承是什么?class继承怎么实现?ES5/ES6 的继承除了写法以外还有什么区别?
  34. 三十四、详解JavaScript中原型和原型链?原型链最终指向哪里 object的原型对象?
  35. 三十五、介绍下深度优先遍历和广度优先遍历,如何实现?
    1. 1、深度优先遍历:
    2. 2、广度优先遍历:
  36. 三十六、Event Loop(事件循环)是什么?setTimeout、Promise、Async/Await 的区别?
    1. 1、Event Loop:
    2. 2、区别:
  37. 三十七、简单理解同步异步、宏任务和微任务
    1. 1、异步任务:
    2. 2、同步任务:
    3. 3、微任务:
    4. 4、宏任务:
    5. 5、执行顺序:
  38. 三十八、如何判断一个对象是否属于某个类?
    1. 1、typeof
    2. 2、instanceof
    3. 3、Object.prototype.toString.call():
  39. 三十九、Javascript中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是?
  40. 四十、对JSON的了解?
    1. 1、理解:
    2. 2、作用:
    3. 3、操作:
  41. 四十一、JavaScript延迟加载的方式有哪些?
    1. 1、使用 setTimeou 延迟方法:
    2. 2、让 JavaScript 最后加载:
    3. 3、defer 属性:

目录

  1. 一、JavaScript的组成(弱类型脚本编程语言)
    1. 1、ECMAScript(核心):
    2. 2、DOM(文档对象模型):
    3. 3、BOM(浏览器对象模型):
  2. 二、JS的基本数据类型和引用数据类型有哪些?有什么区别?
    1. 1、基本数据类型:
    2. 2、引用数据类型:
    3. 3、区别:
  3. 三、ES6语法你知道哪些,分别怎么用?
    1. 1、let 与 const:
    2. 2、箭头函数:
    3. 3、函数的默认参数:
    4. 4、解构赋值:
    5. 5、模板字符串:
    6. 6、扩展运算符(…):
    7. 7、对象的简易写法:
    8. 8、Map、Set 和 Array.from:
    9. 9、Symbol 数据类型(枚举):
    10. 10、export 与 import:
    11. 11、Promise 对象:
    12. 12、class继承:
    13. 13、导入导出规则:
  4. 四、let && const && var的区别是什么?
    1. 1、区别:
  5. 五、解释什么是回调函数?有哪些场景可以使用回调函数?并提供一个简单的例子
    1. 1、解释:
    2. 2、使用场景:
    3. 3、例子(在fn1函数外面打印msg):
  6. 六、Promise、Promise.all、Promise.race 分别怎么用?有什么优缺点?(详解)是同步执行还是异步执行?那么 then 方法呢?
    1. 1、Promise
    2. 2、Promise.all
    3. 3、Promise.race
    4. 4、优点:
    5. 5、缺点:
    6. 5、同步还是异步:
  7. 七、Async / Await怎么用?有什么优点和缺点?如何捕获异常?如何通过同步的方式实现异步?
    1. 1、怎么用(如何通过同步的方式实现异步)?
    2. 2、优点:
    3. 3、缺点:
    4. 4、如何捕获异常?
  8. 八、详解JavaScript中作用域和作用域链?
    1. 1、全局作用域:在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
    2. 2、局部作用域:
    3. 3、块级作用域:
    4. 3、作用域链:
  9. 九、JavaScript闭包是什么?有什么优缺点?为什么要用它?用let是怎么实现的?举例说明?
    1. 1、闭包作用(优点):
    2. 2、闭包形成:
    3. 4、缺点:
    4. 5、应用场景:
    5. 6、let实现闭包:
  10. 十、JavaScript中常见的内存泄漏有哪些?垃圾回收机制是什么
    1. 1、常见的内存泄露:
    2. 2、垃圾回收机制
  11. 十一、JavaScript如何实现深拷贝和浅拷贝?有什么区别?
    1. 1、理解(区别):
    2. 2、实现浅拷贝:
    3. 3、实现深拷贝:
  12. 十二、JavaScript中常用的数组操作方法有哪些?
    1. 2、push && pop 方法:会改变原数组
    2. 4、shift && unshift 方法:会改变原数组
    3. 5、splice 方法:会改变原数组,返回被截取的数组
    4. 6、sort 方法:会改变原数组,返回排序后的数组
    5. 7、concat 方法:不会改变原数组,返回新的数组
    6. 9、join 方法:不会改变原数组,返回新的字符串
    7. 10、indexOf && lastIndexOf() 方法:
    8. 11、reverse 方法:
    9. 12、includes(值)方法:
    10. 13、Array.from(数据)方法:
    11. 14、Array.of() 方法:
    12. 14、map() 方法:
    13. 15、set() 方法:
    14. 16、filter() 方法:
    15. 17、some() 方法:
    16. 18、every() 方法:
    17. 19、reduce() && reduceRight() 方法:
    18. 21、valueOf() 方法:不会改变原数组
    19. 22、forEach() 遍历数组:
    20. 23、find() && findIndex() 方法:
  13. 十三、JavaScript中如何实现数组去重?
    1. 1、set && […new Set(arr)] 去重:
    2. 2、for 嵌套 for,然后splice去重:
    3. 3、indexOf 去重:
    4. 4、sort() 去重:
    5. 5、includes 去重:
    6. 6、利用 hasOwnProperty 去重:
    7. 7、filter 去重:
    8. 8、Map数据结构去重:
    9. 9、reduce && includes:
  14. 十四、JavaScript中扁平化数组方法有哪些?
    1. 1、ES6 的flat():
    2. 2、扩展运算符(…):
    3. 3、JSON 方法:
    4. 4、递归:
    5. 5、reduce() 递归:
  15. 十五、判断数组类型的方法有哪几种?请分别介绍它们之间的区别和优劣?
    1. 1、Object.prototype.toString.call():
    2. 2、 instanceof:
    3. 3、Array.isArray():
    4. 4、性能:
    5. 5、typeof:
  16. 十六、JavaScript中对this理解?有哪几种方法可以改变this指向?call 和 apply 哪个性能更好一些?
    1. 1、this 理解:
    2. 2、改变this指向的方法:
    3. 3、call 和 apply性能比较:
  17. 十七、localStorage 与 sessionStorage 与 cookie 的区别总结?
    1. 1、localStorage:
    2. 2、sessionStorage:
    3. 3、cookie:
  18. 十八、JavaScript中事件有哪三个阶段?事件冒泡如何阻止它?
    1. 1、捕获阶段:
    2. 2、目标阶段:
    3. 3、冒泡阶段:
    4. 4、阻止事件冒泡:
  19. 十九、同步和异步的区别?异步解决方案的发展历程以及优缺点?
    1. 1、同步和异步的区别:
    2. 2、回调函数解决(callback):
    3. 3、Promise 解决:
    4. 4、Generator 解决:
    5. 5、 Async/await 解决:
  20. 二十、模块化的引入与导出 (commonJS规范 和ES6规范)
    1. 1、commonJS 规范:
    2. 2、ES6 规范:
  21. 二十一、函数防抖和函数节流怎样实现?分别的应用场景?有什么区别?如何实现?
    1. 1、函数防抖理解:
    2. 2、函数防抖应用场景:
    3. 3、函数节流理解:
    4. 4、函数节流的应用场景:
    5. 5、相同点和区别:
  22. 二十二、JavaScript常用设计模式有哪几种?
    1. 1、发布订阅模式:
    2. 2、单例模式:
    3. 3、构造函数模式:
    4. 4、工厂模式:
    5. 5、模块模式:
    6. 6、混合模式:
  23. 二十三、JavaScript绑定事件的几种方法?
    1. 1、直接绑定在 dom 上:
    2. 2、使用 onclick:
    3. 3、使用 addEventListener:
  24. 二十四、for、for…of、for…in、forEach有什么区别?
    1. 1、区别:
    2. 2、for循环:
    3. 3、forEach循环:
    4. 4、for in 循环:
    5. 5、for of 循环:
  25. 二十五、JavaScript中常见字符串的操作方法?
    1. 1、length:
    2. 2、indexOf() && lastIndexOf():
    3. 3、split():
    4. 4、substring():
    5. 5、substr():
    6. 6、slice():
    7. 7、concat():
    8. 8、trim():
    9. 9、replace():
    10. 10、search():
    11. 11、toUpperCase():
    12. 12、toLowerCase():
    13. 13、charAt() && charCodeAt():
  26. 二十六、如何在 JavaScript 中比较两个对象?
  27. 二十七、JavaScript中的递归有什么用?怎么实现?
    1. 1、递归概述:
    2. 2、递归写法:
    3. 3、递归求和:
    4. 4、递归求质数:
  28. 二十八、JavaScript中什么是面向对象编程?面向对象的创建方式有哪几种?
    1. 1、面向对象编程概念:
    2. 2、面向对象编程的特点:
    3. 3、面向对象编程优缺点:
    4. 4、例子:
  29. 二十九、面向对象创建有哪几种方式?
    1. 1、字面量创建:
    2. 2、内置构造函数(实例化创建):
    3. 3、工厂模式创建:
    4. 4、自定义构造函数创建:
    5. 5、原型创建:
  30. 三十、如何实现一个 new(构造函数)?new操作符具体干了什么呢?
    1. 1、什么是构造函数:
    2. 2、构造函数new实现:
    3. 3、过程:
  31. 三十一、如何定义一个类?怎么实例化这个类(new)?
    1. 1、构造函数方式:
    2. 2、原型方式:
  32. 三十二、JavaScript中class是什么?有什么用?
    1. 1、class是什么:
  33. 三十三、JavaScript如何实现继承?继承是什么?class继承怎么实现?ES5/ES6 的继承除了写法以外还有什么区别?
  34. 三十四、详解JavaScript中原型和原型链?原型链最终指向哪里 object的原型对象?
  35. 三十五、介绍下深度优先遍历和广度优先遍历,如何实现?
    1. 1、深度优先遍历:
    2. 2、广度优先遍历:
  36. 三十六、Event Loop(事件循环)是什么?setTimeout、Promise、Async/Await 的区别?
    1. 1、Event Loop:
    2. 2、区别:
  37. 三十七、简单理解同步异步、宏任务和微任务
    1. 1、异步任务:
    2. 2、同步任务:
    3. 3、微任务:
    4. 4、宏任务:
    5. 5、执行顺序:
  38. 三十八、如何判断一个对象是否属于某个类?
    1. 1、typeof
    2. 2、instanceof
    3. 3、Object.prototype.toString.call():
  39. 三十九、Javascript中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是?
  40. 四十、对JSON的了解?
    1. 1、理解:
    2. 2、作用:
    3. 3、操作:
  41. 四十一、JavaScript延迟加载的方式有哪些?
    1. 1、使用 setTimeou 延迟方法:
    2. 2、让 JavaScript 最后加载:
    3. 3、defer 属性:

一、JavaScript的组成(弱类型脚本编程语言)

1、ECMAScript(核心):

  • ECMA是(欧洲计算机制造商协会)它规定了 JavaScript 的语法标准。

2、DOM(文档对象模型):

  • DOM 是文档对象模型,规定了文档的显示结构,可以轻松地删除、添加和替换节点。

3、BOM(浏览器对象模型):

  • BOM 是浏览器对象模型,就是浏览器自带的一些功能样式,如搜索框,设置,等学习浏览器窗口交互的对象。


二、JS的基本数据类型和引用数据类型有哪些?有什么区别?

1、基本数据类型:

  • Number、String、Boolean、Null、 Undefined、Symbol

2、引用数据类型:

  • object、Array、Function

3、区别:

  • 声明变量时内存分配不同,基本数据类型存放在栈中,引用数据类型存放在堆内存中的对象。


三、ES6语法你知道哪些,分别怎么用?

1、let 与 const:

2、箭头函数:

3、函数的默认参数:

4、解构赋值:

5、模板字符串:

6、扩展运算符(…):

7、对象的简易写法:

8、Map、Set 和 Array.from:

9、Symbol 数据类型(枚举):

10、export 与 import:

11、Promise 对象:

12、class继承:

13、导入导出规则:


四、let && const && var的区别是什么?

1、区别:

var let const
变量提升 ☑️
全局变量 ☑️
重复声明 ☑️
重新赋值 ☑️ ☑️
暂时死区 ☑️ ☑️
块级作用域 ☑️ ☑️
只声明不初始化 ☑️ ☑️

五、解释什么是回调函数?有哪些场景可以使用回调函数?并提供一个简单的例子

1、解释:

  • JavaScrip t中,回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。

2、使用场景:

  • 资源加载,Ajax 操作回调,图片加载完成执行回调。
  • 事件:DOM 事件及 Node.js 事件基于回调机制

3、例子(在fn1函数外面打印msg):

function fn1(callback){
    setTimeout(function(){
        msg='wait me 3000';
        callback(msg);
    },3000);
}
fn1(data=>{
    console.log(data);//wait me 3000
});

六、Promise、Promise.all、Promise.race 分别怎么用?有什么优缺点?(详解)是同步执行还是异步执行?那么 then 方法呢?

1、Promise

  • Promise 是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。

  • Promise 有三种状态:等待(Pending)、已完成也称执行了(Resolved、fullfilled)和拒绝(Rejected),状态的改变只能是单向的,且变化后不可在改变。

  • Promisethen 方法中的第一个参数,是状态由 pending 变为 resolved 后会调用的回调函数;then 方法中的第二个参数,是状态由 pending 变为 rejected 后调用的回调函数;

  • Promisecatch 方法用于指定发生错误时的回调函数。

  • Promise 支持链式调用

  • Promise 成功时的调用 resolve,失败时的调用 reject

function fn(){
    return new Promise((resolve, reject)=>{
        成功时调用 resolve(数据)
        失败时调用 reject(错误)
    })
}
fn().then(success1, fail1).then(success2, fail2)

2、Promise.all

  • Promise.all() 方法将多个 Promise 实例,包装成一个新的 Promise 实例。
// promise1和promise2都成功才会调用success1
Promise.all([promise1, promise2]).then(success1, fail1)

3、Promise.race

  • Promise.race() 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
// promise1和promise2只要有一个成功就会调用success1
Promise.race([promise1, promise2]).then(success1, fail1)

4、优点:

  • 可以解决回调地狱问题,让回调函数变成了规范的链式写法。
function china() {
    console.log('china中国')
    var p = new Promise(
        function (resolve, reject) {
            console.log('中国  国家')
            resolve('教育大省份')
        }
    )
    return p;
}

function jiangshu(data) {
    console.log('江苏' + data);
    var p = new Promise(function (resolve, reject) {
        console.log('江苏 省份')
        resolve('地级市');
    })
    return p;
}

function xian(data) {
    console.log('盱眙县' + data)
    var p = new Promise(function (resolve, reject) {
        console.log('盱眙县');
        resolve('淮河镇')
    })
    return p;
}

china()
    .then(function (data) { return jiangshu(data) })
    .then(function (data) { return xian(data) })
    .then(function (data) { console.log(data) })

5、缺点:

  • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
  • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段。

5、同步还是异步:

  • promise 构造函数是同步执行的,then 方法是异步执行的。
const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve(5);
    console.log(2);
}).then(val => {
    console.log(val);
});

promise.then(() => {
    console.log(3);
});

console.log(4);

setTimeout(function() {
    console.log(6);
});

// 1  2  4  5  3  6
  • Promise new 的时候会立即执行里面的代码, then 是微任务 会在本次任务执行完的时候执行 setTimeout 是宏任务 会在下次任务执行的时候执行。


七、Async / Await怎么用?有什么优点和缺点?如何捕获异常?如何通过同步的方式实现异步?

1、怎么用(如何通过同步的方式实现异步)?

  • async 作为一个关键字放到函数前面,表示这个函数是一个异步函数,因为 async 就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行。

  • await 是等待的意思,用于等待一个异步方法执行完成,await 关键字只能放到 async 函数里面。

function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

async function test() {
    const v = await takeLongTime();
    console.log(v);
}

test();

2、优点:

  • async / await 的优势在于处理 then 的调用链,能够更清晰准确的写出代码(更像同步函数),并且也能优雅地解决回调地狱问题。

3、缺点:

  • await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低。

4、如何捕获异常?

  • try / catch 可以捕获异常:
async function test(){
    try{
        const res=await test1()
        }catch(err){
            console.log(err)
        }
    console.log("test")
}

八、详解JavaScript中作用域和作用域链?

1、全局作用域:在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:

  • 最外层函数和在最外层函数外面定义的变量拥有全局作用域,例如:
var authorName="山边小溪";
function doSomething(){
    var blogName="梦想天空";
    function innerSay(){
        alert(blogName);
    }
    innerSay();
}
alert(authorName); //山边小溪
alert(blogName); //脚本错误
doSomething(); //梦想天空
innerSay() //脚本错误
  • 所有末定义直接赋值的变量自动声明为拥有全局作用域,例如:
function doSomething(){
    var authorName="山边小溪";
    blogName="梦想天空";
    alert(authorName);
}
doSomething(); //山边小溪
alert(blogName); //梦想天空
alert(authorName); //脚本错误
  • 所有 window 对象的属性拥有全局作用域
一般情况下,window对象的内置属性都拥有全局作用域,例如window.name、window.location、window.top等等。

2、局部作用域:

  • 局部作用域一般只在固定的代码片段内可访问,最常见的例如函数内部:
function doSomething(){
    var blogName="梦想天空";
    function innerSay(){
        alert(blogName);
    }
    innerSay();
}
alert(blogName); //脚本错误
innerSay(); //脚本错误

3、块级作用域:

  • let / const 关键字声明

  • 大括号里面声明,只在大括号内部起作用

if(true){
    var a = 10;//全局变量
    let b = 20;//块级变量(块级作用域 :只在大括号内部起作用)
    console.log(a,b);//10,20

};
console.log(a);//10
// console.log(b);//报错  b is not defined

3、作用域链:

  • 函数去访问变量的时候,会从就近的位置开始去寻找这个变量,直到找到这个变量位置的时候,这个过程叫做作用域链


九、JavaScript闭包是什么?有什么优缺点?为什么要用它?用let是怎么实现的?举例说明?

1、闭包作用(优点):

  • 闭包可以在全局函数里面操作另一个作用域的局部变量。
  • 既能重复使用变量,又不会污染到全局(让这些变量的值始终保持在内存中)。

2、闭包形成:

  • 外层函数嵌套内层函数。

  • 内层函数使用外层函数的局部变量。

  • 把内层函数作为外层函数的返回值。

function bar(){
    //外层函数声明的变量
    var value=1;
    function foo(){
        console.log(value);
    }
    return foo();
};
var bar2=bar;
// 际上bar()函数并没有因为执行完就被垃圾回收机制处理掉
// 就是闭包的作用,调用bar()函数,就会执行里面的foo函数,foo这时就会访问到外层的变量
bar2();

4、缺点:

  • 会增加内存的开销,需注意防止内存泄漏。

5、应用场景:

  • 在函数外使用函数内的变量,函数作为返回值
function F1(){
    var a = 100;
    return function(){
        console.log(a)    
    }
}
var f1 =F1();
var a = 200;
f1() //100
function init(){
    var name = "hello world";//name是一个被init创建的局部变量
    function sayName(){//sayName是一个内部函数,闭包
        alert(name);//使用了父级函数声明的变量name
    }
    sayName();
}
init();//"hello world"
  • 循环为多个按钮绑定监听事件点击每个按钮打印对应的数字(闭包里面的循环)

6、let实现闭包:

  • 循环为多个按钮绑定监听事件点击每个按钮打印对应的数字(因为 let 会形成一个块级作用域(作用死域),且不会存在变量提升)
for(let i=0;i<btn.length;i++){
    btn[i].onclick=function(){console.log(i+1);} 
}    

十、JavaScript中常见的内存泄漏有哪些?垃圾回收机制是什么

1、常见的内存泄露:

  • 意外的全局变量
// bar没有使用var来声明,但是在函数中使用的变量,我们往往是希望只在函数中使用,而一旦函数执行完毕就清除这个变量,但是这种意外的全局变量就会导致直到程序执行完毕,才会释放,即导致了内存泄露。 
function foo(arg) {
    bar = "this is a hidden global variable";
}
  • 循环引用造成的内存泄露

  • 闭包引起的内存泄漏

  • 清理的DOM元素引用

  • 被遗忘的定时器或者回调

2、垃圾回收机制

  • js的垃圾回收机制就是周期性的找出不再使用的变量,然后释放其占用的内存

  • 标记清除法:

    • 这是js最常用的垃圾回收方法,当一个变量进入执行环境时,例如函数中声明一个变量,将其标记为进入环境,当变量离开环境时,(函数执行结束),标记为离开环境
  • 引用计数法:

    • 跟踪记录每个值被引用的次数,声明一个变量,并将引用类型复制给这个变量,则这个值的引用次数+1,当变量的值变成了另一个,则这个值的引用次数-1,当这个值的引用次数为0的时候,就回收


十一、JavaScript如何实现深拷贝和浅拷贝?有什么区别?

1、理解(区别):

  • 假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,就是浅拷贝
  • 如果B没变,那就是深拷贝

2、实现浅拷贝:

  • 例子:
let a=[0,1,2,3,4],
    b=a;
console.log(a===b);
a[0]=1;
console.log(a,b);

3、实现深拷贝:

  • 递归:
//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
    //判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
    var objClone = Array.isArray(obj) ? [] : {};
    //进行深拷贝的不能为空,并且是对象或者是
    if (obj && typeof obj === "object") {
        for (key in obj) {
            if (obj.hasOwnProperty(key)) {
                if (obj[key] && typeof obj[key] === "object") {
                    objClone[key] = deepClone1(obj[key]);
                } else {
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}
  • JSON 对象:无法实现对象中方法的深拷贝
// **注意: 无法实现对象中方法的深拷贝**
//通过js的内置对象JSON来进行数组对象的深拷贝
function deepClone2(obj) {
    var _obj = JSON.stringify(obj),
        objClone = JSON.parse(_obj);
    return objClone;
}
  • Object.assign():当对象只有一级属性为深拷贝,当对象中有多级属性时,二级属性后就是浅拷贝
// 当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。
let obj2 = {name:"zhangsan",info:{num:"001"}};
let resObj = Object.assign({},obj2);
  • jQueryextend 方法:
// $.extend( [deep ], target, object1 [, objectN ] )
// deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
// target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
// object1  objectN可选。 Object类型 第一个以及第N个被合并的对象。 

var array = [1,2,3,4];
var newArray = $.extend(true,[],array);

十二、JavaScript中常用的数组操作方法有哪些?

2、push && pop 方法:会改变原数组

  • push 方法的返回值:添加之后数组的长度
  • pop 方法的返回值:删除掉的那个元素

4、shift && unshift 方法:会改变原数组

  • shift 方法的返回值:删除掉的那个元素

  • unshift 方法的返回值:添加之后的数组长度

5、splice 方法:会改变原数组,返回被截取的数组

  • splice 方法的返回值:以数组形式返回被截取的元素

6、sort 方法:会改变原数组,返回排序后的数组

  • sort 方法的返回值:排序之后的数组

7、concat 方法:不会改变原数组,返回新的数组

  • concat 方法的返回值:返回一个拼接好之后的新数组

9、join 方法:不会改变原数组,返回新的字符串

  • join 方法的返回值:返回链接好的字符串

10、indexOf && lastIndexOf() 方法:

  • indexOf 方法的返回值:如果数组中有这个元素,就返回这个元素的索引,没有就返回 -1

11、reverse 方法:

  • reverse 方法的返回值:返回反转后的数组

12、includes(值)方法:

  • includes 方法的返回值:如果数组中有这个元素,就返回 true,没有就返回 false

13、Array.from(数据)方法:

  • 把 类数组对象/可遍历的对象/字符串 转化为数组

14、Array.of() 方法:

  • 无论有多少参数,参数是什么类型的,Array.of()方法总会创建一个包含所有参数的数组

14、map() 方法:

  • 遍历数组

15、set() 方法:

  • 去重数字

16、filter() 方法:

  • 过滤数组

17、some() 方法:

  • 用于检测数组中的元素是否满足指定条件,如果有一个元素满足,则返回 true,剩余的不会再执行,如果没有,则返回 false

18、every() 方法:

  • 用于检测数组中的元素是否满足指定条件,如果有一个元素不满足,则返回 false,剩余的不会再执行,所有元素都满足条件,则返回 true

19、reduce() && reduceRight() 方法:

  • reduceRight() 方法的功能和 reduce() 功能是一样的,不同的是 reduceRight() 从数组的末尾向前将数组中的数组项做累加

21、valueOf() 方法:不会改变原数组

  • valueOf() 方法返回 Array 对象的原始值

22、forEach() 遍历数组:

  • 循环遍历数组

23、find() && findIndex() 方法:

  • find() 方法返回通过测试(函数内判断)的数组的第一个元素的值。

  • findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。


十三、JavaScript中如何实现数组去重?

1、set && […new Set(arr)] 去重:

  • 不考虑兼容性,这种去重的方法代码最少。这种方法还无法去掉“{}”空对象,后面的高阶方法会添加去掉重复“{}”的方法。
function unique (arr) {
    return Array.from(new Set(arr))
}
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, "NaN", 0, "a", {}, {}]



[...new Set(arr)] 
//代码就是这么少----(其实,严格来说并不算是一种,相对于第一种方法来说只是简化了代码)

2、for 嵌套 for,然后splice去重:

  • 双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
function unique(arr){            
    for(var i=0; i<arr.length; i++){
        for(var j=i+1; j<arr.length; j++){
            if(arr[i]==arr[j]){         //第一个等同于第二个,splice方法删除第二个
                arr.splice(j,1);
                j--;
            }
        }
    }
    return arr;
}
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", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]     // NaN和{}没有去重,两个null直接消失了

3、indexOf 去重:

  • 新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
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 = [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, NaN, "NaN", 0, "a", {…}, {…}]  
// NaN、{}没有去重

4、sort() 去重:

  • 利用 sort() 排序方法,然后根据排序后的结果进行遍历及相邻元素比对。
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))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined]      
// NaN、{}没有去重

5、includes 去重:

  • 检测新数组是否有某个值,没有则 push 进新数组
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])) {//includes 检测数组是否有某个值
            array.push(arr[i]);
        }
    }
    return array
}
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, "NaN", 0, "a", {…}, {…}]     
// {}没有去重

6、利用 hasOwnProperty 去重:

  • 利用 hasOwnProperty 判断是否存在对象属性
function unique(arr) {
    var obj = {};
    return arr.filter(function(item, index, arr){
        return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
    })
}
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, "NaN", 0, "a", {…}] 
// 所有的都去重了

7、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", {…}, {…}]
// {}没有去重

8、Map数据结构去重:

  • 创建一个空 Map 数据结构,遍历需要去重的数组,把数组的每一个元素作为 key 存到 Map 中。由于 Map 中不会出现相同的 key 值,所以最终得到的就是去重后的结果。
function arrayNonRepeatfy(arr) {
    let map = new Map();
    let array = new Array();  // 数组用于返回结果
    for (let i = 0; i < arr.length; i++) {
        if(map .has(arr[i])) {  // 如果有该key值
            map .set(arr[i], true); 
        } else { 
            map .set(arr[i], false);   // 如果没有该key值
            array .push(arr[i]);
        }
    } 
    return array ;
}
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, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

9、reduce && includes:

  • reduce && includes
function unique(arr){
    return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
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, "NaN", 0, "a", {…}, {…}]

十四、JavaScript中扁平化数组方法有哪些?

1、ES6 的flat():

  • 语法: var newArray = arr.flat(depth)depth 指定要拉伸的层数,默认值为1。
var arr = [1,2,[3,4,[5,6,[7,8,[9]]]]]
var res = arr.flat() // [1,2,3,4,[5,6,[7,8,[9]]]]
var res = arr.flat(2) // [1,2,3,4,5,6,[7,8,[9]]]
var res = arr.flat(Infinity) // [1,2,3,4,5,6,7,8,9]

2、扩展运算符(…):

  • 通过 ... 运算符只能解构一层数组,需要循环进行解构,直到数组中的所有数组都解构完成。
var arr = [[1, 2, 8, [6, 7]], 3, [3, 6, 9], 4]
function getNewArr(arr) {
    // 循环数组中的元素判断元素是否为数组
    while (arr.some(item => Array.isArray(item))) {
        // 解构数组
        arr = [].concat(...arr)
    }
    return arr
}
console.log(getNewArr(arr));

3、JSON 方法:

  • 借助 JSON 方法:
let arr = [1, 2, [3, 4, [5, 6, [7, 8, [9]]]]]

let res = JSON.stringify(arr)    //res = [1,2,[3,4,[5,6,[7,8,[9]]]]]    
// 利用正则表达式把[]替换为空字符串
res = res.replace(/\[|\]/g,'')    //res = 1,2,3,4,5,6,7,8,9            
res = '[' + res + ']'            //res = [1,2,3,4,5,6,7,8,9]            
res = JSON.parse(res)            //res =  [1, 2, 3, 4, 5, 6, 7, 8, 9]

4、递归:

  • 递归:
Array.prototype.flatten = function () {
    var resultArr = [];
    var len = this.length;
    for (var i = 0; i < len; i ++) {
        if (Array.isArray(this[i])) {
            resultArr = resultArr.concat(this[i].flatten());
        } else {
            resultArr.push(this[i]);
        }
    }
    return resultArr;
}
var arr=[1,2,3,[4,5,'hello',['world',9,666]]]
console.log(arr.flatten())//[1, 2, 3, 4, 5, "hello", "world", 9, 666]

5、reduce() 递归:

  • reduce:
Array.prototype.flatten = function () {
    return this.reduce(function (prev, cur, curIndex, arr) {
        return Array.isArray(cur) ? prev.concat(cur.flatten()) : prev.concat(cur); 
    }, []);
}

十五、判断数组类型的方法有哪几种?请分别介绍它们之间的区别和优劣?

1、Object.prototype.toString.call():

  • 这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined
Object.prototype.toString.call("An"); // "[object String]"
Object.prototype.toString.call(1); // "[object Number]"
Object.prototype.toString.call(Symbol(1)); // "[object Symbol]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(function () {}); // "[object Function]"
Object.prototype.toString.call({ name: "An" }); // "[object Object]"

2、 instanceof:

  • instanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype
使用 instanceof 判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false。
[] instanceof Array; // true

3、Array.isArray():

  • 专门用来判断对象是否为数组。
var arr =  [1, 2, 3]; 
Array.isArray(arr); // true

4、性能:

  • Array.isArray 的性能最好,instanceoftoString.call 稍微好一点

5、typeof:

typeof 只能检测 基本数据类型,包括 boolean、undefined、string、number、symbol,而 null ,Array、Object ,使用 typeof 出来都是 Object。无法检测具体是哪种引用类型。

十六、JavaScript中对this理解?有哪几种方法可以改变this指向?call 和 apply 哪个性能更好一些?

1、this 理解:

  • this 是函数的内置对象,谁调用这个函数或方法,this 关键字就指向谁
  • 全局作用域或者普通函数中 this 指向全局对象 window
  • 对象调用 对象.函数名( ) 谁调用指向谁 (.前面是谁就指向谁)
  • 在构造函数或者构造函数原型对象中 this 指向构造函数的实例
  • 箭头函数中指向外层作用域的 this

2、改变this指向的方法:

  • call() 方法:
语法:
函数名.call(你要改变的函数的 this 指向, 第二个参数开始,依次是给函数传递的参数)
会直接把函数给调用
第一个参数如果不写或者写一个 null,表示 window

let a = 10;
function fn(b) {
    console.log(this);
    console.log(b);
}
fn.call(a,"gg");  // 直接调用
// Number {10}
// gg
  • apply() 方法:
语法:
函数名.apply(你要改变的函数的 this 指向,第二个参数是一个数组(只能有俩个参数)
数组里面每一项依次是给函数传递参数)
会直接把函数给调用

let a = 10;
function fn(b,c) {
    console.log(this);
    console.log(b);
    console.log(c);
}
fn.apply(a,["bb","cc"]);  // 直接调用
// Number {10}
// bb
// cc
  • bind() 方法:
语法:
函数名.bind(你要改变的函数的 this 指向)
不会立即执行函数
返回值: 就是一个函数(只不过是一个被改变好了 this 指向的函数)
他对函数的参数传递有两个方式
1. 调用返回的函数的时候传递
2. 直接从第二个参数开始依次传递

let a = 10;
function fn(b,c) {
    console.log(this);
    console.log(b);
    console.log(c);
}
var fn1 = fn.bind(a,"bb","cc");  //返回的是改好指向的函数 并没有调用
fn();  //  window   原来函数的指向是不会改变的
fn1();   //Number {10}  

3、call 和 apply性能比较:

  • call 的性能在某些浏览器下要明显比 apply


1、localStorage:

  • localStorage 本地存储技术,存储大小为5MB,如不手动清除永久存在。
  • 不参与服务器端通信

2、sessionStorage:

  • sessionStorage 用于临时保存同一窗口(或标签页)的数据,存储大小为5MB,在关闭窗口或标签页之后将会删除这些数据。
  • 不参与服务器端通信

3、cookie:

  • 生命期为只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。 存放数据大小为4K左右 。有个数限制(各浏览器不同),一般不能超过20个。

  • 与服务器端通信:每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题。


十八、JavaScript中事件有哪三个阶段?事件冒泡如何阻止它?

1、捕获阶段:

  • 从外向内

2、目标阶段:

3、冒泡阶段:

  • 从内向外

4、阻止事件冒泡:

  • 标准的 DOM 方法:
e.stopPropagation();
  • 非标准的方式 老版本的IE支持:
e.cancelBubble = true;

十九、同步和异步的区别?异步解决方案的发展历程以及优缺点?

1、同步和异步的区别:

  • 同步(Synchronization):任务顺序执行,在上一个任务未执行完成之前下一个任务等待执行。
  • 异步(Asynchronous ): 暂时搁置当前任务,等到主线程任务执行完成,再来响应该任务。

2、回调函数解决(callback):

  • 缺点:

    • 回调地狱
    • 不能用 try catch 捕获错误
    • 不能 return
  • 优点:

    • 解决了同步的问题
function doSomething(msg, callback){
    alert(msg);
    if(typeof callback == "function") 
    callback();
  } 
doSomething("回调函数", function(){
    alert("匿名函数实现回调!");
  }); 

3、Promise 解决:

  • Promise 就是为了解决 callback 所产生的问题

  • 优点:

    • 解决了回调地狱
  • 缺点:

    • 无法取消 Promise,错误需要通过 catch 来捕获
axios.get(url1)
    .then(res => {
    //操作
    return axios.get(url2)
}).then(res => {
    //操作
    return axios.get(url3)
}).then(res => {
    //操作
})

4、Generator 解决:

  • 可控制函数的执行
function *fetch() {
    yield ajax('XXX1', () => {})
    yield ajax('XXX2', () => {})
    yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()

5、 Async/await 解决:

  • 异步终极解决方案

  • 优点:

    • 代码清晰,处理了回调地狱的问题
  • 缺点:

    • await 将异步改成同步代码,如果多个异步操作没有依赖性使用 await 会导致性能上的降低。
async function test() {
    // 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
    // 如果有依赖性的话,其实就是解决回调地狱的例子了
    await fetch('XXX1')
    await fetch('XXX2')
    await fetch('XXX3')
}

二十、模块化的引入与导出 (commonJS规范 和ES6规范)

1、commonJS 规范:

  • CommonJS使用 module.exports 和require 来导出、导入模块。
const sum = function(a,b){
    return parseInt(a) + parseInt(b)
}
const subtract = function(a,b){
    return parseInt(a) - parseInt(b)
}

// 导出:
module.exports = {
    sum: sum,
    subtract: subtract,
}

// 引入:
const m = require('./aa.js')

2、ES6 规范:

  • ES6使用 export default 和 import 来导出、导入模块。
// 导出:
export function getList() {
    console.log('获取数据列表')
}


// 引入:
import { getList, save } from "./userApi.js"

二十一、函数防抖和函数节流怎样实现?分别的应用场景?有什么区别?如何实现?

1、函数防抖理解:

触发完事件 n 秒内不再触发,才执行;如果n秒内事件再次被触发,则重新计算时间

1
如: 设置某个函数2s钟内只能执行一次,如果2s内实际触发了大于2次,则2秒内不会执行,会等到最后一次触发结束2s后执行;
function debounce(fn,delay = 100){
    // 创建一个标记用来存放定时器的返回值
    let timer = null  
    return function (){
        // 每当事件触发的时候把前一个 setTimeout清除
        if(timer) clearTimer(timer) 
        // 然后又创建一个新的 setTimeout, 这样就能保证时间间隔内如果事件持续触发,就不会执行 fn 函数
        timer = setTimeout(() => {
            fn.apply(this,arguments)
            timer = null
        },delay)
    }
}

2、函数防抖应用场景:

  • 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力

  • scroll 事件滚动触发事件,scroll一直滚动,则不输出,若停止移动,则1S后输出一次

  • 手机号、邮箱验证输入检测

  • 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染

3、函数节流理解:

持续触发事件,每隔一段时间,只执行一次事件(类似于技能冷却时间)

1
如: 设置某个函数2s钟内只能执行一次,如果2s内实际触发了大于2次,那最多只能执行1次;
function throttle(fn,delay = 100){
  // 通过闭包保存一个标记,相当于一个开关
  let flag = true
  return function () {
    // 在函数开头判断标记定时器是否触发,如果有值return
    if (!flag) return
    flag = false
    setTimeout(() => {
      fn.apply(this, arguments)
      flag = true
    }, delay)
  }
}

4、函数节流的应用场景:

  • 高频点击提交,表单重复提交

  • 搜索联想

  • DOM 元素的拖拽功能实现

  • 计算鼠标移动的距离

5、相同点和区别:

他们都是可以防止一个函数被无意义的高频率调用

都可以通过使用 setTimeout 实现

函数节流:是确保函数特定的时间内至多执行一次

函数防抖:是函数在特定的时间内不被再调用后执行


二十二、JavaScript常用设计模式有哪几种?

1、发布订阅模式:

2、单例模式:

3、构造函数模式:

4、工厂模式:

5、模块模式:

6、混合模式:


二十三、JavaScript绑定事件的几种方法?

1、直接绑定在 dom 上:

<a href="#" onclick="test()">删除</a>    

2、使用 onclick:

document.getElementById("deleteA").onclick = function() {
    alert("删除");
}

3、使用 addEventListener:

document.getElementById("deleteA").addEventListener("click",function(e){
    alert("删除");
});

二十四、for、for…of、for…in、forEach有什么区别?

1、区别:

  • for、for…in、forEach 用于同步线程, for…of 用于异步线程。

  • for、forEach、for…of 都是针对于数组,for…in 既可以用于数组也可用于对象。

2、for循环:

  • 缺点:必须知道循环的次数,必须同索引来拿数据的值

  • 优点:可以随意进行 breakcontinue

let arr = ['taotao', 'huahua', 'laoxie'];
for (var i = 0; i < arr.length; i++) {
    if (arr[i] === 'huahua') {
        // break
        continue
    }
    console.log(arr[i]);
}

3、forEach循环:

  • 缺点:不可以随意进行 breakcontinue

  • 优点:不需要知道循环的系数,可以不通过索引也可以拿到数据

let arr = ['taotao', 'huahua', 'laoxie'];
arr.forEach(function (item, index, arr) {
    // if (item === 'huahua') {
    //     break
    // }
    console.log(item); // 'taotao', 'huahua', 'laoxie'
    console.log(index); // 0 1 2
    console.log(arr); //  ['taotao', 'huahua', 'laoxie']*3
})

4、for in 循环:

  • 遍历对象
var A = {
    a:1,
    b:2,
    c:3,
    d:"hello world"
};  

for(let k in A) {  
    console.log(k,A[k]);  
} 
  • 遍历数组:
var array = [1,2,3,4,5,6,7];  
for(let index in array) {  
    console.log(index,array[index]);  
};  

5、for of 循环:

  • 综合 forforEach 的优点

  • 可以循环有迭代器的数据(Iterator)

  • 普通的对象不可以循环(没有迭代器)

let arr = ['taotao', 'huahua', 'laoxie'];     
for (let key of arr) {
    if (key === 'huahua') continue
    console.log(key);
}


// 不可以循环普通对象
let obj = {
    name: 'zansan',
    age: 18
}
for (let item of obj) {
    console.log(item);
}

二十五、JavaScript中常见字符串的操作方法?

1、length:

  • 获取字符串的长度

2、indexOf() && lastIndexOf():

  • indexOf() 方法返回字符串中指定文本首次出现的索引(位置)
  • lastIndexOf() 方法返回指定文本在字符串中最后一次出现的索引
  • 如果未找到文本, indexOf() 和 lastIndexOf() 均返回 -1

3、split():

  • 通过 split() 将字符串转换为数组:

4、substring():

  • 截取字符串:substring(start, end),无法接受负的索引

5、substr():

  • 截取字符串:substr(start, length)

6、slice():

  • 截取字符串:slice(start, end),可以接受负的索引

7、concat():

  • 拼接字符串

8、trim():

  • 删除字符串中的空白

9、replace():

  • 替换字符串内容

10、search():

  • 查找字符串所在的索引值

11、toUpperCase():

  • 把字符串转换为大写

12、toLowerCase():

  • 把字符串转换为小写

13、charAt() && charCodeAt():

  • charAt() 方法返回字符串中指定下标(位置)的字符串

  • charCodeAt() 方法返回字符串中指定索引的字符 unicode 编码


二十六、如何在 JavaScript 中比较两个对象?

  • 循环遍历两个对象的key和value做比较
function recursiveComparison(objA, objB, flag) {
    for (let key in objA) {
        // 如果objA的key不存在objB中,比较结果为false
        if (!objB.hasOwnProperty(key)) flag = false;
        // 递归调用compareDeeply,进行比较
        flag = compareDeeply(objA[key], objB[key]);
        // 当每一次递归调用递归compareDeeply的结果为false时,跳出循环
        if (!flag) break;
    }
    return flag;
}

const utils = {
    isArray: (value) => Array.isArray(value),
    isObject: (value) => typeof (value) === "object",
    getLength: (value) => {
        if (utils.isArray(value)) return value.length;
        if (utils.isObject(value)) return Object.keys(value).length;
    }
}

function compareDeeply(objA, objB) {
    // 如果objA和objB的类型不全为Object,比较结果为两者严格相等===
    if (!utils.isObject(objA) || !utils.isObject(objB)) return objA === objB;
    // objA和objB都是Object,先判断类型是否全为Object或者Array
    if (objA.constructor !== objB.constructor) return false;
    // objA和objB都是Object,先比较两个obj的长度
    if (utils.getLength(objA) !== utils.getLength(objB)) return false;
    // 执行到此处时,说明要比较的两个对象的长度相等,类型为object / array
    return recursiveComparison(objA, objB, true);
}

export default compareDeeply;

二十七、JavaScript中的递归有什么用?怎么实现?

1、递归概述:

  • 递归是函数中的一种高级用法
  • 自己调用自己就叫递归

2、递归写法:

  • 判断条件,没有判断条件就是死递归
  • 需要执行的代码
  • 变量的更新
  • 函数自己调用自己

3、递归求和:

//递归实现n个数字的和
function getSum(x){
    if(x == 1){
        return 1;
    }
    return x + getSum(x-1);
}
console.log(getSum(5));

4、递归求质数:

function zhishu(a, b) {
    if (a > b) {
        return
    }
    var flag = true;
    for (var i = 2; i <= a - 1; i++) {
        if (a % i === 0) {
            flag = false;
            break;
        }
    }
    if (flag) {
        console.log(a + '是一个质数')
    } else {
        // console.log(a + '不是一个质数')
    }
    a++;
    zhishu(a, b)
}
zhishu(100, 110)

二十八、JavaScript中什么是面向对象编程?面向对象的创建方式有哪几种?

1、面向对象编程概念:

  • 面向对象编程 (OOP)就是在编程过程中把所涉及到的事物分解成一个个的对象,然后由对象之间进行分工与合作。即:
    • 创建对象(对象是谁):
    • 描述对象(难点):
      • 属性:
      • 方法(能做什么?):
    • 操作对象(调用简单):

2、面向对象编程的特点:

  • 封装性
  • 继承性
  • 多态性

3、面向对象编程优缺点:

  • 优点:容易维护、复用、扩展。由于面向对象有封装、继承、多态性,可以设计出低耦合的系统,使系统更加灵活,也更易维护。
  • 缺点:性能相较于面向过程较低。

4、例子:

  • 还是那个🌰,用面向对象的方法来分析如何把大象装进冰箱里?

    对象 方法(行为)
    🐘(大象) 进去
    冰箱 1、打开 2、关闭
  • 在面向对象程序开发的思想中,每一个对象都是一个功能中心,具有明确分工。

let elephant = {
    name: 'daxiang',
    age: 2,
    goIn() {
        console.log('把大象放进冰箱')
    }
}

let refrig = {
    height: 300,
    width: 100,
    open() {
        console.log('打开冰箱')
    },
    close() {
        console.log('关闭冰箱')
    }
}

refrig.open()
elephant.goIn()
refrig.close()

二十九、面向对象创建有哪几种方式?

1、字面量创建:

var person = {
    name: 'jack',
    age: 20,
    say(){
        console.log('我是字面量创建的面向对象')
    }
};

2、内置构造函数(实例化创建):

var person = new Object();
person.name = 'jack';
person.age = 18;
person.run = function(){
    console.log(this.name);
    return this.name  +  this.age;
}
console.log(person.run());

3、工厂模式创建:

function createObject(name,age){
    var person = new Object();
    person.name = name;
    person.age = age;
    person.run = function(){
        return this.name + this.age;
    }
    return person;
}
var box1 = createObject('Lee',100);
var box2 = createObject('jack',200);
alert(box1.run());
alert(box2.run());

4、自定义构造函数创建:

function Fun(name){
    this.name = name;
    this.eat = function(){
        console.log('吃饭了');
    }
}
var s1 = new Fun('mm');
var s2 = new Fun('qiqi');
console.log(s1 instanceof Fun);
console.log(s1.eat == s2.eat);

5、原型创建:

function Str(){};
Str.prototype.name = '小明';
Str.prototype.sex = 'male';
Str.prototype.run = function(){
    console.log('我是一个函数');
}
var obj = new Str();
console.log(obj.name);    
var obj1 = new Str();
console.log(obj.name);
console.log(obj.run == obj1.run);

三十、如何实现一个 new(构造函数)?new操作符具体干了什么呢?

1、什么是构造函数:

  • 首先构造函数也是一个函数,并且任何的函数都可以作为构造函数存在,它的本质是初始化对象。构造函数都是和 new 关键词一起使用的。 new 就是在创建对象,构造函数就是在为初始化的对象添加属性和方法( 成员 )

2、构造函数new实现:

function Func(){
}
let func= new Func();

3、过程:

  • 开辟了一块内存空间 创建了一个新对象
let obj = new Object();
  • 将新建的对象设置为函数中的this

  • 逐个执行函数中的代码

  • 将新建的对象作为返回值

if (typeof(result) == "object"){
    func=result;
}else {
    func=obj;
}

三十一、如何定义一个类?怎么实例化这个类(new)?

1、构造函数方式:

function Car(name,color,price){
this.name=name;
this.color=color;
this.price=price;
this.getColor=function(){
document.write(this.name+”—–”+this.color+”
“);
};
}
var car2 = new Car(“构造桑塔纳”,”red”,”121313”);
car2.getColor();

2、原型方式:

function proCar(){
}
proCar.prototype.name="原型";
proCar.prototype.color="blue";
proCar.prototype.price="10000";
proCar.prototype.getName=function(){
    document.write(this.name+"-----"+this.color+"<br>");
};
var car3=new proCar();
car3.getName();

三十二、JavaScript中class是什么?有什么用?

1、class是什么:

  • class是一个关键字,用来创建一个类


三十三、JavaScript如何实现继承?继承是什么?class继承怎么实现?ES5/ES6 的继承除了写法以外还有什么区别?


三十四、详解JavaScript中原型和原型链?原型链最终指向哪里 object的原型对象?

  • Object.prototype 是所有对象的原型链终点


三十五、介绍下深度优先遍历和广度优先遍历,如何实现?

1、深度优先遍历:

  • 自上而下的遍历搜索
  • 深度优先采用的是堆栈的形式, 即先进后出image.png

2、广度优先遍历:

  • 逐层遍历搜索

  • 广度优先则采用的是队列的形式, 即先进先出。

    image.png


三十六、Event Loop(事件循环)是什么?setTimeout、Promise、Async/Await 的区别?

1、Event Loop:

  • JavaScript 主线程不断的循环往复的从任务队列中读取任务,执行任务,这中运行机制称为事件循环(event loop)

2、区别:

  • setTimeout 属于异步、宏任务。

  • Promise 属于同步任务,(.then)方法属于异步、微任务。

  • Async/Await 属于异步、微任务


三十七、简单理解同步异步、宏任务和微任务

1、异步任务:

  • setTimeoutsetIntervalajax事件绑定等。

2、同步任务:

  • 除了异步任务外的所有任务。

3、微任务:

  • process.nextTickPromise 后的 then 语句和 catch 语句等。

4、宏任务:

  • 除了微任务以外的所有任务(setTimeoutsetInterval)。

5、执行顺序:

  • 先同步再异步,在此基础上先宏任务再微任务


三十八、如何判断一个对象是否属于某个类?

1、typeof

2、instanceof

3、Object.prototype.toString.call():


三十九、Javascript中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是?

  • JavaScripthasOwnProperty 函数方法是返回一个布尔值,指出一个对象是否具有指定名称的属性。此方法无法检查该对象的原型链中是否具有该属性;该属性必须是对象本身的一个成员。
// 如果 object 具有指定名称的属性,那么JavaScript中hasOwnProperty函数方法返回 true,反之则返回 false
object.hasOwnProperty(proName)

四十、对JSON的了解?

1、理解:

  • JSON 是一种轻量级的数据交换格式。
  • JSON 是独立的语言。
  • JSON 易于理解。

2、作用:

  • JSON 是用于存储和传输数据的格式。
  • JSON 通常用于服务端向网页传递数据 。

3、操作:

  • JSON.parse():用于将一个 JSON 字符串转换为 JavaScript 对象。

  • JSON.stringify():用于将 JavaScript 值转换为 JSON 字符串。


四十一、JavaScript延迟加载的方式有哪些?

1、使用 setTimeou 延迟方法:

2、让 JavaScript 最后加载:

  • JavaScript 外部引入的文件放到页面底部,来让 JavaScript 最后引入,从而加快页面加载速度。

3、defer 属性:

  • 表明脚本在执行时不会影响页面的构造,脚本会被延迟到整个页面都解析完毕之后再执行。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 848130454@qq.com

文章标题:JavaScript前端面试题

文章字数:12.5k

本文作者:Spicy boy

发布时间:2019-04-22, 15:08:58

最后更新:2021-03-24, 14:50:29

原始链接:http://www.spicyboy.cn/2019/04/22/JavaScript%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏