一、HTML篇
1. script标签中的defer和async属性的区别
defer和async都是异步加载外部js文件,使得脚本的加载不会阻塞页面的解析。defer会在页面加载完成之后执行js脚本;async脚本的加载和页面的加载同时执行,当js脚本加载完成后会立即执行脚本,如果此时页面还未解析完,则会阻塞页面的解析,且多个async标签执行的顺序不可控。
2. 隐藏元素的方法有哪些
- display: none 元素不会在页面中占据位置,也不会响应绑定的监听事件,非继承属性。会导致页面重排重绘画。
- visibility: hidden 元素在页面中占据空间,不会响应绑定的监听事件,继承属性,会导致页面重绘。
- opacity: 0 元素在页面中占据位置,并且能够响应绑定的监听事件,会导致页面重绘。
3. link和import的区别
两者都是外部引入CSS的方式。
- link:link引用css时,在页面加载时同时加载。支持使用JS去控制DOM改变样式,兼容性良好
- @import:@import引用css时,需要页面加载完后加载;该方式存在兼容性问题,不支持使用JS控制DOM的样式改变。
4. 伪元素和伪类的区别
- 伪元素:在元素的前后添加额外的元素或样式,但是这些元素实际并不在文档中生成,只是在外部显示可见,不会在源代码中找到它们。
- 伪类:将特殊的效果添加到特定元素上,在已有元素上添加类别,不会产生新的元素。
总结:伪元素通过对元素的操作进行元素的改变,伪类是通过在元素上添加伪类改变元素状态。
5. 对盒模型的理解
css盒子模型把元素看作是一个盒子,盒子的内容包括:content、padding、border、margin四个部分组成。css盒子模型分为两种。
- 标准盒模型:width = content;盒子的总宽度=content + padding + border。
- IE盒模型(怪异盒模型):width就是盒子的宽度 即widht = content + padding + border
两种盒模型的切换可以使用:box-sizing: content-box/border-box(ie盒模型)
6. CSS优化和提高性能的方法有哪些
- css压缩:将写好的css进行打包压缩,减少文件体积。
- 减少使用@import,建议使用link 后者会在加载页面的时候一起加载,前者需要等待页面加载完成后进行加载。
- 避免使用通配符,只对需要用到的元素进行选择。
- 尽量少的对标签进行选择,而是使用类。
- 了解那些属性是可以通过继承而来的,避免重复对这些属性赋值。
- 慎重使用高性能属性:浮动、定位。
- 属性为0时不加单位。
- 使用css雪碧图,减少页面的请求次数。
7. sass、less是什么?为什么要使用它们
1. 是什么
它们都是css预处理器,是css上的一种抽象层。它们是一种特殊的语法会被编译成css。可以将css赋予更多动态语言的特性,如变量、继承、运算、函数。
2. 使用sass、less的好处
使用sass和less可以样式结构更加清晰、便于扩展。方便的屏蔽浏览器私有语法差异;轻松实现多重继承,完全兼容css代码。css预处理器(Sass、Less)支持我们写一种类似于CSS但实际并不是CSS的语言,然后把它编译成css代码。
8. 说说对postcss的理解
postcss和sass、less一样仍然是对css进行解析和处理的工具。它将旧的css代码解析转换成新的css代码。它和sass不同就在于,sass/less是处理类css语言而postcss处理的就是css本身。其作用类似于babel将高版本的js转换为低版本的js。它可以将尚未被浏览器广泛支持的css语法,自动为一些需要额外兼容的语法添加前缀。
9. 如何用webpack处理css
webpack本身是一个面向js的模块化打包工具。处理css时需要借助loader的作用。分别是css-loader /less-loader导入css模块对css代码进行编译处理;style-loader: 创建style标签,把css内容写入标签。
10. 什么是 HTML 语义化?为什么要语义化?
1. 是什么
HTMl语义化是指在编写HTML代码时,使用语义化标签来描述页面的结构和内容。常见的语义化标签:header、nav、mian、aside、article、footer
2. 使用语义化标签的好处
- 有利于SEO(搜索引擎优化)
- 增强了代码的可读性和可维护性,有利于开发人员阅读和维护代码。
总结:HTMl语义化使得页面更加易读、易维护、易优化。
二、CSS篇
1. 如何判断元素是否达到可视区域
可以通过以下的属性判断一个元素是否已经进入到可视区域。
- Window.innerHeight: 浏览器可视区域的高度。
- Document.dody.scrollTop || document.documentElement.scrollTop:浏览器滚动过的距离。
- Imgs.offsetTop 是图片imgs元素顶部距离文档顶部的高度(包括滚动距离)。
- imgs到达显示内容区域:imgs.offsetTop < window.innerHeight + document.dody.scrollTop。
关于元素的offsetTop、clientWidht和scrollWidth相关知识点,请查看元素位置相关属性。
2. z-index属性在什么情况下会生效
当元素的position属性是relative、absolute或者fixed时才会生效。
3. 对Flex布局的理解以及使用场景
任何一个元素都可以指定为Flex布局(包括行内元素)。注意当元素设为Flex布局后,子元素的float、clear和vertical-align属性失效。采用Flex布局的元素称为容器,它的所有子元素自动成为容器成员,称为项目。
作用在容器上的属性
- Flex-direction:定义主轴的方向
- Flex-wrap: 如果一条主轴线放不下,如何换行
- Flex-flow: flex-direction和flex-wrap的缩写。默认值 row nowrap
- Justify-content: 项目如何在主轴上对齐
- Align-items:项目在交叉轴如何对齐
作用在项目上的属性
- Order: 项目的排序顺序。数值越小越靠前
- Flex: flex-grow、flex-shrink和flex-basix的缩写。默认值 0 1 auto
- Align-selft: 允许单个项目与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承align-items属性
flex:1表示什么
flex属性是flex-grow、flex-shrink和flex-basix的简写。Flex: 1 等同于felx: 1 1 0%。第一个参数表示flex-grow项目放大比例。默认为0,即不放大(即使空间足够大);Flex-shrink表示项目缩小比列。默认值为1,即空间不够时自动缩小;Flex-basis用来计算项目是否有多余空间,默认值auto即项目本身大小
4. 响应式的基本概念?如何实现
响应式布局是指通过一些技术手段,使得我们编写的网站能够在不同的设备上(桌面电脑、手机、平板电脑等)能够自适应的呈现最佳用户体验。
实现方式
- CSS媒体查询:通过@media查询不同设备的尺寸,设置不同的根元素html的font-size属性
- 流体网格布局和百分比尺寸
- 使用flex布局
5. 定位与浮动
浮动
容器不设置高度的情况下且子元素为浮动时。容器的高度不能被内容撑开。导致容器内的内容溢出到容器外进而影响布局。这种现象被称为浮动。
清楚浮动的方式
- 给父元素定义height属性
- 最后一个浮动元素后添加一个空div标签。并且在该元素上添加clear:both样式
- 给父元素添加overflow:hidden/auto触发BFC
- 使用after伪元素
.clearfix:: after {content: '';display: block;clear: both}。因为clear属性只有在块级元素上才生效。而after等伪元素是内联元素,所以需要display:block;
6. 对BFC的理解,如何创建BFC
BFC是一个独立的布局环境,可以理解为一个容器,在这个容器中的按照一定的规则进行物品摆放,并且影响到其它环境的物品摆放。如果一个元素触发了BFC的条件,则BFC中的元素布局不会受外部布局的影响。
1. 触发BFC的条件
- 根元素
- 元素设置浮动:
float: left/right - 元素设置绝对定位:
position: absolute/fixed - Overflow: hidden/auto/scroll
2. BFC的作用
- 解决margin的重叠问题
- 解决元素高度坍塌问题:在对子元素设置了浮动后,父元素会发生高度坍塌,也就是父元素的高度变为了0。只要触发父元素为BFC即可。
overflow:hidden/auto/scroll - 创建自适应两栏布局:左边宽度固定,右边宽度自适应
7. CSS中的1像素问题是什么?有哪些解决方案?
css中的1px并不能和移动设备上的1px划等号。像素分为:逻辑像素和物理像素。
- 物理像素:移动设备出厂时,不同的设备自带不同的像素,也称为硬件像素
- 逻辑像素:css中记录的像素
UI设计师要求的1px是指设备的物理像素1px,不同的移动端设备物理像素呈现出的1px各有差异 比如在Iphone上1px就会被展示成2px,显得比较粗
解决方案:transform: scale(0.5)
8. CSS加载会阻碍页面的加载吗
页面加载的过程是:根据HTML文件生成DOM树,根据CSS文件生成CSSOM树(DOM解析和CSS解析是并行的,CSS加载不会阻塞DOM树的解析),根据CSSOM和DOM树生成Render树,Render Tree依赖DOM Tree和CSSOM Tree,所以必须等到CSSOM Tree加载完成开始渲染(所以CSS加载是会阻塞页面的渲染)由于js可能会操作前面的dom和样式,因此浏览器会维持html中css和js的顺序,css会在后面的js执行执行前加载完毕,所以css会阻塞后面js脚本的执行。
结论:css加载不会阻塞dom树的解析;css加载会阻塞cssom树的渲染;css加载会阻碍后面js脚本的执行。
9. CSS工程化
CSS工程化是指将CSS在项目中进行模块化、组件化、可维护性和可重用性等方面的处理,以达到工程化管理CSS的目的。在CSS工程化中,通常会使用一些相关的工具来辅助管理CSS,常用的工具包括:
- CSS预处理器:Sass、Less、Stylus等,可以通过变量、函数、混合等方式,提高CSS的可维护性和可重用性。
- CSS后处理器:PostCSS,可以来处理CSS代码,通过插件的形式可以实现一些自动化处理,比如自动添加前缀、压缩CSS等。
- CSS模块化工具:CSS Modules、styled-components等,可以将CSS样式与组件代码结合在一起,提高CSS的可维护性和可重用性。
- CSS命名规范:BEM、SMACSS、OOCSS等,可以通过规范化CSS的命名方式,提高CSS的可读性和可维护性。
总结:通过使用这些工具,可以提高CSS的可维护性、可重用性和可读性,达到CSS工程化的目的,从而使得前端开发更加高效、简洁。
10. CSS有哪些常用单位?这些单位各有什么区别?
CSS中常用的单位:
- px(像素):像素是相对于显示器分辨率而言的,是一个固定的单位。1px可以被理解为显示器上的一个物理像素点,其大小和颜色由显示器控制。
- em(相对长度单位):em是相对于父元素的
font-size大小的单位。当父元素没有设置字体大小时,em相对于浏览器的默认字体大小。 - rem(相对长度单位):rem同样也是相对长度单位,但是相对于根元素(html元素)的
font-size的大小。 - %(百分比):百分比是相对于父元素的宽度、高度和字体大小等的百分比。
- vw/vh(视口单位):vw和vh分别代表视口的宽度和高度。
em和rem的区别:em和rem都是相对长度单位,但em的大小是相对于父元素的字体大小,而rem是相对于根元素(html元素)的字体大小。因此,rem更加稳定,能够更好的控制页面布局和字体大小。
总结:一般情况下,像素是最常用的单位,它在大多数情况下都能提供良好的显示效果。对于需要在不同设备上适配的情况,可以使用em、rem、vw/vh等相对单位。
11. 常见的CSS性能优化的操作或技巧
以下是一些常用的 CSS 性能优化操作和技巧:
- 使用合适的选择器:选择器的复杂度会影响 CSS 渲染性能。尽量使用简单的选择器,避免使用通配符和后代选择器等复杂的选择器。
- 避免使用 @import:@import 可以在 CSS 文件中导入其他 CSS 文件,但它会阻塞页面的渲染,影响性能。建议使用 link 标签来引入 CSS 文件。
- 避免使用 !important:!important 会影响 CSS 属性的优先级,而且会增加解析和渲染时间。建议尽量避免使用 !important。
- 避免使用 inline 样式:inline 样式的优先级最高,但它会增加 HTML 文件的大小,降低页面的加载速度。建议使用外部 CSS 文件和内部样式表。
- 压缩和合并 CSS 文件:压缩和合并 CSS 文件可以减小文件大小,提高页面加载速度。可以使用工具例如 CSSMin 和 YUI Compressor 等来进行压缩和合并操作。
- 使用 CSS Sprites:CSS Sprites 可以将多个小图片合并成一张大图片,并使用 CSS 来显示不同的部分,减少 HTTP 请求次数,提高页面加载速度。
- 避免过度继承:过度继承会导致样式的冗余和继承链的深度增加,影响 CSS 的解析和渲染性能。
- 避免使用高消耗的属性和值:某些属性和值的计算成本比较高,例如 box-shadow、border-radius 等,应该尽量避免使用。
总结:合理使用以上CSS性能优化操作和技巧,可以帮助提高页面的加载速度和渲染性能。
三、JavaScript篇
1. instanceof操作符的实现原理以及实现
判断构造函数的prototype是否出现在某个实例对象的原型链上。
实现
const myInstanceof = (obj, Fn) => {
if (typeof l === 'number' || typeof l === 'boolean' || typeof l === 'string') {
return false;
}
let proto = Object.getPrototypeOf(obj);
const prototype = Fn.prototype;
while (proto) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false
}
myInstanceof([], Array); // true;
myInstanceof(1, Number); // false;
2. 如何安全的获取undefined的值
void xxx:void并不改变表达式的值,只是让表达式不返回值。
3. 如何进行隐式类型转换
隐式类型的转换可以概括为一个函数:
const objToNumber = value => Number(value.valueOf().toString())
4. let、const和var的区别
- 块级作用域:块级作用域由{ }包括,let和const具有块级作用域,var不存在块级作用域。
- 变量提升:var存在变量提升,let和const不存在变量提升,即变量只能在声明后使用,否则会报错。
- 给全局添加属性:var声明的变量为全局变量,并且会将该变量添加为全局对象的属性;但是let和const不会。
- 重复声明:var可以重复声明变量,后面声明的变量会覆盖之前的声明的变量;const和let不允许重复声明变量。
- 暂时性死区:在使用let和const命令声明变量之前,该变量都是不可以使用的,在语法上称之为暂时性死区。使用var声明的变量不存在暂时性死区。
- 初始值设置:在声明变量时,var和let可以不用设置初始值。const声明变量必须设置初始值。
5. 箭头函数和普通函数的区别
- 箭头函数没有自己的this。
- 没有prototype和arguments属性。
- 不能当作构造函数new。
- Call、apply和bind不能改变箭头函数的this指向。
6. proxy可以实现什么功能
在Vue3.0中通过Proxy来替换原本的object.defineProperty来实现数据响应式。Proxy是ES6中新增的功能,它可以用来自定义对象中的操作。
7. 对res参数的理解
扩展运算符被用在函数的形参上,它还可以把一个分离的参数序列整合成一个数组。
8. ES6中模板字符串的优势
在模板字符串中,空格、缩进、换行都会被保留(可以无障碍的直接编写HTMl代码);支持运算式的表达式,可以在${}完成一些计算。
9. Map和Object的区别
- 键的类型:Map的键可以是任意值,包括函数、对象或任意基本类型;Object的键必须是字符串或者Symbol
- 键的顺序:Map中的键是有顺序的。因此当迭代的时候,Map对象是以插入的顺序返回键值;Object的键是无序的
- 性能:Map在频繁增删键值对的场景下表现更好;Object在频繁删除键值对场景下未作优化
- Map实际上是一个数组,它的每一个数据都是一个数组,其形式如下:
const map = [['name':'test'], ['age', 18] - Map数据结构有以下操作方法:
- Size: 返回Map数据结构的成员总数
- Set(key, value): 设置对应的键名和键值,然后返回整个Map结构,如果key已经有值,则会更新值
- Get(key):读取key对应的键值
- Has(key)、delete(key)、clear(): 清楚所有成员
- Entries(): 返回Map中所有的成员
- forEach(): 遍历Map的所有成员
10. weakMap
weapMap对象也是一组键值对的集合,其中的键是弱引用。键名必须是对象,原始数据类型不能作为键名,键值任意。
11. JavaScript脚本延迟加载的方式有哪些
延迟加载:等页面加载完成后再去加载javascript文件。js延迟加载有助于提高页面的加载速度。
- defer属性:给js脚本添加defer属性,这个属性会让js脚本的加载与文档的解析同步进行,在文档加载完成后再执行js脚本。不会导致页面的渲染被阻塞。而且多个设置了defer属性的脚本最后是顺序执行。
- async属性:给js脚本添加async属性。这个属性会使脚本异步加载,不会阻塞页面的渲染。但是当脚本加载完成后会立即执行js脚本。这个时候如果文档还没有加载完那么js的执行可能会阻塞页面的加载。并且多个async属性的脚本执行顺序是不可预测的即是无序的执行。
- 动态创建DOM方式:对文档的加载进行监听,当文档加载完成后再动态的创建scrip标间来加载js脚本
- 使用setTimeout:设置一个定时器来延时加载js脚本。
- 让js最后加载:将js脚本放在文档底部,来使得脚本尽可能在最后加载执行。
12. Unicode、UTF-8、UTF-16和UTF-32的区别
Unicode: Unicode Translation Format 统一码、万国码、单一码。可以理解为ASCII码的超集。Unicode是为了解决传统的字符编码方案局限性而产生。它为每种语言中每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换和处理的的请求。Unicode的实现方式有很多种,常见的有UTF-8、UTF-16、UTF-32和USC-2。
UTF-8:是使用最广泛的Unicode编码方式,它是一种可变长的编码方式,可以完全兼容ASCII码的128个字符。注意:UTF-8是一种编码方式,Unicode是一个字符集合。
总结:Unicode是编码字符集(字符集),而UTF-8、UTF-16以及UTF-32是字符集编码(编码规则)
13. 什么是DOM、BOM
DOM指的是文档对象模型(Document Object Model),它把文档当作一个对象,这个对象主要定义了处理网页内容的方法和接口。
BOM指的是浏览器对象模型(Broswer Object Model),它把浏览器当作一个对象来处理。这个对象主要定义了与浏览器进行交互的方法和接口。
BOM的核心是window,而window具有双重角色,它既是通过js访问浏览器的一个接口,又是一个全局对象。这意味着网页中定义的任何对象、变量以及方法都作为全局对象的一个属性和方法存在。window对象还包含location、navigaotr和screen等浏览器的对象。
14. 对Ajax的理解
AJAX是Asynchronous JavaScript and XML的缩写,指的是通过JavaScript的异步通信,从服务器获取XML文档从中提取数据,再更新当前网页的对应部分,而不再刷新整个网页。
15. ES6模块与CommonJS模块的区别
模块化的作用:避免全局污染,方便依赖管理。
1. CommonJs
在Node中使用的比较多,webpack对ConmmonJs支持和转换,前端代码在编译之前也可以使用CommonJS进行开发。
特点
- 模块由js运行时实现
- 单个值导出,本质上导出的就是exports属性
- 支持动态加载
- 模块同步加载并执行模块文件
在commonjs中每一个js文件都是一个单独的模块,我们可以称之为module。在该模块中,包含commonjs规范的核心变量:
- exports:负责对模块中的内容进行导出
- module.exports:同exports
- require:导入其它模块(自定义模块、系统模块、第三方模块)还可以实现动态加载。 require本质就是一个函数,函数可以在任意上下文中执行,来自由的加载其它模块的属性和方法
Module.exports和exports的区别:
module.exports的本质就是exports。如果不想在commonjs中导出对象,而只是导出一个类或者一个函数或者其它属性的情况下,使用module.export就比较方便。exports会被初始化一个对象,也就是我们只能在该对象上绑定要导出的属性或者方法。但是可以通过在module.exports上自定义导出对象以外的其它类型。
2. ES Module
Nodejs借鉴了CommonJs实现了模块化,从es6开始,JavaScript才真正意义上有自己的模块化实现。在es module中用export来导出模块,import用来导入模块。
Es module的优势:
- 借助es module的静态导入导出实现了tree shaking
- Es module还可以import()懒加载方式实现代码分割
- 所有通过export导出的模块,在import中可以通过结构的方式,结构出来
- 支持混合导入/导出;可以使用export default 和export导出多个属性
- 支持重定向导出:export * from 'module'
- 动态导入:
promise = import('module')动态导入返回一个promise。为了支持这种配置,需要在webpack中做相应的配置处理
Es module的特性
- 静态语法:es module的导入和导出是静态的,import会自动提升到代码的顶层,import, export不能放在块级作用域或条件语句中
- 执行特性:与commonjs不同,commonjs模块同步加载并执行模块文件,Es module提前加载执行模块。Es6模块在预处理阶段分析模块,在执行阶段执行模块。两个阶段采用的都采用深度优先遍历
Import 的属性的特点:
- 使用import导入的模块运行在严格模式下
- 使用import导入的变量是只读的,可以理解为默认为const修饰,无法被重新赋值
总结:es modul是静态的,不能放在块级作用域内,可以导出多个属性和方法,单个导入导出,混合导入导出,es module模块提前加载并执行模块文件,es module导入模块运行在严格模式下,es module的特性可以实现tree shaking 和code spliting。
16. for in 和for of的区别
for…of是ES6新增的遍历方式,允许遍历一个有iterator接口的数据结构。如数组、map、类数组对象、字符串。并且返回各项的值;for…in主要是为了遍历对象而生,除了遍历对象本身的属性之外还会遍历原型链上的属性。
17. 原型与原型链
- 原型:在JavaScript中使用构造函数来创建一个对象,每一个构造函数内部都有一个prototype属性,它的属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的方法和属性。当使用构造函数新建一个对象时,在这个对象的内部包含一个指针__proto__,该指针指向构造函数的prototype属性。在ES5中新增了一个访问对象原型的方法:
Object.getPrototypeOf(obj)。 - 原型链:当访问一个对象属性时,如果该属性并不存在这个对象内部,那么它就会去找它的原型对象里的这个属性,这个原型又会有自己的原型,于是这样一直找下去就形成了一个链条。原型链的尽头一般都是
Object.prototype。
18. 对执行上下文、作用域和闭包的理解
- 闭包:闭包是指有权访问另一个函数作用域中变量的函数。当函数被作为返回值返回时和作为参数被传递时可能会出现闭包。
- 闭包的作用:可以在函数外部访问到函数内部的变量。可以通过这种方式创建私有变量。
19. 对作用域和作用域链的理解
作用域和作用域链是js中很重要的一个组成部分,作用域分为全局作用域、函数作用域和块级作用域。作用域链则可以访问不同作用域中的变量。
1. 作用域
作用域是可访问变量的集合。
全局作用域
- 最外层函数和最外层函数外面定义的变量拥有全局作用域。
- 所有未定义直接赋值的变量自动声明为全局作用域。
- 所有window对象的属性拥有全局作用域。
全局作用域过多有很大的弊端,过多的全局作用域变量会污染全局命名空间,容易引起命名冲突。
函数作用域:声明在函数内部的变量,一般只有固定的代码片段可以访问到。
块级作用域:使用ES6中新增的let和const指令可以声明块级作用域,块级作用域可以在函数中创建也可以在代码块中创建(用{}包裹的代码片段)。
在循环中比较适合绑定块级作用域,这样就可以把声明的计数器变量限制循环内部中。
2. 作用域链
在当前作用域中查找所需要的变量,如果当前作用域中没有该变量,就去父级作用域中查找,以此类推,直到访问到window对象就终止,这一层层的关系就是作用域链。
3. 执行上下文
指当前执行环境中的变量、函数声明,参数(arguments),作用域链,this等信息。
- 全局执行上下文:任何不在函数内部的都是全局执行上下文,它首先会创建一个全局的window对象,并且设置this的值等于这个全局对象,一个程序中只有一个全局上下文。
- 函数执行上下文:当一个函数被执行时,就会为该函数创建一个新的执行上下文。函数的上下文可以有多个。
20. 异步编程
异步编程技术使你的程序可以在执行一个可能长期运行的任务的同时继续对其他事件做出反应而不必等待任务完成。与此同时,你的程序也将在任务完成后显示结果。
1.异步编程的实现方式
- 回调函数方式。当出现多个回调函数嵌套时会造成回调函数地狱,导致代码耦合度太高,不利于代码维护。
- Promise方式。使用Promise的方式可以将多个嵌套的回调函数作为链式调用。但是这种方式,有时会造成多个then的链式调用,导致代码语义不够明确。
- generator方式。用同步的顺序来书写异步编程。
- async函数。async函数是generator和promise实现的一个自动执行的语法糖。将异步的逻辑转化为同步的顺序来书写。
2. 对Promise的理解
promise是异步编程的一种解决方案,可以获取异步操作的消息,它的出现大大改善了异步编程的困境,避免了回调地狱,它比传统的解决方案(回调函数)更合理也更强大。
Promise常用方法
- Then:可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象状态变为rejected时调用,第二个参数可以省略。
- Catch:该方法相当于then方法的第二个参数,指向reject的回调函数。不过catch方法还有一个作用,就是执行resolve回调函数时,如果出现错误,抛出异常,不会停止运行,而是进入catch方法中。
- All:该方法可以完成并行任务,它接受一个数组,数组的每一项都是一个promise对象。当数组中的promise的状态都达到resolved时,all方法的状态就变为resolved,返回结果为一个数组;如果有一个状态变成了rejected,那么alll方法的状态就变成了rejected,返回结果为最先被reject失败状态的值。
- Race:和all方法一样,接受的参数都是一个每项都是promise对象的数组。与all不同的是,有一个promise执行完后直接返回该promise对象。如果第一个promise对象变成rejected,那么race就是rejected状态,反之第一个promise变成了resolved那么race就是resolved的状态。
- Finally:用于指定不管promise对象最后的状态如何,都会执行的操作。finally方法不接受任何参数,这意味着没有办法知道,前面的promise状态到底是fulfilled还是rejected。这表明,fianlly方法里面的操作,是和状态无关的,不依赖于Promise的执行结果。
3. 对async/await的理解
async/await其实是generator的语法糖,它能实现的效果都能用then链来实现,它是为优化then链而开发出来的。从字面上来看,async是异步的简写,await则意为等待,可以理解为用async声明一个异步的函数,而用await等待异步函数的执行。并且await关键字只能出现在async异步函数中,async函数返回的是一个promise对象。如果在async函数中return一个直接量,async函数会把这个直接量通过promise.resolve()封装成promise对象
4. Promise和async/await的区别
- Promise本身是同步的立即执行函数。
- async函数返回一个Promise对象。
21. 并发和并行的区别
- 并发:并发是宏观概念,例如有AB两个任务,在一段时间内通过任务间的切换完成了这两个任务,这种情况可以称之为并发。
- 并行:并行是微观概念,假设CPU中存在两个核心,那么就可以同时完成AB两个任务。同时完成多个任务的情况称之为并行。
22. 垃圾回收
JavaScript代码运行时,需要分配内存空间来存储变量和值。当变量不再参与运行时,就需要系统回收被占用的内存空间,这就是垃圾回收。
1. 回收机制
JavaScript具有自动垃圾回收机制,会定期对那些不再使用的变量和对象所占有的内存进行释放。原理就是查找不再使用的变量并释放掉其占用的内存。JavaScript中存在两种变量,局部变量和全局变量。全局变量的生命周期会持续到页面卸载;而局部变量存在函数中,它的生命周期从函数的执行开始,直到函数执行结束,在这个过程中,局部变量会存储在堆栈中。函数执行结束时,这些局部变量不再被使用,所占的空间就会被释放掉。但是当局部变量被外部函数使用时,如闭包。这种情况在函数执行结束后,外部函数依然引用者函数内部的局部变量,该局部变量不会被释放掉。
2. 垃圾回收方式
浏览器常用的垃圾回收方式分为标记清楚和引用计数。
- 标记清楚:该方式是浏览器最常用的垃圾回收方式。当变量进入执行环境时,就标记这个变量进入环境,被标记进入环境的变量是不可以被回收的。当变量离开执行环境,就会被标记离开环境,被标记离开环境的变量会被回收释放掉其所占用的内存空间。
- 引用计数:跟踪记录每个变量被引用的次数。声明一个变量且将一个引用类型的值赋给该变量,则这个变量的引用次数+1,相反当这个变量的引用变量被覆盖,引用次数-1。当该变量的引用次数为0时,回收该变量并释放其所占的内存。
垃圾回收的代价比较大,所以需要尽量减少垃圾回收。减少垃圾回收的方式如下:
- 重置数组时,将数组的length设置为0而不是
[]。 - 重置对象,当对象不再被使用时设置对象为
null。 - 对函数进行优化:在循环中的函数表达式,如果可以复用,尽量将函数提在外层,减少函数的声明创建。
23. 内存泄漏
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
哪些情况会导致内存泄漏
- 意外的全局变量
- 被遗忘的定时器或回调函数
- 函数中存在对DOM的引用:js函数中保留了对某个DOM元素的引用
- 闭包:不合理的使用闭包,导致某些变量一直被留在内存中不能被释放
24. JS为什么被设计为多线程
JS被设计为单线程的主要原因是为了避免多线程所带来的复杂性。如果JS是多线程,那么在处理并发问题时,需要考虑锁和同步等一些列复杂的问题,这会增加代码的复杂度和开发难度。
JS最初是为了解决网页交互的问题而诞生,网页交付的需求大部分是基于用户事件的,比如点击按钮、输入文本等。这些操作的响应速度要求很高,如果在响应的时间内还要处理其它的任务,可能会网页卡顿,用户体验不佳等问题。
总结:为了避免多线程所带来的复杂性和降低开发难度,并且满足网页交互的及时响应,JS被设计成了单线程。虽然单线程有局限性,但是可以通过异步编程、事件循环等技术手段来实现高效的并发处理。