面试题之javascript

标签: 面试题 分类: 杂文 创建时间:2021-06-24 01:39:21 更新时间:2023-10-20 11:23:28

这篇面试题,主要来源于网上,当作自己日常学习提高用途

参考文章:
1.「2021」高频前端面试题汇总之JavaScript篇(上) 这个文章总结的很多了,可以时常的看一看,我这里仅仅是摘录了一些,比如:原型和原型链;执行上下文、作用域、必包等等,分门别类,都总结的非常的好
2.Web 中高级前端面试题集合(200+)
3.「2021」高频前端面试题汇总之JavaScript篇(上) 这在掘金上的,感觉央视还是挺好看的。

1.for…in和for…of的区别

for…of 是ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构(数组、对象等)并且返回各项的值,和ES3中的for…in的区别如下

for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
for… in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for … of 只遍历当前对象不会遍历原型链;
对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;

总结: for…in 循环主要是为了遍历对象而生,不适用于遍历数组;for…of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。

2.ajax、axios、fetch的区别

(1)AJAX
Ajax 即“AsynchronousJavascriptAndXML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。它是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面。其缺点如下:

  • 本身是针对MVC编程,不符合前端MVVM的浪潮
  • 基于原生XHR开发,XHR本身的架构不清晰
  • 不符合关注分离(Separation of Concerns)的原则
  • 配置和调用方式非常混乱,而且基于事件的异步模型不友好。

(2)Fetch
fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多。fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。
fetch的优点:

  • 语法简洁,更加语义化
  • 基于标准 Promise 实现,支持 async/await
  • 更加底层,提供的API丰富(request, response)
  • 脱离了XHR,是ES规范里新的实现方式

fetch的缺点:

  • fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
  • fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: ‘include’})
  • fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
  • fetch没有办法原生监测请求的进度,而XHR可以

(3)Axios
Axios 是一种基于Promise封装的HTTP客户端,其特点如下:

  • 浏览器端发起XMLHttpRequests请求
  • node端发起http请求
  • 支持Promise API
  • 监听请求和返回
  • 对请求和返回进行转化
  • 取消请求
  • 自动转换json数据
  • 客户端支持抵御XSRF攻击

3.什么是尾调用,使用尾调用有什么好处?

尾调用指的是函数的最后一步调用另一个函数。代码执行是基于执行栈的,所以当在一个函数里调用另一个函数时,会保留当前的执行上下文,然后再新建另外一个执行上下文加入栈中。使用尾调用的话,因为已经是函数的最后一步,所以这时可以不必再保留当前的执行上下文,从而节省了内存,这就是尾调用优化。但是 ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。

4.常见的DOM操作有哪些?

  • DOM 节点的获取,

    1
    2
    // 按照 css 选择器查询
    var pList = document.querySelectorAll('.mooc')
  • DOM 节点的创建

  • DOM 节点的删除

  • 修改 DOM 元素

5.forEach和map方法有什么区别?

这方法都是用来遍历数组的,两者区别如下:

  • forEach()方法会针对每一个元素执行提供的函数,对数据的操作会改变原数组,该方法没有返回值;
  • map()方法不会改变原数组的值,返回一个新数组,新数组中的值为原数组调用函数处理之后的值;

6.哪些情况会导致内存泄漏?

以下四种情况会造成内存的泄漏:

  • 意外的全局变量: 由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
  • 被遗忘的计时器或回调函数: 设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
  • 脱离 DOM 的引用: 获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
  • 闭包: 不合理的使用闭包,从而导致某些变量一直被留在内存当中。

7.!! 运算符能做什么?

!!运算符可以将右侧的值强制转换为布尔值,这也是将值转换为布尔值的一种简单方法。

1
2
3
4
5
6
7
8
9
10
console.log(!!null); // false
console.log(!!undefined); // false
console.log(!!''); // false
console.log(!!0); // false
console.log(!!NaN); // false
console.log(!!' '); // true
console.log(!!{}); // true
console.log(!![]); // true
console.log(!!1); // true
console.log(!![].length); // false

8.简述前端性能优化

  • 页面内容方面

