2023前端面试题

一.HTML篇

1.HTML是什么?它的缩写代表什么?

HTML代表"超文本标记语言"(Hypertext Markup Language),它是一种用于创建网页结构和内容的标记语言。

2.HTML文档的基本结构是什么?

基本的HTML结构包括声明、元素、元素(包含元数据),以及元素(包含页面内容)。

3.HTML5新特性?

语义化元素: HTML5引入了一些新的语义化元素,如

4.HTML元素和HTML标签有什么区别? 

HTML元素是由开始标签、内容和结束标签组成的,而HTML标签只是开始标签或结束标签的一部分。

5.前端页面有那三层构成?

  1. 结构层(Structure Layer)

    结构层是网页的基本骨架,它定义了网页的整体布局和内容组织。在HTML中,结构层由HTML标记和元素组成,包括标题、段落、列表、表格等。这些元素构成了网页的基本结构,用于定义文本内容、图像、链接等。
  2. 样式层(Presentation Layer)

    样式层负责定义网页的外观和布局,使其看起来更吸引人并符合设计要求。在前端开发中,通常使用CSS(层叠样式表)来控制样式层。CSS可以用来设置字体、颜色、边距、内边距、背景、定位等网页元素的外观和排列方式。
  3. 行为层(Behavior Layer)

    行为层是网页的交互部分,它包括用户与网页进行互动的功能和动态行为。通常使用JavaScript来实现行为层,JavaScript可以用于添加交互效果、响应用户输入、验证表单、异步加载数据等。行为层使网页更具动态性和交互性。

6.HTML中的元数据是什么?它们放在哪里? 

元数据包括页面的标题、字符集设置、关键词等信息。它们通常放在元素中,如</code>和<code><meta></code>标签。</p> </blockquote> <p> 7.<strong>如何在HTML中创建超链接?</strong></p> <blockquote> <p>使用<code><a></code>元素来创建超链接,设置<code>href</code>属性来指定链接目标。</p> </blockquote> <p><strong> 8.src和href的区别?</strong></p> <blockquote> <ul> <li><code>src</code> 通常用于加载外部媒体资源或嵌入式内容,并且在加载时会影响页面的布局和呈现。</li> <li><code>href</code> 通常用于创建超链接或关联外部资源,点击链接会导航到新的页面或资源,而不会对当前页面进行布局更改。</li> </ul> </blockquote> <p><strong>9.HTML标签在标准流的表现是什么样?</strong></p> <blockquote> <p>HTML标签的表现方式在标准流(也称为文档流)中取决于它们的类型(块级元素、内联元素、替换元素等)。标准流是浏览器用于排列和渲染HTML元素的一种基本方式,它考虑元素的类型、位置和文档结构。以下是不同类型HTML标签在标准流中的一般表现:</p> <p></p> <p><strong>块级元素(Block-level Elements):</strong></p> <ul> <li>块级元素会独占一行或多行,它们从上到下依次排列。</li> <li>块级元素会在页面中自动换行,每个块级元素都会另起一行。</li> <li>常见的块级元素包括<code><div></code>、<code><p></code>、<code><h1></code>到<code><h6></code>、<code><ul></code>、<code><ol></code>、<code><li></code>、<code><table></code>等。</li> </ul> <p><strong>内联元素(Inline Elements):</strong></p> <ul> <li>内联元素不会强制换行,它们会在同一行上水平排列。</li> <li>内联元素只占据它们包含的内容的宽度,不会独占一行。</li> <li>常见的内联元素包括<code><span></code>、<code><a></code>、<code><strong></code>、<code><em></code>、<code><img></code>、<code><br></code>等。</li> </ul> <p><strong>替换元素(Replaced Elements):</strong></p> <ul> <li>替换元素是一种特殊类型的内联元素,但它们的内容通常由外部资源来替代,如图像、音频、视频等。</li> <li>替换元素会在页面中占据一定的空间,根据外部资源的尺寸和属性来确定大小。</li> <li>常见的替换元素包括<code><img></code>、<code><audio></code>、<code><video></code>。</li> </ul> </blockquote> <p> 10.<strong>HTML中的表格如何创建?</strong></p> <blockquote> <p>表格可以使用<code><table></code>、<code><tr></code>、<code><td></code>等标签创建,其中<code><table></code>用于定义表格,<code><tr></code>用于定义行,<code><td></code>用于定义单元格。</p> </blockquote> <p> 11.<strong>HTML中的表单是什么?</strong></p> <blockquote> <p>表单是用于收集用户输入数据的元素,可以使用<code><form></code>标签创建,包括文本框、单选按钮、复选框等表单控件。</p> </blockquote> <p>12.<strong>什么是HTML属性?</strong></p> <blockquote> <p> HTML属性是提供有关HTML元素的附加信息的键值对,如<code><a href="https://www.example.com"></code>中的<code>href</code>属性。</p> </blockquote> <p><strong>13.严格模式和混杂模式?</strong></p> <blockquote> <p><strong>混杂模式(Quirks Mode)</strong>:</p> <ul> <li>混杂模式是一种兼容性模式,当浏览器检测到网页没有按照现代标准编写时,会进入混杂模式。这通常发生在早期的HTML文档中,或者在没有正确声明文档类型的情况下。</li> </ul> <p><strong>严格模式(Strict Mode)</strong>:</p> <ul> <li>严格模式是一种更加规范和标准化的模式,它要求网页按照HTML和CSS的标准规范编写。在HTML中,可以通过在文档开头添加正确的文档类型声明(<!DOCTYPE>)来启用严格模式。</li> </ul> </blockquote> <p><strong>14.iframe是什么?</strong></p> <blockquote> <p><code><iframe></code> 是 HTML 中的一个标签,用于嵌套另一个HTML文档或外部资源(如另一个网页)到当前页面中。这个标签允许您在一个网页中嵌套另一个独立的网页,从而创建内联框架(Inline Frame)或嵌套的窗口。</p> <p>使用 <code><iframe></code> 可以实现各种功能,包括但不限于以下几种:</p> <ol> <li> <p><strong>嵌套其他网页</strong>:您可以在您的网页中嵌套其他网站的内容,如地图、社交媒体小部件、视频等。</p> </li> <li> <p><strong>显示嵌入式内容</strong>:可以嵌套媒体,如YouTube视频、Google地图、Twitter帖子等。</p> </li> <li> <p><strong>创建内联框架</strong>:可以将一部分内容包装在内联框架中,使其在页面中独立显示。</p> </li> <li> <p><strong>加载外部应用程序</strong>:可以将外部应用程序嵌套到您的网页中,例如在线文档编辑器、在线游戏等。</p> </li> </ol> <p>需要注意的是,虽然 <code><iframe></code> 可以实现各种有用的功能,但要谨慎使用,因为滥用 <code><iframe></code> 可能会导致安全问题,如点击劫持(clickjacking)等。确保只嵌套信任的内容,并了解如何正确配置 <code>sandbox</code> 属性以增加安全性。</p> </blockquote> <p><strong>15.script 标签中 defer 和 async 的区别? </strong></p> <blockquote> <p style="margin-left:0;"><span style="color:#0d0016;">在HTML的<code><script></code>标签中,<code>defer</code>和<code>async</code>用于控制脚本的加载和执行方式的属性,它们之间有一些重要的区别:</span></p> <ol style="margin-left:0;"> <li> <p style="margin-left:0;"><strong>加载方式</strong>:</p> <ul> <li><code>async</code>:异步加载脚本,阻止页面的解析和渲染,一旦下载完成,立即执行。多个标记属性的脚本是需要加载和执行的,执行顺序不确定,取决于<code>async</code>哪个脚本先下载完成。</li> <li><code>defer</code>:脚本也是异步加载的,但是它会保持脚本的执行顺序与它们在页面中的顺序一致。脚本会在文档完成解析之后,DOMContentLoaded 事件触发之前按照顺序执行。</li> </ul></li> <li> <p style="margin-left:0;"><strong>执行时机</strong>:</p> <ul> <li><code>async</code>:脚本一旦下载完成,就会立即执行,无论其在文档中的位置如何。</li> <li><code>defer</code>:脚本会在文档解析完成之后,DOMContentLoaded 事件触发之前执行,保证脚本在文档中的顺序执行。</li> </ul></li> <li> <p style="margin-left:0;"><strong>适用场景</strong>:</p> <ul> <li><code>async</code>:适用于不依赖于页面解析过程中的其他脚本或文档元素的脚本,通常用于第三方或独立的脚本。</li> <li><code>defer</code>:适用于等待页面解析完成后执行的脚本,可以确保脚本访问和操作页面中的元素。</li> </ul></li> </ol> <pre><code class="language-html"><!DOCTYPE html> <html> <head> <title>Script Loading

