web前端面试合集2022版

1. JavaScript有哪些数据类型?

JavaScript共有八种数据类型,分别是 Number、Boolean、String、Undefined、Null、Object、Symbol、BigInt。

2. 如何判断一个数据是NaN

NaN是非数字,但是用 typeof 检测是number类型。

  • 用 typeof 判断是否是number类型并且判断是否满足isNaN
  • 利用 NaN 是唯一一个不等于任何自身的特点 n !== n
  • 利用ES6中提供的 Object.is() 方法(判断两个值是否相等)

3. JS中 null 和 undefined 的区别

相同点:隐式类型转换都是false 不同点:number(null) 是0,number(undefined)是NaN

null表示值被定义了,但是是空值 undefined表示未被定义

4. 闭包是什么?

闭包可以简单被理解成:定义在一个函数内部的函数。其中一个内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

特点:

  • 函数嵌套函数
  • 函数内部可以引用外部的参数和变量
  • 参数和变量不会被垃圾回收机制回收

优点:

  • 变量长期驻扎内存
  • 避免全局变量污染
  • 私有成员的存在

缺点:

  • 使用不当会造成内存泄漏

5. JS中常见的内存泄露

  • 意外的全局变量
  • 被遗忘的计时器或回调函数
  • 脱离DOM的引用
  • 闭包

6. 事件委托是什么?

事件委托就是利用事件冒泡,只定制一个事件处理程序,就可以管理某一类型的所有事件。

事件委托,称为事件代理,是JS中很常用的绑定事件的方法。事件委托就是把原本需要绑定在子元素上面的事件委托给父元素,让父元素担当事件监听的职务,原理是DOM元素的事件冒泡。

7. 什么是事件冒泡

一个事件触发后,会在子元素和父元素之间传播,这种传播分为三个阶段:

  • 捕获阶段(从 window 对象传导到目标节点(从外到里),这个阶段不会响应任何事件)
  • 目标阶段(在目标节点上触发)
  • 冒泡阶段(从目标节点传导回window对象,从里到外。) 事件委托/事件代理就是利用事件冒泡的机制把里层需要响应的事件绑定到外层

8. 本地存储与cookie的区别

相同点:

都是在浏览器(客户端)内存储,存储方式为字符串

不同点:

1、生命周期:

1)cookie如果不设置有效期,就是临时存储(存储在内存中),如果设置了有效期,到期之后会消失(存储在硬盘里)

2)localStorage是永久存储,关闭浏览器数据也不会消失,除非手动删除

3)sessionStorage是临时存储,仅在当前回话下有效。引入了一个窗口的概念,数据仅在当前窗口下生效,关闭当前窗口的话数据会被删除

2、cookie数据在每次网络请求的时候都会被发送给服务端,localStorage和sessionStorage则不会

3、cookie大小限制为4kb,storage则为5M

4、storage比cookie的安全性更高一些

9. let、const和var的区别

  • var声明的变量会挂载在window上,而let和const则不会
  • var声明的变量存在变量提升,而let和const则不会
  • 同一个作用域下var可以声明同名变量,而let和const则不行
  • let和const会生成块级作用域
  • const一旦声明之后不可修改,如果是复杂数据类型的话,可以修改其属性

10. 数组方法

  • push(),从后面添加元素,返回值为添加完元素后的数组长度
  • pop(),从后面删除元素,只能删除一个,返回值为删除的元素
  • shift(),从前面删除元素,只能删除一个,返回值为删除的元素
  • unshift(),从前面添加元素,返回值是添加完元素后的数组长度
  • splice(i, l),删除从i开始的,长度为l的元素,返回值是删除的元素
  • concat(),连接两个数组,返回值为连接后的新数组
  • split(),将字符串转换为数组
  • sort(),将数组进行排序,返回值是排好的数组,默认是按照最左边的数字进行排序,不是按照数字大小排序的
  • reverse(),反转数组,返回值是反转后的数组
  • slice(start, end),切去索引值start到end的数组,不包含end,返回值是切出来的数组
  • forEach(),遍历数组,没有返回值
  • map(),遍历数组,返回值为一个新数组
  • filter(),过滤数组,返回一个满足要求的数组

