文章参考 饥人谷若愚 老师的 “2023前端学习路线终极版(大、全、干)”的学习提纲,自行总结答案,仅供参考。
1. 常见HTML5标签及使用场景
header
:用于定义文档或页面的头部区域,包含网站标题、导航等。nav
:专门用于放置导航链接。main
:表示页面的主要内容区域。section
:可以对内容进行分组划分,比如不同主题的内容块。article
:用于表示独立的、可复用的内容,如一篇文章、一个博客帖子。aside
:通常用于侧边栏内容。footer
:定义文档或页面的底部区域,包含版权信息等。h1
-h6
:表示不同级别的标题,用于文档结构划分。p
:用于段落文本。img
:用于插入图片。a
:创建超链接。<ul>
、<ol>
、<li>
:分别用于无序列表、有序列表和列表项。<table>
、<tr>
、<td>
:用于构建表格。
2. 两种盒模模型-怪异盒模型以及标准盒模型
在 CSS 中,盒模型是一种用于描述网页元素布局的模型。它由内容区(content)、内边距(padding)、边框(border)和外边距(margin)四个部分组成。
怪异盒模型(IE 盒模型)和标准盒模型是两种不同的盒模型实现方式,它们的区别在于对元素宽度和高度的计算方式不同:
- 怪异盒模型:也称为 IE 盒模型,是 Internet Explorer 浏览器使用的盒模型。在怪异盒模型中,元素的宽度和高度包括内容区、内边距和边框。这意味着如果设置了元素的宽度和高度,内边距和边框的大小也会被包含在这个宽度和高度内。
- 标准盒模型:是 W3C 推荐的盒模型,也是现代浏览器普遍使用的盒模型。在标准盒模型中,元素的宽度和高度只包括内容区,内边距和边框的大小不包括在宽度和高度内。如果设置了元素的宽度和高度,内边距和边框的大小会在这个宽度和高度之外额外计算。
可以通过修改元素的
box-sizing
属性来改变元素的盒模型:
box-sizing: content-box
表示标准盒模型(默认值);box-sizing: border-box
表示怪异盒模型。
3. 边距折叠
边距折叠(Margin Collapsing)是指在 CSS 布局中,垂直相邻的两个或多个块级元素的外边距(margin)有时会合并为单个外边距,其大小遵循一定的计算规则。具体来说:
- 相邻兄弟元素间的外边距折叠:当没有非空内容、padding 或 border 将它们分隔时,垂直方向上的相邻块级元素的外边距会折叠;
- 父元素与第一个/最后一个子元素的外边距折叠:如果父元素没有边框且不创建新的 BFC(例如 overflow 不是 visible),其顶部和底部外边距可能与其第一个或最后一个子元素的外边距折叠;
- 空的块级元素:当元素 B 的 margin-top 直接贴到元素 A 的 margin-bottom 的时候(也就是中间的元素没有内容),也会发生边界折叠。
外边距折叠可能会导致布局问题,例如元素之间的间距不正确或父元素的高度计算错误。为了避免边距折叠,可以采取以下方法:
- 添加内边距(padding)或边框(border):给其中一个元素添加非零的 padding 或 border 可以阻止外边距重叠;
- 创建 BFC(Block Formatting Context):可以通过设置以下 CSS 属性之一来创建一个 BFC,这通常会阻止外边距折叠的发生:
- float 属性不为 none;
- position 属性值为 absolute 或 fixed 且 z-index 不是 auto;
- display 属性值为 inline-block;
- display 属性值为 table-cell;
- display 属性值为 table-caption;
- display 属性值为 flex;
- display 属性值为 grid;
- display 属性值为 flow-root;
- overflow 属性值不为 visible。
4. HTML head 部分常见的标签及作用
title
:定义文档的标题,显示在浏览器的标题栏或标签页上。
meta
:用于提供关于文档的元数据信息,如字符集声明(<meta charset="UTF-8">
)、描述文档内容(用于搜索引擎优化)、设置视口(用于移动端适配)等。
link
:主要用于链接外部的样式表文件(如 CSS 文件)。
script
:用于嵌入或引用 JavaScript 脚本文件。
style
:可以直接在头部定义内联的 CSS 样式。
5. CSS选择器
元素选择器:如
p
选择所有<p>
元素。类选择器:以
.
开头,如.myClass
选择所有具有myClass
类的元素。ID 选择器:以
#
开头,如#myId
选择具有特定 ID 的元素。后代选择器:如
div p
选择所有在<div>
元素内部的<p>
元素。子选择器:如
div > p
选择<div>
元素的直接子元素<p>
。相邻兄弟选择器:如
p + q
选择紧跟在<p>
元素后面的<q>
元素。通用兄弟选择器:如
p ~ q
选择<p>
元素后面所有的<q>
元素。属性选择器:如
[attribute]
选择具有指定属性的元素,[attribute=value]
等。伪类选择器:如
:hover
用于当鼠标悬停时,:active
用于元素被激活时等伪元素选择器:如
::before
可在元素之前添加内容,::after
可在元素之后添加内容。
6. 块、行的特性
块级元素特性:
- 独自占据一行,前后会有换行。
- 可以设置宽度、高度、内外边距等属性。
- 常见的块级元素如
<div>
、<p>
、<h1>
到<h6>
、<ul>
、<ol>
、<li>
等。行内元素特性:
- 不会独自占据一行,而是与其他行内元素在同一行显示,直到一行排满。
- 宽度和高度通常不能直接设置(某些情况下可以通过特定方式设置)。
- 内边距和外边距的水平方向有效,垂直方向的设置对布局影响较小。
- 常见的行内元素如
<span>
、<a>
、<img>
等。
7. Flex布局及相关属性的计算方法
flex-direction
:决定主轴的方向,即子元素的排列方向。可以是水平的(row
或row-reverse
)或垂直的(column
或column-reverse
)。justify-content
:定义了子元素在主轴上的对齐方式。可选值包括flex-start
(左对齐或顶部对齐)、flex-end
(右对齐或底部对齐)、center
(居中对齐)、space-between
(两端对齐,子元素之间均匀分布)和space-around
(每个子元素周围均匀分布空白)。flex-wrap
:确定子元素是否换行。可以是nowrap
(不换行)、wrap
(换行)或wrap-reverse
(反向换行)。align-items
:设置子元素在侧轴上的对齐方式。可能的值有flex-start
(顶部对齐或左对齐)、flex-end
(底部对齐或右对齐)、center
(垂直居中对齐)、stretch
(拉伸以填充容器高度或宽度)和baseline
(使子元素的基线对齐)。align-content
:用于多行 Flex 容器,定义了行在侧轴上的对齐方式。类似于justify-content
,但适用于多行情况。flex-grow
:指定子元素的扩展比例。当容器有剩余空间时,子元素可以按照比例扩展以填充空间。flex-shrink
:定义子元素的收缩比例。当容器空间不足时,子元素可以按照比例收缩。flex-basis
:设置子元素在主轴上的初始大小。可以是固定值、百分比或auto
(根据子元素的内容自动计算大小)。flex
:是flex-grow
、flex-shrink
和flex-basis
的简写属性。计算 Flex 布局中子元素的大小和位置时,通常会考虑以下因素:
- 主轴和侧轴的方向。
- 子元素的
flex-grow
、flex-shrink
和flex-basis
属性。- 容器的大小和可用空间。
- 子元素的内容大小。
可参考此文:Flex布局——flex-basis、flex-grow、flex-shrink_flex布局做页面,flex-basis、flex-grow、flex-shrink的计算方法-CSDN博客
8. BFC是什么
BFC 即块级格式化上下文(Block Formatting Context),是 CSS 中的一个概念,用于描述页面中块级元素如何布局、定位和相互影响的一种机制。BFC 是一个独立的渲染区域,具有一定的规则来决定其中元素的排布方式。
BFC 的主要作用包括:
- 防止 margin 重叠:同一个 BFC 下的两个相邻块级元素,会发生上下方向的 margin 重叠。
- 实现两栏布局:可以防止文字环绕,实现左侧固定、右侧自适应的两栏布局。
- 解决高度塌陷问题:当子元素设置浮动时,父元素可能会出现高度塌陷。通过创建 BFC,可以让父元素包含浮动的子元素,从而解决高度塌陷问题。
创建 BFC 主要有以下几种方法:
- 根元素(
<html>
)自动创建 BFC。- 使用
overflow
属性,将其值设置为除visible
以外的值,如hidden
、auto
、scroll
等。- 使用
float
属性,将元素的float
值设置为left
或right
。- 使用
display
属性,将其值设置为inline-block
、table-cell
、table-caption
、flex
、inline-flex
等。- 使用
position
属性,将其值设置为absolute
或fixed
。
9. 如何实现垂直、水平居中的布局
利用 Flex 布局:
.parent { display: flex; justify-content: center; align-items: center; }
利用绝对定位结合平移:
.parent { position: relative; } .child { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
利用表格布局:
.parent { display: table-cell; vertical-align: middle; text-align: center; }
利用伪元素:
.parent::before { content: ''; display: inline-block; height: 100%; vertical-align: middle; } .child { display: inline-block; vertical-align: middle; }
10. 响应式媒体查
通过编写一系列的媒体查询规则,可以使网页在不同尺寸的设备上呈现出合适的布局和外观。例如:
@media screen and (max-width: 768px) { /* 当屏幕宽度小于等于 768 时的样式 */ }
11. 移动端动态适配方案
- viewport 元标签设置:通过设置
<meta name="viewport" content="width=device-width, initial-scale=1.0">
来确保页面以设备的实际宽度进行显示和缩放。- 使用相对单位:如百分比、em、rem 等,让布局元素根据屏幕尺寸相对变化。
- Flex 布局或 Grid 布局:这些现代布局方式能够更灵活地实现元素的排列和适配。
- 图片资源适配:根据不同屏幕分辨率提供不同尺寸的图片,或者使用
srcset
属性让浏览器自动选择合适的图片。- JavaScript 动态计算:通过脚本在运行时根据设备特性调整一些元素的样式或尺寸。
- 媒体查询与断点设置:如前面提到的,根据不同屏幕宽度范围设置不同的样式。
- 移动端框架和库:如 Bootstrap 等,提供了一套现成的移动端适配解决方案。
- 响应式设计模式:例如流式布局、混合布局等,以适应多种设备形态。
- 服务器端检测与响应:在服务器端根据请求的设备信息发送相应适配好的内容。
- UI 组件库的使用:许多 UI 组件库本身具备良好的移动端适配能力。
12. CSS3常见特效如圆角、阴影、形变、过渡、动画
圆角(border-radius):
可以让元素的边角变得圆润,例如
border-radius: 10px;
使元素四个角都有半径为 10 像素的圆角。阴影(box-shadow):
为元素添加投影效果,如
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
表示水平偏移量、垂直偏移量、模糊半径和阴影颜色。形变(transform):
- 平移(translate):如
transform: translate(10px, 20px);
水平移动 10 像素,垂直移动 20 像素。- 旋转(rotate):如
transform: rotate(45deg);
旋转 45 度。- 缩放(scale):如
transform: scale(1.5);
放大 1.5 倍。- 倾斜(skew):如
transform: skew(10deg, 20deg);
等。过渡(transition):
可以让元素在特定属性发生变化时,产生平滑的过渡效果,如
transition: all 0.3s ease;
表示所有属性变化在 0.3 秒内以特定的缓动效果过渡。动画(animation):
通过定义关键帧来创建更复杂的动画效果。比如
@keyframes myAnimation { 0% { opacity: 0; } 100% { opacity: 1; } } div { animation: myAnimation 2s; }
13. 目前常见的js数据类型
原始数据类型(Primitive types):
- Number:表示数字,包括整数和小数。
- String:字符串。
- Boolean:布尔值,只有
true
和false
两个值。- Undefined:变量声明未初始化时的值。
- null:表示空值或不存在的对象引用。
引用数据类型(Reference types):
- Object:对象,可以包含各种属性和方法。
- Array:数组。
- Function:函数。
特殊的数据类型或概念:
- Symbol 类型:它是一种独一无二且不可变的数据类型,主要用于创建对象的唯一属性。
- BigInt 类型:用于表示任意精度的整数。
其余参考16基础类型与引用类型。
14. var、let、const 的区别
可参考此文:面试官:说说var、let、const之间的区别 | web前端面试 - 面试官系列
作用域方面:
var
:只有函数作用域。let
和const
:有块级作用域,如在循环体、条件语句块等中创建的变量不会影响到外部。变量提升:
var
:存在变量提升,即在其声明之前就可以使用,只是值为undefined
。let
和const
:不存在变量提升,在声明之前使用会导致错误。重复声明:
var
:可以在同一作用域内重复声明同一个变量。let
和const
:不允许重复声明。暂时性死区:
let
和const
:存在暂时性死区,即只要进入其所在的块级作用域,在此之前对该变量的操作都是非法的。可变性:
var
:可以随意改变变量的值。const
:通常用来声明常量,不能再重新赋值,但如果所指向的是对象或数组,可以修改其内部的元素或属性。let
:可以改变变量的值。比如以下示例能更清晰地体现它们的区别:
function example() { var a = 1; if (true) { var a = 2; // 可以重复声明 } console.log(a); let b = 3; if (true) { // let b = 4; // 报错,不能重复声明 } console.log(b); const c = 5; // c = 6; // 报错,不能重新赋值 const d = { key: 'value' }; d.key = 'newValue'; // 可以修改对象属性 }
15. 布尔与false值
在 JavaScript 中,布尔类型只有
true
和false
两个值。但需要注意的是,有一些值在特定的情境下会被视为等同于
false
,这些被称为“假值”,包括:
false
本身。0
(数字 0)。""
(空字符串)。null
。undefined
。
16. 基础类型和引用类型
基础类型(原始类型):
包括
Number
、String
、Boolean
、undefined
、null
。这些类型的数据直接存储在栈内存中,在赋值操作时会进行值的直接复制。引用类型:
主要有对象(包括普通对象、数组、函数等)。引用类型的数据存储在堆内存中,而在栈内存中保存的是指向堆内存中实际数据的指针。当进行赋值操作时,实际上是复制了这个指针,使得两个变量指向同一块堆内存中的数据。
例如:
// 基础类型示例 let num1 = 5; let num2 = num1; num2 = 10; console.log(num1); // 5,num1 的值未受影响 // 引用类型示例 let obj1 = { name: 'John' }; let obj2 = obj1; obj2.name = 'Jane'; console.log(obj1.name); // 'Jane',因为 obj1 和 obj2 指向同一个对象
17. typeof 的返回值
undefined
:表示未定义的变量或值。boolean
:表示布尔类型的变量或值。string
:表示字符串类型的变量或值。number
:表示数字类型的变量或值,包括整数和浮点数。bigint
:表示大整数类型的变量或值。symbol
:表示符号类型的变量或值。object
:表示对象类型的变量或值,包括普通对象、数组、函数等。function
:表示函数类型的变量或值。需要注意的是,
typeof
运算符的返回值都是字符串,且都是小写形式。此外,对于一些特殊情况,如null
被视为对象类型,NaN
被视为数字类型等,需要特别注意。
18. 运算符结合性和优先级
- 优先级:
- 一元运算符(如
++
、--
、!
等)具有较高的优先级。- 乘法、除法和取模运算符(
*
、/
、%
)的优先级高于加法和减法运算符(+
、-
)。- 比较运算符(如
==
、!=
、<
、>
等)的优先级低于算术运算符。- 逻辑与运算符(
&&
)的优先级高于逻辑或运算符(||
)。- 赋值运算符(如
=
)的优先级较低。- 结合性:
- 大多数运算符具有左结合性,即从左到右执行。例如,加法运算符
+
和乘法运算符*
都是左结合的。- 赋值运算符
=
和三元运算符? :
是右结合的,即从右到左执行。
19. 流程控制语句
条件语句:
if...else
:根据条件的真假执行不同的代码块。switch
:根据表达式的值与不同的情况进行匹配并执行相应代码。循环语句:
for
:通过初始化、条件判断和迭代来执行循环操作。while
:只要条件为真就持续循环。do...while
:先执行一次代码块,然后再判断条件是否继续循环。跳转语句:
break
语句:用于跳出当前循环或switch
语句。当执行到break
语句时,程序会立即终止当前循环或switch
语句的执行,并继续执行循环或switch
语句后面的代码。continue
语句:用于终止当前循环的本次迭代,并开始下一次迭代。当执行到continue
语句时,程序会立即结束当前循环的本次迭代,并开始执行下一次迭代。return
语句:用于从函数中返回一个值,并结束函数的执行。当执行到return
语句时,程序会立即返回指定的值,并结束函数的执行。throw
语句:用于抛出一个异常。当执行到throw
语句时,程序会立即抛出一个异常,并将控制权转移到最近的catch
块中,以便处理异常。
20. 字符串操作
在 JavaScript 中,常见的字符串操作包括:
字符串连接:
let str1 = "Hello"; let str2 = "World"; let concatenatedStr = str1 + " " + str2;
获取字符串长度:
let str = "Hello"; let length = str.length;
提取子字符串:
let str = "HelloWorld"; // 从索引 5 开始,提取 5 个字符 let subStr = str.slice(5, 10);
查找字符或字符串的位置:
let str = "Hello World"; let position = str.indexOf("World");
替换字符串中的部分内容:
let str = "Hello World"; let newStr = str.replace("World", "Universe");
转换为大写或小写:
let str = "Hello"; let upperCaseStr = str.toUpperCase(); let lowerCaseStr = str.toLowerCase();
21. 0.1 + 0.2 === 0.3与大数相加
在 JavaScript 中,由于浮点数精度的问题,
0.1 + 0.2!== 0.3
,而是一个非常接近但不等于 0.3 的值。对于大数相加,也可能会遇到一些问题,比如可能会超出数值表示的范围,导致结果不准确。为了解决这些问题,可以使用一些专门的库或方法来进行高精度计算。
先理解为什么:
- IEEE 754 浮点数标准:JavaScript 中的数字是基于 IEEE 754 标准实现的双精度浮点数(64 位),其中最高 1 位为符号位(0 表示正数,1 表示负数),接下来的 11 位用于存储指数部分(范围大约从-1022 到 1023),剩余的 52 位用来存储尾数(或称小数部分)。
- 有限的尾数位数:虽然 52 位对于大多数数值来说已经足够精确,但对于一些特定的小数,特别是在二进制无法精确表示的情况下,例如像 0.1 和 0.2 这样的十进制小数,在转换为二进制时会出现无限循环小数。因此,它们只能被近似表示,这就导致了精度损失。
- 运算过程中的舍入误差:在进行加、减、乘、除等运算过程中,由于这些操作都是基于近似值进行的,所以结果可能进一步累积误差,特别是连续多次计算后,这种误差可能会变得明显。
如何解决:
方法一:使用第三方库
比如
mathjs
等,这些库提供了更精确的数学计算功能。方法二:转换为整数后计算
将浮点数乘以一个适当的倍数转换为整数进行计算,计算完成后再除以该倍数转换回浮点数。例如:
const num1 = 0.1; const num2 = 0.2; const int1 = num1 * 10; const int2 = num2 * 10; const result = (int1 + int2) / 10;
方法三:设置精度
可以自己编写一些函数来设置和处理一定的精度。
function setPrecision(num, precision) { return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision); }
方法四:固定小数位数后再比较或运算:
function fixDecimal(num, decimalPlaces) { const factor = Math.pow(10, decimalPlaces); return Math.round(num * factor) / factor; }
方法五:使用误差范围来判断:
在比较两个浮点数是否相等时,可以设置一个合理的误差范围,只要它们的差值在这个范围内就认为是相等的。
const tolerance = 0.00001; const isEqual = Math.abs(num1 - num2) < tolerance;
22. 数组操作API
添加元素:
push()
:在数组末尾添加一个或多个元素。unshift()
:在数组开头添加一个或多个元素。删除元素:
pop()
:删除数组末尾的元素并返回该元素。shift()
:删除数组开头的元素并返回该元素。获取元素:
at()
:通过索引获取元素(支持负数索引)。查找元素:
indexOf()
:返回指定元素在数组中首次出现的索引,如果没有则返回-1
。includes()
:判断数组是否包含指定元素。截取数组:
slice()
:返回一个新的数组,包含指定范围内的元素。修改数组:
splice()
:用于添加、删除或替换数组中的元素。遍历数组:
forEach()
:对数组中的每个元素执行一个函数。map()
:创建一个新数组,其元素是原始数组元素经过函数处理后的结果。filter()
:创建一个包含通过测试的元素的新数组。排序:
sort()
:对数组进行排序。连接数组:
concat()
:连接多个数组。
23. JavaScript数组的map方法的实现原理
JavaScript 数组的
map
方法的实现原理是通过遍历数组中的每个元素,对每个元素执行指定的函数,并将函数的返回值存储在一个新的数组中,最后返回这个新数组。以下是一个简单的
map
方法实现示例:Array.prototype.myMap = function(callback) { const newArray = []; for (let i = 0; i < this.length; i++) { newArray.push(callback(this[i], i, this)); } return newArray; };
24. 深拷贝原理和实现
深拷贝的原理主要是创建一个与原始对象完全独立的副本,包括对象的所有属性及其嵌套的对象或数组等,使得对副本的修改不会影响到原始对象。
以下是一个使用 JavaScript 实现深拷贝的示例代码:
function deepCopy(obj) { if (typeof obj!== 'object' || obj === null) { return obj; } let newObj = Array.isArray(obj)? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = deepCopy(obj[key]); } } return newObj; }
在这个实现中,首先判断对象是否为基本类型,如果是则直接返回。然后根据对象类型创建相应的空副本。接着通过遍历原始对象的属性,对每个属性值进行递归的深拷贝并赋值给新副本。
25. 词法作用域、立即执行函数表达式
词法作用域:
词法作用域是指变量的作用域是在代码编写时就确定好的,而不是在运行时动态确定的。它遵循就近原则,即一个变量在哪个函数中定义,那么在这个函数及其嵌套的函数中都可以访问到该变量,而在外部则无法直接访问。这是由代码的词法结构决定的,而不是由函数的调用顺序决定的。
立即执行函数表达式(IIFE):
立即执行函数表达式是一种特殊的函数定义和执行方式。它的形式通常是
(function() {...})()
或者(function(...){...})()
。这样的写法可以创建一个临时的函数,并立即执行它,常用于创建一个独立的作用域,避免污染全局变量,同时可以在其中进行一些初始化操作或执行特定的逻辑。以下是一个结合了词法作用域和立即执行函数表达式的例子:
(function() { var innerVariable = '内部变量'; console.log(innerVariable); })();
在这个例子中,通过立即执行函数表达式创建了一个独立的作用域,其中定义的
innerVariable
只能在这个作用域内被访问,不会影响到全局作用域。这体现了词法作用域的规则,同时也利用了立即执行函数表达式的特性。
26. 闭包
闭包是指一个函数和与其相关的引用环境组合在一起。
具体来说,当一个函数在其定义的环境(比如包含该函数的外部函数)之外被调用时,仍然能够访问在其定义环境中的变量,并且这些变量的状态得以保留,这就形成了闭包。
function outerFunction() { let outerVariable = '我是外部变量'; return function innerFunction() { console.log(outerVariable); }; } let closureFunction = outerFunction(); closureFunction();
在这个例子中,
innerFunction
就是一个闭包,它在outerFunction
执行完后依然能访问到outerFunction
中的outerVariable
。闭包有很多重要的用途,比如实现数据隐藏和封装、创建函数工厂、实现回调函数等
- 数据隐藏和封装:可以将一些数据和相关操作封装在闭包内,防止外部直接访问和修改,只通过特定的函数接口来操作。
- 创建函数工厂:通过闭包来生成具有特定行为和关联数据的函数。
- 实现回调函数:在异步操作或事件处理中,利用闭包保存相关状态和上下文信息。
- 模块模式:创建自包含的模块,其中模块内的函数可以通过闭包访问模块内的私有数据。
- 记忆功能:让函数记住之前的计算结果或状态,例如在一些计算函数中实现缓存机制。
- 模拟类的行为:通过闭包来模拟类的属性和方法。
- 处理循环中的变量:在循环中创建的函数可以通过闭包正确访问到每一轮循环中的特定变量值。
在使用闭包时,需要注意以下一些问题:
- 内存泄漏风险:如果闭包引用了大量不再需要的数据,可能导致这些数据不能被及时释放,造成内存泄漏。要注意及时清理不必要的引用。
- 循环引用:闭包内的对象和外部对象之间可能形成循环引用,这也可能引发内存管理问题。
- 变量作用域:要清楚理解闭包内可以访问到的变量范围,避免出现意外的变量覆盖或访问错误。
- 性能影响:过多复杂的闭包使用可能在一定程度上影响性能,尤其是大量闭包同时存在时,需要合理评估性能开销。
- 可读性和可维护性:闭包可能会使代码的逻辑变得不那么直观,要注意保持代码的清晰可读,添加必要的注释。
- 异步场景下的注意事项:在异步操作中使用闭包时,要确保相关数据在需要时仍然有效,避免因异步流程导致的状态不一致问题。
27. 柯里化、回调函数
柯里化(Currying):
柯里化是把接受多个参数的函数转变为接受一个单一参数(最初函数的第一个参数)的函数,并且返回一个新的函数,新函数可以继续接收余下的参数,逐步求值。
柯里化的主要作用是将多参数的函数拆分成一系列单参数函数的组合,增加了函数的灵活性和可复用性。
例如,一个简单的加法函数可以进行柯里化:
function add(a, b) { return a + b; } function curriedAdd(a) { return function(b) { return add(a, b); }; } let add5 = curriedAdd(5); console.log(add5(3));
回调函数(Callback Function):
回调函数是作为参数传递给另一个函数,在特定的事件或操作完成后被调用的函数。它允许在异步操作或一系列操作中,在合适的时机执行自定义的逻辑。
比如在异步请求中,当请求成功或失败时调用相应的回调函数:
function fetchData(url, successCallback, failureCallback) { // 模拟异步操作 if (Math.random() > 0.5) { successCallback('Data fetched successfully'); } else { failureCallback('Error fetching data'); } } fetchData('example.com', function(result) { console.log(result); }, function(error) { console.error(error); });
28. ES6常用语法
let 和 const 声明变量:
let x = 10; const PI = 3.14;
箭头函数:
const add = (a, b) => a + b;
模板字符串:
const name = 'John'; const message = `Hello, ${name}!`;
类的定义:
class Person { constructor(name) { this.name = name; } sayHello() { console.log(`Hello, I'm ${this.name}`); } }
Promise 对象:
const promise = new Promise((resolve, reject) => { // 异步操作 resolve('成功'); });
for...of 循环:
const array = [1, 2, 3]; for (let item of array) { console.log(item); }
展开运算符:
const arr1 = [1, 2]; const arr2 = [...arr1, 3, 4];
对象属性简洁表示:
const name = 'Jack'; const person = { name };
29. 构造函数、对象、原型,原型链
构造函数:
构造函数是一种特殊的函数,用于创建特定类型的对象。它通常使用大写字母开头来命名以区分普通函数。通过使用
new
操作符调用构造函数可以创建新的对象实例。对象:
对象是包含属性和方法的数据结构。可以通过直接创建对象字面量或使用构造函数来创建对象。
原型:
每个构造函数都有一个
prototype
属性,指向一个对象,这个对象被称为原型对象。原型对象上的属性和方法可以被该构造函数创建的所有对象实例共享。原型链:
当访问一个对象的属性或方法时,如果该对象自身不存在,则会沿着原型链向上查找,直到找到该属性或方法或者到达原型链的顶端(通常是
Object.prototype
)。这样就形成了一条原型链,实现了继承和属性共享的机制。例如:
function Person(name) { this.name = name; } Person.prototype.sayHello = function() { console.log(`Hello, I'm ${this.name}`); }; const person1 = new Person('John'); const person2 = new Person('Jane'); person1.sayHello(); person2.sayHello();
在这个例子中,
Person
是构造函数,Person.prototype
是原型对象,person1
和person2
通过原型链可以访问到sayHello
方法。可参考此文:5分钟带你搞懂 构造函数、原型对象、实例对象、原型、原型链_简述一下对原型,构造函数以及实例的理解-CSDN博客
- Class写法
在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
class 的本质是 function。它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
ES6
Class
写法的示例和细节:class Vehicle { constructor(type) { this.type = type; } start() { console.log(`The ${this.type} is starting.`); } } // 继承 class Car extends Vehicle { constructor(type, color) { super(type); // 调用父类构造函数 this.color = color; } honk() { console.log('Beep beep!'); } } const myCar = new Car('Sedan', 'Red'); myCar.start(); myCar.honk();
还可以在类中定义静态属性和方法:
class MathUtils { static PI = 3.14159; static calculateCircleArea(radius) { return this.PI * radius * radius; } } console.log(MathUtils.PI); console.log(MathUtils.calculateCircleArea(5));
另外,可以在类中使用 getter 和 setter 方法来控制属性的访问和修改:
class Person { constructor(name) { this._name = name; } get name() { return this._name; } set name(newName) { this._name = newName; } } const person = new Person('Alice'); person.name = 'Bob'; console.log(person.name);
可参考此文:4.3 ES6 Class 类 | 菜鸟教程