Hello, world!

在上面的示例中,script1.js使用async属性,它会异步加载并立即执行,而script2.js使用defer属性,会等待文档解析完成后执行,保证执行顺序正确。 

二.CSS篇

1.什么是CSS?它的作用是什么?

CSS是层叠样式表(Cascading Style Sheets)的缩写,它是一种用于控制网页或文档的外观和布局的样式语言。CSS的主要作用是将样式与内容分离开来,这样可以更容易地对网页的外观进行管理和调整。以下是CSS的主要作用

  1. 样式化网页元素:CSS允许开发者定义网页上各个元素的样式,如文字颜色、字体、背景颜色、边框、间距等。通过为元素应用不同的样式规则,可以使页面看起来更漂亮、更有吸引力。

  2. 布局控制:CSS可以用来定义网页元素的位置和大小,从而控制页面的布局。开发者可以使用CSS的布局属性来实现各种不同的页面布局,包括响应式设计、网格布局、居中对齐等。

  3. 网页响应性:CSS可以帮助开发者创建响应式设计,使网页能够适应不同屏幕尺寸和设备,从而提供更好的用户体验。通过媒体查询和适应性样式,可以根据用户的设备动态调整页面布局和样式。

  4. 维护性和可重用性:通过将样式信息集中存储在CSS文件中,可以轻松地在整个网站或应用程序中重复使用样式规则。这提高了代码的可维护性,因为一次更新样式规则就可以应用到所有相关的元素上。

  5. 提高性能:使用CSS来定义页面样式可以减少页面加载时间,因为浏览器可以缓存样式信息并在多个页面之间共享。这有助于提高网页的性能和加载速度。

2.请解释CSS盒模型是什么,它由哪些部分组成?

CSS盒模型是一种用来描述HTML元素在页面布局中所占空间的模型。它定义了一个元素的可视区域,并将其分为不同的部分,以便确定元素的大小、内边距、边框和外边距。CSS盒模型由以下几个主要部分组成:

  1. 内容区域(Content Area):内容区域是元素包含的实际内容,例如文本、图像或其他媒体。这个区域的大小由元素的width(宽度)和height(高度)属性定义。

  2. 内边距(Padding):内边距是内容区域与元素边框之间的空白区域。它可以通过padding属性来定义,用于控制元素内容与边框之间的间距。

  3. 边框(Border):边框是围绕内容区域和内边距的线条或边界。边框的样式、宽度和颜色可以通过border属性来设置。

  4. 外边距(Margin):外边距是元素边框与相邻元素之间的空白区域。外边距可以通过margin属性来定义,用于控制元素与其周围元素之间的间距。

在CSS盒模型中,这些部分是相互嵌套的,从内到外依次是内容区域、内边距、边框和外边距。这些部分的总和决定了元素在页面上所占的空间。