11. 普通函数与构造函数的区别

  1. 构造函数也是一个普通函数,创建方式和普通函数一致,但是构造函数习惯性首字母大写
  2. 调用方式不一样,普通函数直接调用,构造函数要用new去实例化
  3. 调用时,构造函数内部会创建一个新对象,普通函数不会创建新对象
  4. 构造函数内部的this指向实例,普通函数内部的this指向调用函数的对象,如果没有调用对象,则默认为window
  5. 构造函数默认的返回值是创建的对象,普通函数的返回值由内部的return决定
  6. 构造函数的函数名与类名相同

12. 原型/原型链/继承

什么是原型:

任何对象实例都有一个原型,也叫原型对象。这个原型对象由对象的内置属性_proto_指向它的构造函数的prototype指向的对象,即任何对象都是由一个构造函数创建的,但是不是每一个对象都有prototype,只有方法才有prototype。

什么是原型链

原型链基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法,我们知道,每个构造函数都有一个原型对象,每个原型对象都有一个指向构造函数的指针,而实例又包涵一个指向原型对象的内部指针。

原型链的核心就是依赖对象的_proto_的指向,当自身不存在的属性时,就一层层的扒出创建对象的构造函数,直至到Object时,就没有_proto_指向了。

因为_proto_实质找的是prototype,所以我们只要找这个链条上的构造函数的prototype,其中Object.prototype是没有_proto_属性的,它==null

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含指向原型对象内部的指针。

原型继承是 js 的一种继承方式,原型链作为实现继承的主要方法,其基本思路是利用原型让一个引用类型继承另一个引用类型的属性和方法,

原型继承:利用原型中的成员可以被和其相关的对象共享这一特性,可以实现继承,这种实现继承的方式,就叫做原型继承.

13. Promise的理解

什么是Promise?

我们都知道,Promise是承诺的意思,承诺它过一段时间会给你一个结果

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

从语法上讲,Promise是一个对象,从它可以获取异步操作的消息

Promise的状态

  • pending 等待状态
  • rejected 失败状态
  • fulfiled 成功状态

状态一旦改变,就不会再变

Promise的特点

  • Promise对象不受外界影响
  • Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可逆

Promise的缺点

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

14. 请简述async的用法

async就是Generation和Promise的语法糖,async就是将Generator的*转换成async,将yield转换成await

函数前必须加一个async,异步操作方法前加一个await关键字,意思就是等一下,执行完了再继续走。注意:await 只能在 async 函数中运行,否则会报错

Promise 如果返回的是一个错误的结果,如果没有做异常处理,就会报错,所以用 try..catch 捕获一下异常就可以了

15. 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?

当发送一个URL请求时,不管这个URL是web页面URl还是Web页面上每个资源的URL,浏览器都会开启一个线程来处理这个请求,同时在远程DNS服务器上启动一个DNS查询。这能使得浏览器得到请求对应的IP地址。

浏览器与远程web服务器通过TCP三次握手协商来建立一个TCP/IP连接,该握手包括一个同步报文,一个同步-应答报文和一个应答报文。这三个报文在浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,然后服务器相应并接受客户端的请求,最后由客户端发出该请求已被接受的报文。

一旦TCP/IP连接建立,浏览器会通过该连接向远程服务器发送HTTP的GET请求。远程服务器找到资源并使用HTTP相应返回该资源

16. GET请求传参长度的误区

我们常说GET请求参数的大小存在限制,而POST请求的参数大小是无限制的。

实际上HTTP协议从未规定GET/POST的请求长度是多少,对GET请求参数的限制是来源与浏览器或WEB服务器,浏览器或WEB服务器限制了url的长度。为了明确这个概念,我们必须强调下面几点。

  • HTTP协议未规定 GET 和 POST 的长度限制
  • GET 的最大长度显示是因为浏览器和web服务器限制了 URI 的
  • 长度不同的浏览器和 WEB 服务器,限制的最大长度不一样要支持
  • IE,则最大长度为 2083byte,若只支持 Chrome,则最大长度8182byte

17. 说说前端中的事件流

HTML 与 JS 交互是通过事件驱动来实现的,例如鼠标点击事件 onclick、页面滚动事件 onscroll 等等。可以向文档或者文档中的元素添加事件监听来订阅事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下 ”事件流“ 的概念。

什么是事件流

事件流描述的是从页面中接收事件的顺序,DOM2 级事件流包括下面几个阶段:

事件捕获阶段处于目标阶段事件冒泡阶段

