2023三月最新精选前端面试题附解答思路

1:如何判断某个字符串的长度

length

2:SPA的理解 single-page application

单页面应用,局部刷新,不利于SEO,速度快,url是哈希模式

3:SSR是什么

服务端渲染,用于SEO

4:尾递归

每次返回一个新的函数,就不带上一个函数的参数,也不用储存桑一个函数,复杂度O(1),不会造成栈溢出

实现方法就是在末尾调用自身,在调用的函数内部执行新的形参

5:js中数组是如何在内存中存储的?

可以通过多种数据结构来实现,数组是以一种哈希映射的方式存储的。
需要注意的是:原始类型的数据放在栈中,对象的数据放在堆中,而数组是特殊的对象

Array.from()方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

6:对数组内的元素进行排序

sort()

7:模块化方案的理解

commonJS和ES Module,主要从用于哪里,用的语法去解释

commonjs的特点:同步/运行时加载,磁盘读取速度快
ES module:在编译就确定了输入输出

8:Iterator、Generator、async await分别是什么,区别在哪里,以及用在什么场景

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。用于for…of遍历

Generator

Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

async await是Generator的语法糖

9:Map和WeakMap的区别

理解弱引用和强引用,就要不要手动回收
Map的键可以是任意类型,WeakMap只接受对象作为键,不接受其它类型的值作为键

Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键;WeakMap的键是弱引用,键所指向的对象是可以被垃圾回收,此时键是无效的。

Map可以被遍历,WeakMap不能被遍历

10:js中如何取消请求

controller.abort(),比如过了多久我就取消请求

11:深拷贝和浅拷贝

地址有没有共享

存在浅拷贝的obj.assign、arr.slice()、arr.concat()、扩展运算符复制

12:JS中顶级对象是Object,Object.prototype: 最顶层的原型对象

13:object与map的区别

从键值对、访问,赋值、删除、获取长度、迭代、去述说

键值对:object只能用字符串当键,map各种类型的值都可以当键

访问:object通过.访问,map通过get()访问

赋值:object通过.键去赋值,map通过set()赋值

删除:object通过delete操作符删除或者=nll,map通过delete()删除

大小:object通过keys()转换成数组再计算,map通过size()即可

迭代:map拥有迭代器

14:pnpm、npm、yarn的区别与优势

npm2 是通过嵌套的方式管理 node_modules 的,会有同样的依赖复制多次的问题。

npm3+ 和 yarn 是通过铺平的扁平化的方式来管理 node_modules,解决了嵌套方式的部分问题,但是引入了幽灵依赖的问题,并且同名的包只会提升一个版本的,其余的版本依然会复制多次。

什么是幽灵依赖?

最主要的一个问题是幽灵依赖,也就是你明明没有声明在 dependencies 里的依赖,但在代码里却可以 require 进来。

这个也很容易理解,因为都铺平了嘛,那依赖的依赖也是可以找到的。

但是这样是有隐患的,因为没有显式依赖,万一有一天别的包不依赖这个包了,那你的代码也就不能跑了,因为你依赖这个包,但是现在不会被安装了。

这就是幽灵依赖的问题。

Fast, disk space efficient package manager

pnpm 则是用了另一种方式,不再是复制了,而是都从全局 store 硬连接到 node_modules/.pnpm,然后之间通过软链接来组织依赖关系。

这样不但节省磁盘空间,也没有幽灵依赖问题,安装速度还快,从机制上来说完胜 npm 和 yarn。


主要围绕npm2嵌套,复制多次依赖,npm3和yarn扁平化,但带来幽灵依赖,pnpm则节省磁盘空间,安装速度快,因为包从全局硬连接到.pnpm,然后软连接到依赖

15:输入url,到页面的画面展示的过程

1、首先,在浏览器地址栏中输入url

2、浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。

3、在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。

4、浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手。

5、握手成功后,浏览器向服务器发送http请求,请求数据包。

6、服务器处理收到的请求,将数据返回至浏览器

7、浏览器收到HTTP响应

8、读取页面内容,浏览器渲染,解析html源码

9、生成Dom树、解析css样式、js交互,渲染显示页面

浏览器下载HTML后,首先解析头部代码,进行样式表下载,然后继续向下解析HTML代码,构建DOM树,同时进行样式下载。当DOM树构建完成后,立即开始构造CSSOM树。理想情况下,样式表下载速度够快,DOM树和CSSOM树进入一个并行的过程,当两棵树构建完毕,构建渲染树,然后进行绘制。

Tips:浏览器安全解析策略对解析HTML造成的影响:

当解析HTML时遇到内联JS代码,会阻塞DOM树的构建,会先执行完JS代码;当CSS样式文件没有下载完成时,浏览器解析HTML遇到了内联JS代码,此时,浏览器暂停JS脚本执行,暂停HTML解析。直到CSS文件下载完成,完成CSSOM树构建,重新恢复原来的解析。