需要注意的是,CSS盒模型有两种不同的表示方式:标准盒模型(content-box)和IE盒模型(border-box)。在标准盒模型中,元素的宽度和高度仅包括内容区域,而在IE盒模型中,元素的宽度和高度包括了内容区域、内边距和边框。开发者可以通过设置CSS属性box-sizing来选择使用哪种盒模型表示方式。

3.什么是浮动(float)?它的作用是什么?如何清除浮动?

浮动(float)是CSS中的一种布局属性,用于控制元素在其容器中的水平位置。浮动的主要作用是使元素能够在文本流中沿着其容器的左侧或右侧浮动,并允许其他元素围绕它。主要用途包括实现多列布局、文字环绕图像以及创建网页导航菜单等。

浮动的主要属性是float,其可能的值包括:

  • float: left;:元素将向左浮动,其他元素将尝试围绕其右侧。通常用于创建多列布局或文字环绕图像。

  • float: right;:元素将向右浮动,其他元素将尝试围绕其左侧。也可用于创建多列布局或文字环绕图像。

浮动元素会被移出文档流,这意味着其他元素会忽略它,不再占用其位置。这也是清除浮动的原因,因为浮动元素可能导致容器坍塌(container collapse),从而影响布局。清除浮动是确保容器能够正确包含浮动元素的一种技术。

常见的清除浮动方法

.clearfix::after {
  content: "";
  display: table;
  clear: both;
}

在包含浮动元素的容器上添加一个带有clearfix类的样式,它会在容器的末尾插入一个清除浮动的伪元素。

.container {
  overflow: auto; /* 或 overflow: hidden; */
}

在包含浮动元素的容器上设置overflow属性为autohidden,这将创建一个新的块级格式化上下文(BFC),从而包含浮动元素。

使用clear属性: 在浮动元素后面添加一个带有clear属性的空元素,如

这会在浮动元素的下方创建一个新的块级元素,从而清除浮动。

4.什么是CSS伪类?举例说明伪类的用法。 

CSS伪类是一种用于选择文档中特定状态或特定元素的选择器。它们允许你为元素的特定状态或位置应用样式,而不是基于元素的类名或标签名进行选择。伪类以冒号(:)开头,后面跟着伪类的名称。

1.:hover 伪类:当用户鼠标悬停在元素上时应用样式。

a:hover {
  color: red;
}

2. :active 伪类:当元素被激活(例如,被点击)时应用样式。

button:active {
  background-color: yellow;
}

3.:focus 伪类:当元素获得焦点时应用样式(通常用于表单元素)。

input:focus {
  border-color: blue;
}

4.:nth-child 伪类:根据元素在其父元素中的位置选择元素。

li:nth-child(even) {
  background-color: lightgray;
}

5.:not 伪类:选择不匹配指定选择器的元素。

p:not(.special) {
  font-weight: bold;
}

6.:first-child:last-child 伪类:分别选择父元素的第一个和最后一个子元素。

li:first-child {
  font-weight: bold;
}

li:last-child {
  color: red;
}

5.什么是伪元素 ?

伪元素(Pseudo-elements)是CSS中的一种选择器,用于选择元素的特定部分或生成的内容,而不是选择整个元素。伪元素以两个冒号(::)开头,后面跟着伪元素的名称。伪元素允许开发者在文档中的元素内部或外部插入额外的样式和内容,以实现更丰富的页面布局和样式效果。

1.::before 伪元素:用于在元素的内容前面插入额外的内容。

p::before {
  content: "前置内容: ";
}

2.::after 伪元素:用于在元素的内容后面插入额外的内容。

a::after {
  content: " (链接)";
}

3.::first-line 伪元素:选择元素的第一行文本。

p::first-line {
  font-weight: bold;
}

4.::first-letter 伪元素:选择元素的第一个字母。

p::first-letter {
  font-size: 150%;
}

5.::selection 伪元素:用于选择文本时的样式。

::selection {
  background-color: yellow;
  color: black;
}

上述示例将选择文本的背景颜色设为黄色,文本颜色设为黑色。

6.什么是BEM(块、元素、修饰符)命名规范?它的作用是什么? 

 BEM是一种CSS命名规范,全称为块(Block)、元素(Element)、修饰符(Modifier)。它旨在帮助开发者更清晰地组织和命名CSS类,以实现可维护、可扩展和易理解的代码。BEM的核心思想是将页面分解成块、元素和修饰符三个层次,以更好地描述组件和它们之间的关系。

块(Block):块是页面上的一个独立的、可复用的组件或模块,通常由一个父级元素包裹,并具有一个描述性的类名,用来表示这个块的名称。

...

元素(Element):元素是块的组成部分,它不能独立存在,必须与块相互关联。元素使用双下划线(__)来连接块的类名和元素的名称。

Submit

修饰符(Modifier):修饰符是用来修改块或元素外观、状态或行为的类名。它们以单独的类名存在,通常使用单独的单短横线(-)来连接块或元素的类名和修饰符的名称。

...

7.什么是BFC?

BFC(Block Formatting Context)是CSS中的一个概念,它用于描述一个独立的渲染区域,其中元素的布局和渲染是相对于其他元素进行的。BFC是Web页面布局中的一个重要概念,它对元素的定位、清除浮动、边距折叠等方面都有重要影响。

以下是关于BFC的一些关键特点和作用:

  1. 元素内部的独立容器: BFC会创建一个独立的渲染容器,使其内部的元素在布局上不受外部元素的影响。这意味着在一个BFC内部,元素的定位和布局是相对于BFC内部的内容进行的,而不会影响到外部元素。

  2. 清除浮动: 当一个元素包含浮动元素时,它的高度可能会塌陷,不会包围浮动元素。但如果这个元素被设置为一个新的BFC,它将会包围浮动元素,解决了浮动元素导致的高度塌陷问题。

  3. 边距折叠: 在BFC中,相邻元素的上下边距会发生折叠(margin collapse),这可以用于合并相邻元素的外边距。但是在不同的BFC中,边距不会折叠,因此BFC可以用来控制边距折叠的行为。

  4. 阻止浮动元素溢出: 在BFC内部,浮动元素不会溢出到BFC的外部,而是会被包含在BFC内。

  5. 强制分隔内容: BFC可以将页面分隔成不同的区域,这对于实现一些复杂的布局要求非常有用。