addEventListener:addEventListener 是 DOM2 级事件新增的指定事件处理程序的操作,这个方法接收 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。IE只支持事件冒泡。

18. 说一下事件委托

事件委托是指,不在事件的发生地(直接DOM)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素DOM的类型,来做出不同的相应。

举例:最经典的就是ul和li标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在li标签上直接添加,而是在父元素ul上面添加

好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。

18. JS的new操作符做了哪些事情

new操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象。

19. 改变函数内部 this 指针的函数(bind、apply、call的区别)

通过 apply 和 call 改变this指向,他们两个函数的第一个参数都是一样的表示要改变指向的那个对象,第二个参数:apply是数组,而call则是arg1,arg2...这种形式。通过bind改变this作用域会返回一个新的函数,这个函数不会马上执行。

20. JS中各种位置clientHeight、scrollHeight、offsetHeight、以及scrollTop、offsetTop、clientTop的区别?

  • clientHeight:表示的是可视区域的高度,不包含border和滚动条。
  • offsetHeight:表示可视区域的高度,包含了border和滚动条。
  • scrollHeight:表示了所有区域的高度,包含了因为滚动被隐藏的部分。
  • clientTop:表示边框border的厚度,在未指定的情况下一般为0。
  • scrollTop:滚动后被隐藏的高度,获取对象相对于由offsetParent属性指引的父坐标(CSS定位的元素或body元素)距离顶端的高度

21. JS中的垃圾回收机制

必要性: 由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JS程序每次创建字符串、数组或者对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则JS的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。JS的解释器可以检测到何时程序不再使用一个对象了,当它确定了一个对象是无用的时候,它就知道不再需要这个对象,可以把它所占用的内存释放掉了。

例如:

var a = "hello word";
var b = "hello wrod";
var a = b;
复制代码

这时,会释放掉"hello word",释放内存以便再引用垃圾回收的方法:标记清除、计数引用。

标记清除:

这时最常见的垃圾回收方式,当变量进入环境时,就标记这个变量为”进入环境“,从逻辑上讲,永远不能释放进入环境的变量所占用的内存,只要执行流程进入相应的环境,就可能用到他们。当离开环境时,就标记为离开环境。垃圾回收器在运行的时候会给存储在内存中的变量都加上标记,然后去掉环境变量中的变量,以及被环境变量中的变量所引用的变量(条件性去除标记),删除所有被标记的变量,删除的变量无法在环境变量中被访问所以会被删除,最后垃圾回收器完成了内存的清除工作,并回收他们所占用的内存。

引用计数法:

另一种不太常见的方法就是引用计数法,引用计数法的意思就是每个值被引用的次数,当声明了一个变量,并用一个引用类型的值赋值给变量,则这个值的引用次数为 1,相反的,如果包含了对这个值引用的变量又取得了另外一个值,则原先的引用值引用次数就减 1,当这个值的引用次数为 0时,说明没有办法再访问这个值了,因此就把所占的内存给回收进来,这样垃圾收集器再次运行的时候,就会释放引用次数为 0 的这些值。

用引用计数法会存在内存泄漏:

function problem {
    var objA = new Object();
    var objB = new Object();
    
    objA.a = objB;
    objB.b = objA;
}
复制代码

在这个栗子里面,objA和objB通过各自的属性互相引用,这样的话,两个对象的引用次数都为 2,在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成后,因为计数不为 0,这样的相互引用如果大量存在就会导致内存泄漏。

特别是在DOM对象中,也容易存在这种问题,

var element = document.getElementById('');
var myObj = new Object();

myObj.element = element;

element.a = myObj;
复制代码

这样就不会有垃圾回收的过程。

22. 监听对象属性的改变

在ES5里面可以通过Object.defineProperty来实现已有属性的监听

Object.defineProperty(user, 'name', {
    set: function(key, value) {
        
    }
})
复制代码

缺点:如果id不在user对象中,则不能监听id的变化。

在ES6中可以通过Proxy来实现

var user = new Proxy({}, {
    set: function(target, key, value, receiver) {
        
    }
});
复制代码

这样即使有属性在Proxy中不存在,通过user.id来定义也同样可以这样监听这个属性的变化

23. 自己实现一个bind函数

原理:通过apply或者call方法来实现

(1) 初始版本