1.通过文件合并、css 雪碧图、使用 base64 等方式来减少 HTTP 请求数,避免过多的请求造成等待的情况;
2.通过 DNS 缓存等机制来减少 DNS 的查询次数;
3.通过设置缓存策略,对常用不变的资源进行缓存;
4.通过延迟加载的方式,来减少页面首屏加载时需要请求的资源,延迟加载的资源当用户需要访问时,再去请求加载;
5.通过用户行为,对某些资源使用预加载的方式,来提高用户需要访问资源时的响应速度;

  • 服务器方面

1.使用 CDN 服务,来提高用户对于资源请求时的响应速度;
2.服务器端自用 Gzip、Deflate 等方式对于传输的资源进行压缩,减少传输文件的体积;
3.尽可能减小 cookie 的大小,并且通过将静态资源分配到其他域名下,来避免对静态资源请求时携带不必要的 cookie;

9.webpack 中 loader 和 plugin 的区别是什么?

loader:loader 是一个转换器,将 A 文件进行编译成 B 文件,属于单纯的文件转换过程;
plugin:plugin 是一个扩展器,它丰富了 webpack 本身,针对是 loader 结束后,webpack 打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听 webpack 打包过程中的某些节点,执行广泛的任务。

10.如何实现函数的柯里化?

1.什么是函数柯里化?
把接收多个参数的函数变换为接收一个单一参数(最初函数的第一个参数)的函数,并返回接收剩余参数而且返回结果的新函数的技术。JS 函数柯里化的优点:

  • 可以延迟计算,即如果调用柯里化函数传入参数是不调用的,会将参数添加到数组中存储,等到没有参数传入的时候进行调用;
  • 参数复用,当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选;

2.怎么实现?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function curringAdd() {
let args = [].slice.call(arguments, 0);

function add() {
args = [...args, [].slice.call(arguments, 0)];
return add
}

add.toString = function() {
return args.reduce((t, a) => t + +a, 0);
}

return add;
}

console.log(curringAdd(1)(2)(3)) // 6
console.log(curringAdd(1, 2, 3)(4)) // 10
console.log(curringAdd(1)(2)(3)(4)(5)) // 15
console.log(curringAdd(2, 6)(1)) // 9
console.log(curringAdd(1)) // 1

11.webpack 中 loader 和 plugin 的区别是什么?

  • loader
    loader 是一个转换器,将 A 文件进行编译成 B 文件,属于单纯的文件转换过程;

  • plugin
    plugin 是一个扩展器,它丰富了 webpack 本身,针对是 loader 结束后,webpack 打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听 webpack 打包过程中的某些节点,执行广泛的任务。

12.什么是防抖和节流?有什么区别?如何实现?

网友总结的口诀: DTTV(Debounce Timer Throttle Variable - 防抖靠定时器控制,节流靠变量控制)。

  • 防抖
    触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间。
1
2
3
4
5
6
7
8
9
function debounce(fn, timing) {
let timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, timing);
}
}
  • 节流
1
2
3
4
5
6
7
8
9
10
11
function throttle(fn, timing) {
let trigger;
return function() {
if (trigger) return;
trigger = true;
fn();
setTimeout(() => {
trigger = false;
}, timing);
}
}

13.函数内部 arguments 变量有哪些特性,有哪些属性,如何将它转换为数组

  • arguments 所有函数中都包含的一个局部变量,是一个类数组对象,对应函数调用时的实参。如果函数定义同名参数会在调用时覆盖默认对象
  • arguments[index]分别对应函数调用时的实参,并且通过 arguments 修改实参时会同时修改实参
  • arguments.length 为实参的个数(Function.length 表示形参长度)
  • arguments.callee 为当前正在执行的函数本身,使用这个属性进行递归调用时需注意 this 的变化
  • arguments.caller 为调用当前函数的函数(已被遗弃)
  • 转换为数组:var args = Array.prototype.slice.call(arguments, 0);

14.this、apply、call、bind?