要创建一个BFC,可以通过以下方式之一:

  • 将元素的overflow属性设置为非默认值(例如,overflow: auto;overflow: hidden;)。
  • 将元素的display属性设置为inline-blocktable-celltable-captionflexgrid等非默认值。
  • 将元素的position属性设置为absolutefixed

8.什么是粘性布局 ?

"粘性布局"(Sticky Layout)是一种用于网页布局的设计模式,它结合了固定定位(position: fixed)和相对定位(position: relative)的特性,使元素在页面上根据滚动位置实现不同的定位方式。

粘性布局通常用于导航栏、侧边栏、工具栏等需要在页面滚动时保持在特定位置的元素。这种布局模式的主要特点是:

  1. 元素初始位置:元素在页面上的初始位置可以是普通文档流中的某个位置。

  2. 滚动位置触发:当用户滚动页面,一旦元素的顶部(或底部)到达某个特定的滚动位置时,它会触发粘性定位,保持在那个位置不动。

  3. 滚动结束还原:当用户继续滚动页面,直到元素的底部(或顶部)到达另一个特定的滚动位置时,元素将会取消粘性定位,并回到初始位置。

  4. 不同滚动方向支持:粘性布局可以根据滚动方向(上滚或下滚)实现不同的粘性效果,例如,一个导航栏可以在用户向下滚动时固定在顶部,而在用户向上滚动时恢复到初始位置。

  5. 使用 CSS position 属性:粘性布局通常使用 CSS 的 position 属性来实现,其中 position: fixed 用于固定定位,而 position: relative 用于相对定位。

以下是一个简单的示例,展示了一个粘性导航栏的基本结构:


.navbar {
  position: relative; /* 初始位置 */
}

.navbar.sticky {
  position: fixed; /* 粘性定位 */
  top: 0;
}

在这个示例中,当用户向下滚动页面时,通过 JavaScript 或 CSS 类添加 .sticky 类,从而将导航栏固定在顶部。当用户向上滚动并导航栏返回到初始位置时,移除 .sticky 类,取消粘性效果。

三.JS篇

1.JavaScript怎么判断数据的类型?

在JavaScript中,您可以使用多种方式来判断数据的类型。以下是一些常见的方法:

1.使用typeof运算符: typeof 运算符可以用于检查变量或值的数据类型。它返回一个表示数据类型的字符串。

typeof 42; // 返回 "number"
typeof "Hello"; // 返回 "string"
typeof true; // 返回 "boolean"
typeof {}; // 返回 "object"
typeof []; // 返回 "object"
typeof null; // 返回 "object"
typeof undefined; // 返回 "undefined"
typeof function(){}; // 返回 "function"

需要注意的是,typeof null 返回 "object",这被认为是 JavaScript 中的一个历史 bug。

2.使用instanceof运算符: instanceof 运算符可以用于检查一个对象是否属于特定的构造函数的实例。

  1. instanceof 只能用于检查对象是否是由指定构造函数创建的实例。它不适用于原始数据类型,如数字、字符串和布尔值。

  2. instanceof 会遍历对象的原型链,因此如果对象的原型链上出现了指定构造函数,它也会返回 true。这可以用于检查继承关系。

  3. 要注意,如果你在多个窗口或框架中使用 instanceof 来检查对象类型,可能会出现问题,因为不同窗口或框架中的构造函数是不同的。

  4. 使用 instanceof 时,确保构造函数名首字母大写,因为构造函数通常以大写字母开头的命名规范

var str = "Hello";
console.log(str instanceof String); // 返回 false,因为 str 是一个原始字符串,不是 String 对象

var arr = [];
console.log(arr instanceof Array); // 返回 true,因为 arr 是 Array 的实例

var person = { name: "John" };
console.log(person instanceof Object); // 返回 true,因为 person 是 Object 的实例

function Dog() {}
var dog = new Dog();
console.log(dog instanceof Dog); // 返回 true,因为 dog 是 Dog 的实例

3.使用Object.prototype.toString方法: 这是一种更强大的方法,可以用于检查几乎所有数据类型,包括内置对象和自定义对象。

Object.prototype.toString.call(42); // 返回 "[object Number]"
Object.prototype.toString.call("Hello"); // 返回 "[object String]"
Object.prototype.toString.call(true); // 返回 "[object Boolean]"
Object.prototype.toString.call({}); // 返回 "[object Object]"
Object.prototype.toString.call([]); // 返回 "[object Array]"
Object.prototype.toString.call(null); // 返回 "[object Null]"
Object.prototype.toString.call(undefined); // 返回 "[object Undefined]"
Object.prototype.toString.call(function(){}); // 返回 "[object Function]"

2.什么是类数组? 

类数组(array-like object)是一种在 JavaScript 中常见的数据结构,它看起来像一个数组,但实际上是一个对象。这些对象通常具有数值键(整数索引),并且具有 length 属性,但它们不具备数组的所有方法和特性。

以下是一些常见的类数组对象的特点:

  1. 有数值键: 类数组对象通常具有数值键,就像一个数组一样,您可以通过整数索引来访问元素。

  2. 具有 length 属性: 类数组对象通常有一个 length 属性,该属性表示对象中元素的数量。

  3. 不具备数组方法: 虽然类数组对象看起来像数组,但它们不具备数组的方法,例如 push()pop()forEach() 等。您不能直接使用这些方法。

  4. 原型链上没有数组方法: 类数组对象的原型链通常不包含数组方法,这意味着您不能通过原型链访问数组方法。

 要将类数组对象转换为真正的数组,您可以使用以下方法