Function.prototype.bind = function(obj, arg) {
    var arg = Array.prototype.slice.call(arguments, 1);
    var context = this;
    return function(newArg) {
        arg = arg.concat(Array.prototype.slice.call(newArg));
        return context.apply(obj, arg);
    }
}
复制代码

(2) 考虑到原型链(因为在new一个bind生成的新函数的时候,必须的条件是要继承原函数的原型)

Function.prototype.bind = function(obj, arg) {
    var arg = Array.prototype.slice.call(arguments, 1);
    var content = this;
    var bound = function(newArg) {
        arg = arg.concat(Array.prototype.slice.call(newArg));
        return context.apply(obj, arg);
    }
    var F = function() {}
    F.prototype = context.prototype;
    bound.prototype = new F();
    return bound;
}

前端部分知识:

浏览器

输入一个URL到页面过程中发生了什么(高频)

  1. 首先在浏览器中输入URL
  2. 查找缓存:浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,如果有则显示页面内容。如果没有则进行下一步。
  3. DNS域名解析:浏览器向DNS服务器发起请求,解析该URL中的域名对应的IP地址。DNS服务器是基于UDP的,因此会用到UDP协议。
  4. 建立TCP连接:解析出IP地址后,根据IP地址和默认80端口,和服务器建立TCP连接
  5. 发起HTTP请求:浏览器发起读取文件的HTTP请求,,该请求报文作为TCP三次握手的第三次数据发送给服务器
  6. 服务器响应请求并返回结果:服务器对浏览器请求做出响应,并把对应的html文件发送给浏览器
  7. 关闭TCP连接:通过四次挥手释放TCP连接
  8. 浏览器渲染:客户端(浏览器)解析HTML内容并渲染出来,浏览器接收到数据包后的解析流程为:
  • 构建DOM树:词法分析然后解析成DOM树(dom tree),是由dom元素及属性节点组成,树的根是document对象
  • 构建CSS规则树:生成CSS规则树(CSS Rule Tree)
  • 构建render树:Web浏览器将DOM和CSSOM结合,并构建出渲染树(render tree)
  • 布局(Layout):计算出每个节点在屏幕中的位置
  • 绘制(Painting):即遍历render树,并使用UI后端层绘制每个节点。

浏览器渲染机制、重绘、重排

网页生成的过程:

  • 构建DOM树:词法分析然后解析成DOM树(dom tree),是由dom元素及属性节点组成,树的根是document对象
  • 构建CSS规则树:生成CSS规则树(CSS Rule Tree)
  • 构建render树:Web浏览器将DOM和CSSOM结合,并构建出渲染树(render tree)
  • 布局(Layout):计算出每个节点在屏幕中的位置
  • 绘制(Painting):即遍历render树,并使用UI后端层绘制每个节点。

重排:当DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。

触发条件:

  1. 添加或者删除可见的DOM元素

2. 元素尺寸改变——边距、填充、边框、宽度和高度

重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。

触发条件:改变元素的color、background、box-shadow等属性

HTML相关

常见的语义化标签

header nav main article section aside footer

语义化的优点如下:

  • 对机器友好,带有语义的文字表现力丰富,更适合搜索引擎的爬虫爬取有效信息,有利于SEO。除此之外,语义类还支持读屏软件,根据文章可以自动生成目录;
  • 对开发者友好,使用语义类标签增强了可读性,结构更加清晰,开发者能清晰的看出网页的结构,便于团队的开发与维护

H5新特新

  1. 语义化标签
  2. 媒体标签
  3. 表单
  4. DOM查询操作
  5. Web存储
  6. 拖拽
  7. ...

script标签中defer和async的区别

多个带async属性的标签,不能保证加载的顺序;多个带defer属性的标签,按照加载顺序执行;

async属性,表示后续文档的加载和执行与js脚本的加载和执行是并行进行的,即异步执行;defer属性,加载后续文档的过程和js脚本的加载(此时仅加载不执行)是并行进行的(异步),js脚本需要等到文档所有元素解析完成之后才执行,DOMContentLoaded事件触发执行之前。

CSS相关

盒模型(高频)

盒模型都是由四个部分组成的,分别是margin、border、padding和content。

  • 标准盒模型的width和height属性的范围只包含了content,
  • IE盒模型的width和height属性的范围包含了border、padding和content。

flex常用属性(高频)

1.flex-direction属性决定主轴的方向(即项目的排列方向)。

2. flex-wrap属性定义,如果一条轴线排不下,如何换行。