1.this 永远指向最后调用它的那个对象。
2.怎么改变 this 的指向:
使用 ES6 的箭头函数;在函数内部使用 _this = this;使用 apply、call、bind;new 实例化一个对象。
3.箭头函数的 this 始终指向函数定义时的 this,而非执行时。
4.bind() 方法会创建一个新函数,当这个新函数被调用时,它的 this 值是传递给 bind() 的第一个参数, 它的参数是 bind() 的其他参数和其原本的参数。

15.es5和es6的区别,说一下你所知道的es6?

ECMAScript5,即ES5,是ECMAScript的第五次修订,于2009年完成标准化ECMAScript6,即ES6,是ECMAScript的第六次修订,于2015年完成,也称ES2015ES6是继ES5之后的一次改进,相对于ES5更加简洁,提高了开发效率ES6新增的一些特性:
(1) let声明变量和const声明常量,两个都有块级作用域ES5中是没有块级作用域的,并且var有变量提升,在let中,使用的变量一定要进行声明
(2) 箭头函数ES6中的函数定义不再使用关键字function(),而是利用了()=>来进行定义
(3) 模板字符串模板字符串是增强版的字符串,用反引号(`)标识,可以当作普通字符串使用,也可以用来定义多行字符串
(4) 解构赋值ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值
(5) for of循环for…of循环可以遍历数组、Set和Map结构、某些类似数组的对象、对象,以及字符串
(6) import、export导入导出ES6标准中,Js原生支持模块(module)。将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用
(7) set数据结构:Set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数
(8) … 展开运算符可以将数组或对象里面的值展开;还可以将多个值收集为一个变量
(9) 修饰器 @decorator是一个函数,用来修改类甚至于是方法的行为。修饰器本质就是编译时执行的函数
(10) class 类的继承:ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念
(11) async、await使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程, 提高代码的简洁性和可读性async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成
(12) promise:Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理、强大
(13) Symbol:Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的
(14) Proxy代理使用代理(Proxy)监听对象的操作,然后可以做一些相应事情

8.setTimeout、Promise、Async/Await 的区别?

事件循环中分为宏任务队列和微任务队列其中setTimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行async函数表示函数里面可能会有异步方法,await后面跟一个表达式async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。

9.isNaN 和 Number.isNaN 函数的区别?

  • 函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。
  • 函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。
参考文章:
1.高频前端面试题汇总之JavaScript篇(上) 自查自纠倒是挺好的。

10.为什么0.1+0.2 ! == 0.3,如何让其相等

这个主要的原因就是浮点数存储的精度问题。一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

参考文章:
1.为什么0.1+0.2 ! == 0.3,如何让其相等? 这里对于上面的这个问题回答的很详细,可以参考。

11.== 操作符的强制类型转换规则

对于 == 来说,如果对比双方的类型不一样,就会进行类型转换。假如对比 x 和 y 是否相同,就会进行如下判断流程:

1.首先会判断两者类型是否相同,相同的话就比较两者的大小;
2.类型不相同的话,就会进行类型转换;
3.会先判断是否在对比 null 和 undefined,是的话就会返回 true
4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断

12.var,letconst的区别是什么?

1.var声明的变量会挂载在window上,而let和const声明的变量不会。

2.var声明变量存在变量提升,let和const不存在变量提升。

3.let和const声明形成块作用域。

4.同一作用域下let和const不能声明同名变量,而var可以。

5.let/const有暂存死区特性。

6.const: 一旦声明必须赋值,不能使用null占位;声明后不能再修改;如果声明的是复合类型数据,可以修改其属性

13.什么是事件冒泡?什么是事件捕获?

1.冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。
2.捕获型事件:事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定)。
3.支持W3C标准的浏览器在添加事件时用addEventListener(event,fn,useCapture)方法,基中第3个参数useCapture是一个Boolean值,用来设置事件是在事件捕获时执行,还是事件冒泡时执行。而不兼容W3C的浏览器(IE)用attachEvent()方法,此方法没有相关设置,不过IE的事件模型默认是在事件冒泡时执行的,也就是在useCapture等于false的时候执行,所以把在处理事件时把useCapture设置为false是比较安全,也实现兼容浏览器的效果。

小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。