var array = Array.from(arrayLike);

var array = [...arrayLike];

var array = [...arrayLike];

var array = Array.prototype.slice.call(arrayLike);

3.JS精度丢失问题是什么?怎么解决?

JavaScript 中的精度丢失问题通常与浮点数运算有关,这是因为 JavaScript 使用的是双精度浮点数表示数字,而不是精确的十进制表示法。这可能导致在某些情况下精度丢失,特别是在执行小数点后很多位的计算时。

解决办法:

1.转化成整数进行计算.

2.使用专用的数学库:有一些第三方数学库,如 BigNumber.js 和 Decimal.js,可以提供更高精度的数学运算功能。

3.四舍五入:在某些情况下,你可以通过四舍五入来处理小数精度问题,以获得符合预期的结果。JavaScript 提供了 toFixed() 方法来控制小数点后的位数,并且可以使用 Math.round() 等函数来进行四舍五入操作。

4.小数点后的位数控制:在某些情况下,你可以通过限制小数点后的位数来避免精度丢失问题,以适应你的需求。这样可以减少计算中的精度问题。

4.JS内存泄漏的情况有那些? 

JavaScript 中的内存泄漏是指不再使用的内存仍然被程序占用,而没有被垃圾回收机制释放。内存泄漏可能会导致应用程序变得越来越占用内存,最终可能导致程序性能下降或崩溃。以下是一些常见的导致 JavaScript 内存泄漏的情况:

未释放事件监听器:如果你在 DOM 元素上添加了事件监听器,但没有正确地移除它们,那么这些事件监听器会持续占用内存,即使元素已经被从 DOM 中移除。为了避免这种情况,你应该使用 removeEventListener 方法来移除事件监听器。

const button = document.getElementById('myButton');
button.addEventListener('click', myFunction);

// 错误的移除方式,会导致内存泄漏
// button.removeEventListener('click', myFunction);

// 正确的移除方式
button.removeEventListener('click', myFunction);

闭包引用:当函数内部定义的变量被外部引用时,这个闭包可能会导致内存泄漏。如果闭包引用了大量的数据或长时间存活的对象,它们将不会被垃圾回收。

function createClosure() {
  const data = new Array(1000000).fill('memory'); // 大量数据
  return function() {
    // 闭包引用了大量数据
    console.log(data.length);
  };
}

const closure = createClosure();

// 即使不再使用 closure,但它仍然保留了对大量数据的引用,可能导致内存泄漏

定时器未清除:如果你使用 setTimeoutsetInterval 创建了定时器,但忘记清除它们,定时器函数会继续执行,而不会释放相关资源。

// 错误的方式,定时器未清除
const timer = setInterval(() => {
  console.log('Interval function');
}, 1000);

// 正确的方式,清除定时器
clearInterval(timer);

未释放不再需要的引用:在 JavaScript 中,当你不再需要某个对象或变量时,应该将其引用置为 null,以便让垃圾回收机制回收这些对象的内存。如果你保持不再需要的引用,那么这些对象将无法被释放。

let obj = { data: 'some data' };

// 当不再需要 obj 时,应该将其引用置为 null
obj = null;

循环引用:当两个或多个对象相互引用时,如果没有外部引用指向它们中的任何一个,那么它们将无法被垃圾回收,即使你不再需要它们。

const objA = {};
const objB = {};

objA.ref = objB;
objB.ref = objA;

// 即使不再需要 objA 和 objB,它们仍然相互引用,可能导致内存泄漏

5.JS事件流和事件模型 

JavaScript 事件流和事件模型是用于处理和管理Web页面上发生的事件的关键概念。它们有助于理解事件是如何传播、捕获和处理的。事件模型通常有两种:冒泡事件模型和捕获事件模型。

事件流(Event Flow):

 事件流描述了事件从发生到被处理的整个过程。它可以分为三个阶段:

  • 捕获阶段(Capture Phase):事件从根元素开始向目标元素传播,这个阶段用于捕获事件,通常不太常用。

  • 目标阶段(Target Phase):事件达到目标元素,也就是触发事件的元素。

  • 冒泡阶段(Bubble Phase):事件从目标元素开始向根元素传播,这个阶段用于冒泡事件,通常是最常用的阶段。

在标准的DOM事件模型中,事件首先进入捕获阶段,然后达到目标阶段,最后进入冒泡阶段。但大多数情况下,我们更关心冒泡阶段,因为它允许我们在父元素上捕获子元素的事件。

事件模型(Event Model)

事件模型定义了如何注册、捕获和处理事件的规则。有两种主要的事件模型:

  • 冒泡事件模型(Bubbling Event Model):在冒泡事件模型中,事件从目标元素开始冒泡,一直传播到根元素。这意味着父元素上的事件监听器会在子元素的事件监听器之前触发。

  • 捕获事件模型(Capturing Event Model):在捕获事件模型中,事件从根元素开始捕获,一直传播到目标元素。这意味着父元素上的事件监听器会在子元素的事件监听器之后触发。

大多数现代浏览器都支持冒泡事件模型,而捕获事件模型很少使用。你可以使用addEventListener 方法来注册事件监听器,然后选择是否使用冒泡或捕获。

6.JavaScript中this的指向 

JavaScript 中的 this 关键字是一个非常重要且常常令人困惑的概念,它指向当前执行上下文中的对象。this 的指向取决于函数是如何被调用的,以下是一些常见情况:

全局作用域中的 this 在全局作用域(在任何函数外部)中,this 指向全局对象,通常是 window 对象(浏览器环境)。

console.log(this); // 在浏览器中,输出 window 对象

函数中的 this 在函数内部,this 的值取决于函数的调用方式。以下是一些常见情况:

  • 作为函数调用: 当函数以普通函数的方式调用时,this 通常指向全局对象(在严格模式下是 undefined)。