3.flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

4.justify-content属性定义了项目在主轴上的对齐方式。

5.align-items属性定义项目在交叉轴上如何对齐。

6.align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

position属性

  1. 固定定位 fixed: 元素的位置相对于浏览器窗口是固定位置,即使窗口是滚动的它也不会移动。Fixed 定 位使元素的位置与文档流无关,因此不占据空间。 Fixed 定位的元素和其他元素重叠。
  2. 相对定位 relative: 如果对一个元素进行相对定位,它将出现在它所在的位置上。然后,可以通过设置垂直 或水平位置,让这个元素“相对于”它的起点进行移动。 在使用相对定位时,无论是 否进行移动,元素仍然占据原来的空间。因此,移动元素会导致它覆盖其它框。
  3. 绝对定位 absolute: 绝对定位的元素的位置相对于最近的已定位父元素,如果元素没有已定位的父元素,那 么它的位置相对于。absolute 定位使元素的位置与文档流无关,因此不占据空间。 absolute 定位的元素和其他元素重叠。
  4. 粘性定位 sticky: 元素先按照普通文档流定位,然后相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位。而后,元素定位表现为在跨越特定阈值前为相对定 位,之后为固定定位。
  5. 默认定位 Static: 默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声 明)。 inherit: 规定应该从父元素继承 position 属性的值。

transition和animation的区别

transition是过度属性,强调过度,它的实现需要触发一个事件(比如鼠标移动上去,焦点,点击等)才执行动画。它类似于flash的补间动画,设置一个开始关键帧,一个结束关键帧。
animation是动画属性,它的实现不需要触发事件,设定好时间之后可以自己执行,且可以循环一个动画。它也类似于flash的补间动画,但是它可以设置多个关键帧(用@keyframe定义)完成动画。

CSS3中有哪些新特性

新增各种CSS选择器 (: not(.input):所有 class 不是“input”的节点)

1.圆角 (border-radius:8px)

2.多列布局 (multi-column layout)

3.阴影和反射 (Shadoweflect)

4.文字特效 (text-shadow)

5.文字渲染 (Text-decoration)

6.线性渐变 (gradient)

7.旋转 (transform) 增加了旋转,缩放,定位,倾斜,动画,多背景

隐藏元素的方法有哪些

1.使用display: none; 隐藏dom;

2.使用visibility: hidden; 隐藏dom;

3.使用z-index: -888; 把元素的层级调为负数,然后其他元素覆盖即可;

4.使用opacity: 0; 把元素的透明度调为0,也可以达到隐藏;

5.使用固定定位position: absolute; 把元素定位到看不见的区域;

6.使用transform: scale(0, 0); 把元素缩放为0,也可以实现元素隐藏。

盒子水平垂直居中方法(高频)

1.利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过translate来调整元素的中心点到页面的中心

2.利用绝对定位,设置四个方向的值都为0,并将margin设置为auto,由于宽高固定,因此对应方向实现平分,可以实现水平和垂直方向上的居中。该方法适用于盒子有宽高的情况

3.利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过margin负值来调整元素的中心点到页面的中心。该方法适用于盒子宽高已知的情况

4.使用flex布局,通过align-items:center和justify-content:center设置容器的垂直和水平方向上为居中对齐,然后它的子元素也可以实现垂直和水平的居中

CSS样式优先级

  • !important
  • 内联样式(1000)
  • ID选择器(0100)
  • 类选择器(0010)
  • 元素选择器(0001)
  • 通配符选择器(0000)

JS相关

JS的数据类型

基本类型:

Number、String、Boolean、Null、undefined、Symbol

复杂类型:

Object、Array、Function

JS的类型检测(高频)

typeof

instanceof

Object.prototype.toString.call()

作用域和作用域链

作用域负责收集和维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。(全局作用域、函数作用域、块级作用域)。 作用域链就是从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链。

原型和原型链(高频)

原型:

  • 每个 class都有显示原型 prototype
  • 每个实例都有隐式原型 _ proto_
  • 实例的_ proto_指向对应 class 的 prototype

原型链: 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念

特点: JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

闭包(高频)

闭包是指有权访问另一个函数作用域中的变量的函数 ——《JavaScript高级程序设计》
当函数可以记住并访问所在的词法作用域时,就产生了闭包,
即使函数是在当前词法作用域之外执行 ——《你不知道的JavaScript》