JavaScript 会阻塞 DOM 生成,而样式文件又会阻塞 JavaScript 的执行,所以在实际的工程中需要重点关注 JavaScript 文件和样式表文件,使用不当会影响到页面性能的。

16:白屏优化

  1. DNS解析优化

针对DNS Lookup环节,我们可以针对性的进行DNS解析优化。

  • DNS缓存优化
  • DNS预加载策略
  • 稳定可靠的DNS服务器
  1. TCP网络链路优化

多花点钱吧

  1. 服务端处理优化

服务端的处理优化,是一个非常庞大的话题,会涉及到如Redis缓存、数据库存储优化或是系统内的各种中间件以及Gzip压缩等…

  1. 浏览器下载、解析、渲染页面优化

根据浏览器对页面的下载、解析、渲染过程,可以考虑一下的优化处理:

  • 尽可能的精简HTML的代码和结构
  • 尽可能的优化CSS文件和结构
  • 一定要合理的放置JS代码,尽量不要使用内联的JS代码
  • 将渲染首屏内容所需的关键CSS内联到HTML中,能使CSS更快速地下载。在HTML下载完成之后就能渲染了,页面渲染的时间提前,从而缩短首屏渲染时间;
  • 延迟首屏不需要的图片加载,而优先加载首屏所需图片(offsetTop

因为JavaScript 会阻塞 DOM 生成,而样式文件又会阻塞 JavaScript 的执行,所以在实际的工程中需要重点关注 JavaScript 文件和样式表文件,使用不当会影响到页面性能的

17:预防用户快速点击,造成数据多次提交

通过js设置点击一次后按钮在一段时间内不可点击,一是使用setTimeout,二是使用按钮的disabled属性

18:random()生成的是伪随机数还是真随机数

伪随机数

19:如何判断页面是通过pc端还是移动端访问?

①JS 通过navigator.userAgent属性拿到这个字符串,只要里面包含mobiandroidiphone等关键字,就可以认定是移动设备。

window.screen对象返回用户设备的屏幕信息,该对象的width属性是屏幕宽度(单位为像素)

③第三种方法是侦测屏幕方向,手机屏幕可以随时改变方向(横屏或竖屏),桌面设备做不到。

window.orientation属性用于获取屏幕的当前方向,只有移动设备才有这个属性,桌面设备会返回undefined

④第四种方法是,手机浏览器的 DOM 元素可以通过ontouchstart属性,为touch事件指定监听函数。桌面设备没有这个属性。

⑤css媒体查询

20:map与filter的区别

结合参数是一样的,但是用途不一样去述说

参数:array.map(function(currentValue,index,arr), thisValue)

  • currentValue:数组元素;
  • index:索引
  • arr:原数组;
  • thisValue:作为该执行回调时使用,传递给函数,用作 “this” 的值

用途上:map返回的是数值处理后并且和原数组长度相同的新数组,filer返回的是数值不变但长度不同的新数组

21:map与foreach的区别

map用来返回一个处理后的新数组,foreach不会返回数据,一般用来处理数据

22:页面的滚动距离值

window.pageYOffset、window.scrollY

23:如何顺序执行多个异步任务

通过for循环多次执行包含异步任务的函数,即for+await,通过generator的next()方法

24:promise的三种状态

pending(进行中)、fulfilled(已成功)、rejected(已失败)

25:遍历一个任意长度的list中的元素并依次创建异步任务,如何获取所有任务的执行结果?

Promise.all 需要传入一个数组,数组中的元素都是 Promise 对象。当这些对象都执行成功时,则 all 对应的 promise 也成功,且执行 then 中的成功回调。如果有一个失败了,则 all 对应的 promise 失败,且失败时只能获得第一个失败 Promise 的数据。

Promise.allSettled() 方法返回一个在所有给定的 promise 都已经 fulfilled 或 rejected 后的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果。

26:DOM文档加载的顺序、ready与load执行顺序与区别

1.解析HTML结构

2.加载外部脚本和样式表文件

3.解析并执行脚本代码

4.构造HTML DOM模型 //ready

5.加载图片等外部文件

6.页面加载完毕 //load

这个顺序要记

  • $(window).load()必须等到页面内包括图片的所有元素加载完毕后才能执行(比如图片和媒体资源,它们的加载速度远慢于DOM的加载速度)加载完成之后才执行。$(window).load不能同时编写多个,如果有多个$(window).load(),那么只有最后一个$(window).load()里面的函数或者代码才会执行,之前的$(window).load()都将被覆盖。
  • $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕。但这并不代表页面的所有数据已经全部加载完成,一些大的图片有会在建立DOM树之后很长一段时间才行加载完成。$(document).ready()可以同时编写多个,并且都可以得到执行。

30:script标签放在header和body底部有什么区别

脚本会阻塞页面的渲染,所有放在body底部最好,此时dom树已渲染完成

31:arguments如何转成数组

一是ES6的Array.from()方法,二是for循环将arguments内的内容复制到新数组,三是用rest参数转换

32:如何让Promise.all在抛出异常后依然有效

实现思路:使用map过滤,如果是rejected的就return一个错误值,如果是resolve就原值,返回后的数组再进入all的then方法,继而继续执行

var p1 = new Promise((resolve, reject) => { resolve(‘p1’); });

var p2 = new Promise((resolve, reject) => { resolve(‘p2’); });

var p3 = new Promise((resolve, reject) => { reject(‘p3’); });

Promise.all([p1, p2, p3].map(p => p.catch(e => ‘出错后返回的值’ ))) .then(values => { console.log(values); }).catch(err => { console.log(err); })

33:setTimeout的运行机制

把函数内的代码移出本次执行,等到下一轮事件轮询时再检查,如果时间到了就执行,否则继续等待

事件轮询的工作是监听调用堆栈,并确定调用堆栈是否为空。如果调用堆栈是空的,它将检查消息队列,看看是否有任何挂起的回调等待执行。

在这种情况下,消息队列包含一个回调,此时调用堆栈为空。因此,事件轮询将回调推到堆栈的顶部。

ES6引入了任务队列的概念,任务队列是 JS 中的 promise 所使用的。消息队列和任务队列的区别在于,任务队列的优先级高于消息队列,这意味着任务队列中的promise 作业将在消息队列中的回调之前执行

34:遍历数组的方式

for循环、forEach、for…in、for…of、map、filter

35:模块化的好处

更方便进行代码的检查,可以结合webpack等工具的tree-shaking“树摇”减少代码体积,提升系统性能

36:new.target的理解

new.target属性允许你检测函数或构造方法是否是通过new运算符被调用的。

在通过new运算符被初始化的函数或构造方法中,new.target返回一个指向构造方法或函数的引用。在普通的函数调用中,new.target 的值是undefined。

37:判断数据类型的几种方法

typeof、instanceof、constructor、Array.isArray(),需要联想他们之间的区别

typeof在判断原始类型时,typeof null会等于object。而且对于对象(Object)、数组(Array)来说,都会转换成object

instanceof是通过原型链来判断的,但是对于对象来说,Array也会被转换成Object,而且也不能区分基本类型stringboolean

constructor 判断方法跟instanceof相似,但是constructor检测Object与instanceof不一样,constructor还可以处理基本数据类型的检测,不仅仅是对象类型。

  1. null和undefined没有constructor;
  2. 判断数字时使用(),比如 (123).constructor,如果写成123.constructor会报错
  3. constructor在类继承时会出错,因为Object被覆盖掉了,检测结果就不对了

Array.isArray() 用于确定传递的值是否是一个 Array。如果对象是 Array ,则返回true,否则为false。

38:vue的diff算法

39:如何遍历arguments?

转换为数组,比如用扩展运算符,用map等等

需要补充的是:Arguments 对象的 callee 属性,通过它可以调用函数自身。

40:apply()的用法

改变this的指向,数组传参变为一般传参

41:bind()的用法

bind()方法主要就是将函数绑定到某个对象

42:cookie的有效时间设置为0会怎么样

设置为0即跟随系统默认,其销毁与Session销毁时间相同,即都在浏览器关闭后的特定时间删除。

43:css动画和js实现动画分别有哪些优缺点?

CSS动画

优点
  • 浏览器可以对动画进行优化
  • 代码相对简单,性能调优方向固定
  • 对于帧速表现不好的低版本浏览器,CSS3可以做到自然降级,而JS则需要撰写额外代码
缺点
  • 运行过程控制较弱,无法附加事件绑定回调函数
  • 代码冗长,想用CSS实现稍微复杂一点动画,最后CSS代码都会变得非常笨重

JS动画

优点
  • 控制能力很强, 可以在动画播放过程中对动画进行控制:开始、暂停、回放、终止、取消都是可以做到的。
  • 动画效果比css3动画丰富,有些动画效果,比如曲线运动,冲击闪烁,视差滚动效果,只有js动画才能完成
  • CSS3有兼容性问题,而JS大多时候没有兼容性问题
缺点
  • 代码的复杂度高于CSS动画
  • JavaScript在浏览器的主线程中运行,而主线程中还有其它需要运行的JavaScript脚本、样式计算、布局、绘制任务等,对其干扰导致线程可能出现阻塞,从而造成丢帧的情况

44:实现动画有哪几种思路?

  1. css3的transition 属性
  2. css3的animation 属性
  3. 原生JS动画
  4. 使用canvas绘制动画
  5. SVG动画
  6. Jquery的animate函数
  7. 使用gif图片

45:e.target与e.currentTarget的区别

  • e.target触发事件的元素
  • e.currentTarget绑定事件的元素

46:数组的常用方法

操作方法:

增:

push()方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度,会影响原数组

unshift()

unshift()在数组开头添加任意多个值,然后返回新的数组长度,会影响原数组

splice()

传入三个参数,分别是开始位置、0(要删除的元素数量)、插入的元素,返回空数组,会影响原数组

concat()

首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组,不会影响原始数组

删:

pop()

pop() 方法用于删除数组的最后一项,同时减少数组的 length 值,返回被删除的项,会影响原数组

shift()

shift()方法用于删除数组的第一项,同时减少数组的 length 值,返回被删除的项,会影响原数组

splice()

传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组,会影响原数组

slice()

slice() 用于创建一个包含原有数组中一个或多个元素的新数组,不会影响原始数组

改:

还是用splice()

查:

indexOf()

返回要查找的元素在数组中的位置,如果没找到则返回-1

includes()

返回要查找的元素在数组中的位置,找到返回true,否则false

find()

返回第一个匹配的元素

排序方法:

reverse()

顾名思义,将数组元素方向排列

sort()

sort()方法接受一个比较函数,用于判断哪个值应该排在前面

转换方法:

join() 方法接收一个参数,即字符串分隔符,返回包含所有项的字符串

迭代方法:

some()

对数组每一项都运行传入的函数,如果有一项函数返回 true ,则这个方法返回 true

every()

对数组每一项都运行传入的函数,如果对每一项函数都返回 true ,则这个方法返回 true

forEach()

对数组每一项都运行传入的函数,没有返回值

filter()

对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回

map()

对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组

47:如何区分数组和对象

Array.isArray()、instanceid、constructor、Object.prototype.toString.call

48:如何确保构造函数只能被new调用,而不被普通函数调用

①instanceof

我们可以使用 instanceof 检测某个对象是不是另一个对象的实例,例如 new Person() instanceof Person --> true

  • 通过 new 来调用构造函数,会生成一个新对象,并且把这个新对象绑定为调用函数的 this
  • 如果普通调用函数,非严格模式 this 指向 window,严格模式指向 undefined

使用 new 调用函数和普通调用函数最大的区别在于函数内部 this 指向不同: new 调用后 this 指向实例,普通调用则会指向 window

instanceof 可以检测某个对象是不是另一个对象的实例。如果为 new 调用, this 指向实例,this instanceof 构造函数 返回值为 true ,普通调用返回值为 false

②new.target

《ECMAScript 6 入门》中讲到: ES6new 命令引入了一个 new.target 属性,该属性一般用在构造函数之中,返回 new 命令作用于的那个构造函数。如果构造函数不是通过 new 命令或 Reflect.construct() 调用的,new.target 会返回 undefined因此这个属性可以用来确定构造函数是怎么调用的

49:什么是防抖和节流

  • 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

  • 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

    实现:setTimeout

50:实现图片懒加载

loading="lazy"

60:cookie、localStorage和sessionStorage的区别

生命周期上:

  • cookie:可设置失效时间,没有设置的话,默认是关闭浏览器后失效
  • localStorage:除非被手动清除,否则将会永久保存。(保存用户的cookie)
  • sessionStorage: 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除。

存储数据大小:

  • cookie:4KB左右
  • localStorage和sessionStorage:可以保存5MB的信息。

http请求:

  • cookie:每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题
  • localStorage和sessionStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信

61:什么是同源?如何处理跨域?

协议+域名+端口都一样就要同源,跨域使用CORS

62:CORS的简单请求是哪些?

  • 请求方法是:HEADGETPOST,三者之一
  • 请求头信息不超过以下几个字段:
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-Id
    • Content-Type:值为三者之一application/x-www/form/urlencoded、multipart/form-data、text/plain

63:如何实现多个标签页之间通信?

我只会localStorage和WebSocket

64:什么是事件委托?

“事件委托”即是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。

一个父元素有很多子元素,每个子元素都绑定了同样的函数,那我们就可以把这个函数绑定在父元素上,在点击子元素的时候冒泡到父元素执行,就是一个简单的事件委托

65:promise中的all和allSettled的区别

promise.all出现reject即输出异常的信息,但fullfild的信息也消失了,promise.allSettled不会造成信息丢失

66:for…in和for…of的区别

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

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

67:null和undefined的区别

null是空对象,undefined是未定义

68:具备 Iterator 接口的数据结构有哪些?

原生具备 Iterator 接口的数据结构如下。

Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象

就可以用for…of循环

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for…of循环遍历它的成员。也就是说,for…of循环内部调用的是数据结构的Symbol.iterator方法。

for…of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。

你可能感兴趣的:(前端,java,算法,javascript,数据结构)