function myFunction() {
  console.log(this); // 在浏览器中,输出 window 对象
}

myFunction();
  •  作为对象方法调用: 当函数作为对象的方法调用时,this 指向调用该方法的对象。
const obj = {
  name: 'Alice',
  greet: function() {
    console.log(this.name); // 输出 'Alice'
  }
};

obj.greet();
  •  构造函数中的 this 当使用 new 关键字调用构造函数时,this 指向新创建的对象。
function Person(name) {
  this.name = name;
}

const alice = new Person('Alice');
console.log(alice.name); // 输出 'Alice'
  •  使用 callapply 方法: 可以使用 callapply 方法显式地设置函数的 this 值。
function sayHello() {
  console.log(`Hello, ${this.name}!`);
}

const person = { name: 'Bob' };
sayHello.call(person); // 输出 'Hello, Bob!'
  •  箭头函数中的 this 箭头函数的 this 始终指向定义该箭头函数的外层函数的 this 值,而不是调用它的对象。
const obj = {
  name: 'Alice',
  greet: function() {
    setTimeout(() => {
      console.log(this.name); // 输出 'Alice'
    }, 1000);
  }
};

obj.greet();

四.Vue篇

1.vue2的响应式原理

Vue.js 2.x 的响应式原理是基于"依赖追踪"和"发布-订阅模式"构建的。它的核心概念是Vue实例中的数据属性(data properties)如何与DOM元素建立关联,以便在数据变化时自动更新视图。

以下是Vue 2.x 响应式原理的基本工作方式:

1.数据属性定义: 在Vue实例中,你通过data选项来定义数据属性。这些属性成为Vue实例的响应式数据。

new Vue({
  data: {
    message: 'Hello, Vue!'
  }
})

2.依赖追踪: 当模板中的表达式引用数据属性时,Vue会在内部建立依赖关系。Vue跟踪哪些组件依赖哪些数据属性。这种追踪是静态的,即在模板编译阶段建立。

{{ message }}

3.触发Getter(读取器): 当访问数据属性时,Vue会调用相应数据属性的Getter方法来获取数据的值,并将"观察者"(Watcher)添加到依赖列表中。

4.观察者(Watcher): 观察者是一个与依赖的数据属性相关联的对象,它会监听数据属性的变化。

5.数据变化时通知: 当数据属性发生变化时,Vue会触发Setter(写入器)方法来更新数据的值,然后通知相关的观察者。

6.视图更新: 观察者接收到通知后,会调用其自身的更新方法,这将导致相关的视图组件重新渲染,从而反映数据的变化。

这个响应式系统使得数据和视图之间的关联变得非常紧密,而且不需要手动干预DOM。这也是Vue在开发过程中能够提供数据驱动视图的核心原因之一。

需要注意的是,在Vue 2.x 中,这个响应式系统对于动态添加的属性(非初始属性)是无法响应的。如果需要动态添加属性并让其响应式,你可以使用Vue.set方法或this.$set方法来实现。

2.Vue数据双向绑定的原理

  1. 数据劫持(Data Observation):Vue使用了一个名为"数据劫持"的技术来追踪数据的变化。当你创建一个 Vue 实例时,Vue会遍历数据对象中的所有属性,使用 Object.defineProperty 方法将这些属性转换为"getter"和"setter"。这意味着当访问或修改数据属性时,Vue可以拦截这些操作,从而知道何时数据被访问或修改。

  2. 虚拟 DOM(Virtual DOM):Vue引入了虚拟 DOM 这个概念,它是一个虚拟的内存中的DOM表示。Vue的模板编译器将模板转换为虚拟 DOM 树。当数据发生变化时,Vue会比较新旧虚拟 DOM 树,找到需要更新的部分,然后仅更新这些部分,而不是重新渲染整个页面。

  3. Watcher 观察者:Vue还使用了Watcher(观察者)来建立视图和数据的连接。每个数据属性都有一个对应的观察者对象。当数据属性被访问时,Watcher 将记录该属性的依赖项,当属性发生变化时,Watcher 负责通知视图更新。

  4. Dep 依赖管理:每个观察者都有一个 Dep(依赖)对象,它用于管理依赖该观察者的所有数据属性。当一个数据属性被访问时,它会将该观察者添加到自己的依赖列表中,当属性变化时,它会通知所有依赖于它的观察者。

3.Vue3响应式原理

Vue 3 的响应式原理相对于 Vue 2 有一些重要的改进和优化,主要是通过 Proxy 对象和 Reflect API 来实现的。以下是 Vue 3 的响应式原理的主要特点:

  1. Proxy 对象:Vue 3 使用了 JavaScript 的 Proxy 对象来替代 Vue 2 中的 Object.defineProperty,这使得响应式系统更加灵活和强大。Proxy 可以拦截对象上的各种操作,包括属性访问、属性赋值、属性删除等,而不仅仅是 get 和 set 操作。这使得 Vue 3 的响应式系统能够更精确地追踪属性的变化,而不仅仅是属性的读取和写入。

  2. Reactivity API:Vue 3 引入了新的 Reactivity API,允许开发者更灵活地创建和管理响应式数据。通过 refreactivetoRefs 等 API,你可以更容易地定义和处理响应式数据。例如,你可以使用 ref 来创建一个响应式的基本数据类型,或者使用 reactive 来创建一个响应式的对象。

  3. Composition API:Vue 3 推出了 Composition API,它使得组件的代码更具可组织性和可维护性。Composition API 允许你在组件内部自由组合和重用逻辑代码,而不再受限于 Vue 2 的选项对象格式。Composition API 的函数形式也更容易进行测试和调试。

  4. 跨组件更新追踪:Vue 3 的响应式系统具有更好的跨组件更新追踪能力,这意味着当数据变化时,Vue 3 能够更有效地更新相关组件,减少不必要的渲染开销,提高了性能。

  5. 缓存:Vue 3 引入了缓存机制,可以缓存计算属性和组件的渲染结果,减少不必要的重复计算和渲染,提高了性能。

 4.Vue组件生命周期