闭包形成的条件:

  1. 函数的嵌套
  2. 内部函数引用外部函数的局部变量,延长外部函数的变量生命周期

闭包用途:

  1. 能够访问函数定义时所在的词法作用域(阻止其被回收)
  2. 私有化变量
  3. 模拟块级作用域
  4. 创建模块

闭包缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏

EventLoop(高频)

JS是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,所以会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列。微任务队列的代表就是,Promise.then,MutationObserver,宏任务的话就是setImmediate setTimeout setInterval

new运算符的实现机制

  1. 首先创建了一个新的空对象
  2. 设置原型,将对象的原型设置为函数的prototype对象。
  3. 让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
  4. 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

ES相关

var/let/const

  1. var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。 let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。 const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,且不能修改。
  2. var可以先使用,后声明,因为存在变量提升;let必须先声明后使用。
  3. var是允许在相同作用域内重复声明同一个变量的,而let与const不允许这一现象。
  4. 会产生暂时性死区:

https://es6.ruanyifeng.com/#docs/let​es6.ruanyifeng.com/#docs/let

var tmp = 123;

if (true) {
  // 存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,
     所以在let声明变量前,对tmp赋值会报错
  tmp = 'abc'; // ReferenceError
  let tmp;
}

map 跟 forEach 的区别

map有返回值 forEach 没有返回值

常用的数组方法

豆豆:JS常用数组方法7 赞同 · 0 评论文章

ES6常用的新特新

1.新增symbol类型 表示独一无二的值,用来定义独一无二的对象属性名;

2.const/let 都是用来声明变量,不可重复声明,具有块级作用域。存在暂时性死区,也就是不存在变量提升。(const一般用于声明常量);

3.变量的解构赋值(包含数组、对象、字符串、数字及布尔值,函数参数),剩余运算符(...rest);

4.模板字符串(${data});

5.扩展运算符(数组、对象);

6.箭头函数;

7.Set和Map数据结构;

8.Proxy/Reflect;

9.Promise;

10.async函数;

Vue相关

vue 的生命周期

create阶段:vue实例被创建 beforeCreate: 创建前,此时data和methods中的数据都还没有初始化 created: 创建完毕,data中有值,未挂载

mount阶段: vue实例被挂载到真实DOM节点 beforeMount:可以发起服务端请求,去数据 mounted: 此时可以操作Dom

update阶段:当vue实例里面的data数据变化时,触发组件的重新渲染 beforeUpdateupdated

destroy阶段:vue实例被销毁 beforeDestroy:实例被销毁前,此时可以手动销毁一些方法 destroyed

vue 的通讯方式

父子组件通讯 props $emit parent、children Ref

兄弟之间通讯 event bus

跨组件通讯 $attrs、$listeners Provide、inject

vuex

computed与watch

watch 属性监听 是一个对象,键是需要观察的属性,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,监听属性的变化,需要在数据变化时执行异步或开销较大的操作时使用

computed 计算属性 属性的结果会被缓存,当computed中的函数所依赖的属性没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取。除非依赖的响应式属性变化时才会重新计算,主要当做属性来使用 computed中的函数必须用return返回最终的结果 computed更高效,优先使用。data 不改变,computed 不更新。

使用场景computed:当一个属性受多个属性影响的时候使用,例:购物车商品结算功能 watch:当一条数据影响多条数据的时候使用,例:搜索数据

v-if 和 v-for 的优先级

v-for的优先级是高于v-if的,如果两者同时出现的话,那每次循环都会执行v-if,会很浪费性能,我们正确的做法应该是再v-for的外面新增一个模板标签template,在template上使用v-if

$nextTick

  1. nextTick是Vue提供的一个全局API,是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用$nextTick,则可以在回调中获取更新后的DOM;

2. Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启1个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中-次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。nextTick方法会在队列中加入一个回调函数,确保该函数在前面的dom操作完成后才调用;

3. 比如,我在干什么的时候就会使用nextTick,传一个回调函数进去,在里面执行dom操作即可;

4. 我也有简单了解nextTick实现,它会在callbacks里面加入我们传入的函数,然后用timerFunc异步方式调用它们,首选的异步方式会是Promise。这让我明白了为什么可以在nextTick中看到dom操作结果。