Vue.js 2.x 中的组件生命周期是一个非常重要的概念,它定义了组件在创建、挂载、更新和销毁等不同阶段所执行的钩子函数。以下是 Vue.js 2.x 组件生命周期的详细解析:

  1. beforeCreate(创建前)

    • 钩子函数在实例被创建之后立即调用。
    • 此时实例的数据观测和事件配置都尚未初始化。
    • 不能访问 datapropscomputedmethods 等属性或方法。
  2. created(创建后)

    • 钩子函数在实例被创建后立即调用。
    • 此时实例已经完成数据观测和事件配置,但是尚未挂载到DOM上。
    • 可以访问 datapropscomputedmethods 等属性或方法。
    • 常常用来进行异步操作,如发起网络请求或订阅事件。
  3. beforeMount(挂载前)

    • 钩子函数在实例被挂载到DOM之前立即调用。
    • 此时虚拟DOM已经生成,但尚未渲染到页面上。
  4. mounted(挂载后)

    • 钩子函数在实例被挂载到DOM之后立即调用。
    • 此时实例已经渲染到页面上,可以进行DOM操作。
    • 常用于获取或操作DOM元素。
  5. beforeUpdate(更新前)

    • 钩子函数在数据更新后,但在虚拟DOM重新渲染和打补丁之前立即调用。
    • 在此时可以访问到更新之前的DOM状态。
    • 避免在此期间更改数据,以防进入无限循环。
  6. updated(更新后)

    • 钩子函数在数据更新后,虚拟DOM重新渲染和打补丁之后立即调用。
    • 可以进行一些DOM操作,但要注意避免触发无限循环更新。
  7. beforeDestroy(销毁前)

    • 钩子函数在实例销毁之前立即调用。
    • 可以在这里进行一些清理工作,如取消订阅、定时器等。
    • 该阶段组件仍然完全可用。
  8. destroyed(销毁后)

    • 钩子函数在实例销毁之后立即调用。
    • 所有的事件监听器和子组件实例都已经被销毁。
    • 该阶段组件不再可用。

5.Vue中虚拟dom是什么?有什么作用?

在Vue.js中,虚拟DOM(Virtual DOM)是一种用于提高性能的技术。虚拟DOM是一个抽象的浏览器内存中的虚拟树,它与实际的DOM树相对应。Vue.js使用虚拟DOM来跟踪DOM树的状态变化,并最小化对实际DOM的操作,以提高性能和响应速度。

虚拟DOM的作用包括以下几个方面:

  1. 提高性能:虚拟DOM可以减少对实际DOM的直接操作次数。当数据变化时,Vue.js首先会在虚拟DOM中进行比较,找出需要更新的部分,然后将这些更新应用到实际DOM上。这个过程比直接操作实际DOM更高效,因为DOM操作通常是比较昂贵的。

  2. 简化复杂性:虚拟DOM允许开发者将视图的状态抽象为一个虚拟树,这样更容易理解和管理。开发者只需要关注数据的变化,而不需要手动操作DOM元素。

  3. 跨平台兼容性:虚拟DOM可以使Vue.js跨多个平台,例如浏览器、移动应用和服务器端渲染。通过不同的渲染器,Vue.js可以将虚拟DOM渲染成不同的输出。

  4. 实现高级特性:虚拟DOM也为Vue.js引入了一些高级特性,例如条件渲染、列表渲染和动画效果。这些特性可以更容易地实现,而无需手动处理DOM的复杂性。

总之,虚拟DOM是Vue.js中的一个重要概念,它通过在内存中构建虚拟树来优化DOM操作,提高了应用程序的性能和可维护性,使开发者可以更专注于数据和应用逻辑而不是DOM操作。这使得Vue.js成为一个流行的前端框架之一。

6.Vue中组件通信的方式?

在Vue.js中,组件之间可以通过多种方式进行通信,具体的选择取决于你的应用需求和架构。以下是一些常见的组件通信方式:

  1. Props 和 Events(父子组件通信)

    • 通过将数据通过props(属性)从父组件传递给子组件,子组件可以读取这些属性来显示数据。
    • 子组件可以通过触发事件,使用$emit来通知父组件发生了某些事情,父组件可以监听这些事件并执行相应的操作。
  2. 自定义事件

    • 使用Vue的实例方法$on$emit,你可以创建自定义事件,使任何两个组件之间都可以进行通信。
  3. Vuex(状态管理)

    • 对于大型应用程序或需要多个组件之间共享状态的情况,可以使用Vuex来进行全局状态管理。Vuex提供了一个中央存储库,可以在任何组件中访问和修改应用程序的状态。
  4. $parent 和 $children

    • Vue组件实例具有$parent$children属性,允许你在组件树中的不同组件之间直接访问父组件和子组件。但要注意,这种方式通常被认为不太推荐,因为它会引入一定的耦合性。
  5. 事件总线

    • 你可以创建一个简单的事件总线(Bus)来实现任何两个组件之间的通信。可以通过Vue实例作为中介来进行事件的触发和监听。
  6. Provide 和 Inject(祖先和后代组件通信)

    • 使用provideinject选项,祖先组件可以向其后代组件提供数据,后代组件可以通过inject选项访问这些数据。
  7. 全局事件总线

    • 你可以使用Vue的全局事件总线来实现跨组件通信。通过在Vue实例上使用$on$emit来创建全局事件,任何组件都可以监听并触发这些事件。
  8. WebSocket 或其他通信协议

    • 如果需要跨组件、跨浏览器窗口或跨设备的实时通信,可以使用WebSocket或其他通信协议来建立双向通信。

选择哪种通信方式取决于你的项目需求和组件之间的关系。通常,应该首先尝试使用Props和Events进行通信,然后根据需要引入更高级的通信方式,如Vuex或事件总线。

7.Vue中nextTick是什么?有什么作用 ?

在Vue.js中,nextTick 是一个重要的异步方法,用于在 Vue 实例更新DOM后执行回调函数。它的主要作用是确保你在更新数据后立即操作DOM元素,而不必担心数据更新是否已经同步到DOM中。

具体来说,nextTick 的作用包括:

  1. 异步更新DOM:Vue的数据更新是异步的,当你修改了数据时,Vue并不会立即更新DOM。而使用nextTick,你可以在下一轮事件循环中确保DOM已经更新,然后再执行你的回调函数。这对于在数据变化后操作DOM非常有用,因为你不需要手动等待DOM更新完成。

  2. 避免重复操作:有时候你可能需要在同一个事件循环中多次修改数据,如果不使用nextTick,可能会导致不必要的重复操作DOM。使用nextTick可以确保在同一事件循环中所有的数据更新都已经完成。

  3. 执行一些特定的DOM操作:在某些情况下,你可能需要在Vue实例更新DOM后立即执行一些特定的DOM操作,例如获取元素的尺寸、位置或焦点等。nextTick让你能够确保这些DOM操作在数据更新后进行。

import { nextTick } from 'vue'

nextTick(() => {
  // 这里的代码会在 DOM 更新后执行
  console.log('DOM updated')
})

8.Vue中v-for的key的作用 

在Vue.js中,v-for指令用于循环渲染一个数组或对象的元素,它会为每个循环的元素创建一个新的Vue实例或虚拟DOM节点。key属性是用来帮助Vue跟踪每个循环元素的特定身份,以便在数据发生变化时能够高效地更新DOM。

key属性的作用包括:

  1. 提高性能:当数据发生变化时,Vue使用key来识别哪些元素是新增的、删除的或重排的。如果没有提供key,Vue可能会采用默认的机制来尝试识别元素的身份,这可能会导致性能下降,因为Vue需要更多的计算来确定元素的变化。

  2. 防止重复渲染:当使用v-for循环渲染相似的元素列表时,为每个元素提供唯一的key可以确保Vue不会错误地重用之前渲染的元素状态,从而避免潜在的错误和不一致。

  3. 方便的动画过渡:如果你使用Vue的过渡效果,key属性还可以帮助Vue正确地应用过渡效果,以便在元素被添加或删除时产生流畅的动画。

在使用v-for时,通常建议为每个循环的元素提供一个唯一的key,这个key可以是一个字符串或数字,通常使用循环元素的唯一标识符。

五.webpack篇 

1.chunk是什么?

在Webpack中,"chunk"(代码块)是指将 JavaScript 代码分割成小块的一种技术。Webpack允许你将应用程序的代码拆分成多个文件,每个文件被称为一个代码块(chunk)。这个功能有助于减小单个 JavaScript 文件的大小,提高应用程序的性能,特别是在大型应用中。

Webpack中的"chunk"通常有两种类型:

  1. Entry Chunk(入口代码块): 这是由Webpack配置文件中的入口点(entry point)定义的代码块。入口点是你应用程序的起点,Webpack从这里开始分析依赖关系,然后将所有相关的模块打包到一个或多个入口代码块中。通常,一个应用程序会有一个或多个入口点,每个入口点生成一个入口代码块。

  2. Split Chunk(拆分代码块): 这是通过Webpack的代码分割(code splitting)功能生成的代码块。代码分割允许你按需加载(lazy load)代码,将不同部分的代码拆分成独立的代码块,以减少初始加载时间。拆分代码块可以根据不同的条件生成,比如按照模块的共享性质、大小等。

通过使用Webpack的代码块功能,你可以更好地管理你的应用程序的代码,优化性能,以及实现按需加载,从而提高用户体验。通过合理配置Webpack的代码块策略,你可以控制生成的代码块的数量和大小,以满足你的项目需求。

2.loader是什么?

在Webpack和许多其他前端构建工具中,"loader" 是一种用于对不同类型文件进行转换和处理的插件。Loader允许你在导入或加载文件时对它们进行预处理,以便在项目中使用。

以下是Loader的一些关键特点和用途:

  1. 文件转换: Loader用于将不同类型的文件(例如JavaScript、CSS、图片、字体等)转换成可以被应用程序理解的格式。例如,使用Babel Loader可以将ES6/ES7的JavaScript代码转换为ES5,以便在旧版浏览器中运行。

  2. 模块化: Loader可以将资源文件转换成模块,使其可以通过import语句在代码中引用。这种模块化的特性对于将CSS样式或图片等资源与特定组件或模块关联非常有用。

  3. 链式处理: 你可以使用多个Loader来创建一个Loader链,每个Loader都按照一定顺序依次处理文件。这使得你可以在项目中执行多个转换步骤,例如首先使用Sass Loader编译Sass文件,然后使用CSS Loader处理CSS文件,最后使用Style Loader将CSS样式注入到HTML中。

  4. 自动解析依赖关系: Loader可以自动解析文件之间的依赖关系,确保在需要时自动加载依赖的模块或资源。

  5. 配置灵活: Loader通常可以通过Webpack配置文件的配置项来进行自定义和调整。这使得你可以根据项目需求来配置Loader的行为。

3.常用的loader?

以下是一些常用的Webpack Loader及其作用:

  1. babel-loader: 用于处理JavaScript文件,特别是新的ECMAScript标准(如ES6、ES7等)。它将现代JavaScript代码转换为向后兼容的代码,以便在旧版浏览器中运行。

  2. css-loader 和 style-loader: 用于处理CSS文件。css-loader负责解析CSS文件,处理CSS模块化,并解决依赖关系,而style-loader则将CSS样式以