v-for中key的作用

  1. key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度,更高效的更新虚拟DOM;
  2. Vue在patch过程中判断两个节点是否是相同节点,key是一个必要条件,渲染一组列表时,key往往是唯一标识,所以如果不定义key的话,Vue只能认为比较的两个节点是同一个,哪怕它们实际上不是,这导致了频繁更新元素,使得整个patch过程比较低效,影响性能;
  3. 从源码中可以知道,Vue判断两个节点是否相同时主要判断两者的key和元素类型等,因此如果不设置key,它的值就是undefined,则可能永 远认为这是两个相同的节点,只能去做更新操作,这造成了大量的dom更新操作,明显是不可取的。

keep-alive的实现

作用:实现组件缓存

钩子函数: ”activated “组件渲染后调用 ”deactivated“组件销毁后调用

原理:Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。

配置属性: include 字符串或正则表达式。只有名称匹配的组件会被缓存 exclude 字符串或正则表达式。任何名称匹配的组件都不会被缓存 max 数字、最多可以缓存多少组件实例

Vuex

Vuex 是一个专为 Vue 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。

  1. Vuex 的状态存储是响应式的;当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新

2. 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation, 这样使得我们可以方便地跟踪每一个状态的变化 Vuex主要包括以下几个核心模块:

  1. State:定义了应用的状态数据
  2. Getter:在 store 中定义“getter”(可以认为是 store 的计算属性),就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来, 且只有当它的依赖值发生了改变才会被重新计算

3. Mutation:是唯一更改 store 中状态的方法,且必须是同步函数

4. Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作

5. Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中

vue-router 怎么传参

1.id传参

//路由配置
{
    path: '/orderDetail/:id',
    name: 'orderdetail',
    component: () => import('../components/orderDetail.vue'),
    meta: { showTabbar: false}
  }

this.$router.push({path:`/orderDetail/${this.id}`})

2.query传参

this.$router.push({path:`/orderDetail`,query:{id:this.id}})

3.pramas传参

this.$router.push({name:`orderdetail`,params:{id:this.id}})

插槽

1.普通插槽

2.具名插槽

3.作用域插槽

vue 的双向绑定原理

当一个Vue实例创建时,Vue会遍历data选项的属性,用 Object.defineProperty 将它们转为 getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher重新计算,从而致使它关联的组件得以更新。

Vue2和Vue3区别

属性监听:

defineProperty: 遍历对象,劫持对象的每一个属性; Proxy: 劫持整个对象,并返回一个代理对象;

对数组方法的支持:

defineProperty: push...等不能监听;Proxy: 可以监听;

兼容:

defineProperty(ES5)兼容性比Proxy(ES6)好。

Vue3

  1. setup函数

2. 声明普通类型 ref

3. 声明复杂类型 reactive

4.watch 的用法

5. v-model 的用法

6. Vue3双向绑定原理

7. Vue3的生命周期

8. Vue3通讯

9. Vue3.2 setup 语法糖等

webpack相关

常用Loader(高频)

raw-loader:加载文件原始内容(utf-8)

file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)

source-map-loader:加载额外的 Source Map 文件,以方便断点调试

svg-inline-loader:将压缩后的 SVG 内容注入代码中

image-loader:加载并且压缩图片文件

json-loader 加载 JSON 文件(默认包含)

babel-loader:把 ES6 转换成 ES5

ts-loader: 将 TypeScript 转换成 JavaScript

awesome-typescript-loader:将 TypeScript 转换成 JavaScript,性能优于 ts-loader

sass-loader:将SCSS/SASS代码转换成CSS

css-loader:加载 CSS,支持模块化、压缩、文件导入等特性

style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS

postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀

vue-loader:加载 Vue.js 单文件组件

常用的Plugin

define-plugin:定义环境变量 (Webpack4 之后指定 mode 会自动配置)

ignore-plugin:忽略部分文件

html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)

web-webpack-plugin:可方便地为单页应用输出 HTML,比 html-webpack-plugin 好用

uglifyjs-webpack-plugin:不支持 ES6 压缩 (Webpack4 以前)

terser-webpack-plugin: 支持压缩 ES6 (Webpack4)

webpack-parallel-uglify-plugin: 多进程执行代码压缩,提升构建速度

mini-css-extract-plugin: 分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin)

serviceworker-webpack-plugin:为网页应用增加离线缓存功能

clean-webpack-plugin: 目录清理

oader 和 plugin 的区别

Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。 因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。

Plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。

Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入

Git相关

git 常用命令

  1. git clone

2. git add .

3. git commit -m

4. git push

5. git pull

6. git branch 查看本地分支

7. git branch -a 查看远程分支

8. git branch -d xxx 删除分支

9. git checkout xxx 切换分支

10. git checkout -b xxx 新建分支并且切换到改分支

11. git branch -m 旧分支名 新分支名 分支重命名

12. git status 查看修改的文件

13. git merge 合并分支

14. git checkout -b xxx origin/xxx 拉去远程分支

15. git log 查看提交记录

网络安全

GET和POST区别(高频)

1.GET在浏览器回退不会再次请求,POST会再次提交请求

2.GET请求会被浏览器主动缓存,POST不会,要手动设置

3.GET请求参数会被完整保留在浏览器历史记录里,POST中的参数不会

4.GET请求在URL中传送的参数是有长度限制的,而POST没有限制

5.GET参数通过URL传递,POST放在Request body中

6.GET参数暴露在地址栏不安全,POST放在报文内部更安全

7.GET一般用于查询信息,POST一般用于提交某种信息进行某些修改操作

8.GET产生一个TCP数据包;POST产生两个TCP数据包

HTTP和HTTPS

http 和 https 的基本概念

http: 是一个客户端和服务器端请求和应答的标准(TCP),用于从 WWW 服务器传输超文本到本地浏览器的超文本传输协议。 https:是以安全为目标的 HTTP 通道,即 HTTP 下 加入 SSL 层进行加密。其作用是:建立一个信息安全通道,来确保数据的传输,确保网站的真实性。

http 和 https 的区别(高频)

1.`HTTP` 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头

2.`HTTP` 是不安全的,而 HTTPS 是安全的

3.`HTTP` 标准端口是80 ,而 HTTPS 的标准端口是443

4.`在OSI` 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层

5.`HTTP` 无法加密,而HTTPS 对传输的数据进行加密

6.`HTTP`无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书

http状态码(高频)

  1. 200响应成功

2. 301永久重定向

3. 302临时重定向

4. 304资源缓存

5. 403服务器禁止访问

6. 404服务器资源未找到

7. 500 502服务器内部错误 504 服务器繁忙

性能相关

请求跨域

  1. jsonp(利用script标签没有跨域限制的漏洞实现。缺点:只支持GET请求)
  2. CORS(设置Access-Control-Allow-Origin:指定可访问资源的域名)
  3. proxy代理 目前常用方式,通过服务器设置代理
  4. 利用h5新特性window.postMessage()

浏览器本地存储

浏览器的本地存储主要分为Cookie、localStorage和sessionStorage。

共同点: 都是保存在浏览器端、且同源的
不同点:

  1. cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。
  2. 存储大小限制也不同,
  • cookie数据不能超过4K,sessionStorage和localStorage可以达到5M
  • sessionStorage:仅在当前浏览器窗口关闭之前有效;
  • localStorage:始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据;
  • cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭

3.作用域不同

  • sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;
  • localstorage:在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在
  • cookie: 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在

防抖

多次触发事件,事件处理函数只能执行一次,并且是在触发操作结束时执行。也就是说,当一个事件被触发准备执行事件函数前,会等待一定的时间(这时间是码农自己去定义的,比如 1 秒),如果没有再次被触发,那么就执行,如果被触发了,那就本次作废,重新从新触发的时间开始计算,并再次等待 1 秒,直到能最终执行!

const debounce = (fn, time) => {
        let timer = null
        return function () {
          clearTimeout(timer)

          timer = setTimeout(() => {
            fn.apply(this)
          }, time)
        }
      }

节流

事件触发后,规定时间内,事件处理函数不能再次被调用。也就是说在规定的时间内,函数只能被调用一次,且是最先被触发调用的那次。

const throttle = (fn, time) => {
        let flag = true
        return function () {
          if (!flag) return
          flag = false
          setTimeout(() => {
            fn.apply(this)
            flag = true
          }, time)
        }
      }

优化项目

  1. 移除生产环境的控制台打印
  2. 第三方库的按需加载
  3. 避免css表达式、滤镜,较少DOM操作,优化图片、精灵图,避免图片空链接等
  4. 降低请求成本
  5. 减少请求数
  6. 减少传输体积

你可能感兴趣的:(前端,前端,面试,javascript)