一、前言
本文以大厂面经为依据,列举js、css相关问题并附上答案,针对性强,学的有劲。
二、css
2.1盒模型
文档中的每个元素都可以被描述为矩形盒子,渲染引擎的目的就是判定其大小,属性。
盒子由四个属性组成:
margin: 外边距 border:边框 padding: 内边距 content:内容
盒模型分为两种
标准盒模型(box-sizing: content-box) width=content的width height=content的height
怪异盒模型(box-sizing: border-box) width=content的width+padding+border height=content的height+padding+border
2.2垂直居中的方法
方法一:flex .body { display: flex; justify-content: center; align-items: center }
方法二:position+transform .body { position:relative; } .inner { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); }
方法三:设置各个距离0,margin: auto position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto;
2.3三栏布局
flex、position、float都可以
2.4css权重计算方法
CSS基本选择器包含ID选择器、类选择器、标签选择器、通配符选择器(*) !important > 行内样式 > ID选择器 > 类选择器 > 标签选择器 > 通配符选择器
2.4.1扩展Vue中的scoped原理及穿透
vue style标签上有scoped属性,作用是指它css样式仅作用于当前的组件元素。避免样式污染。如果项目样式全加上了scoped相当于实现了样式模块化。
.example { color: red; }其原理主要是通过postcss转移实现
.example[data-v-5558831a] { color: red; }即postcss给一个组件的所有dom添加了一个独一无二的动态属性,然后给css选择器添加额外一个对应的属性选择器来对应dom,使得样式只作用于含有该属性的dom。
如果项目中引用了第三方组件,需要局部修改其样式,又不想去除scoped,就用到了穿透
外层 >>> 第三方组件 { 样式 }通过 >>> 可以使得在使用scoped属性的情况下,穿透scoped,修改其他组件的值。
但实际上建议的还是通过在组件最外层添加唯一的class来区分不同组件,scoped有坑,具体不列举了。
2.5 BFC
BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
解决的问题
- 父元素塌陷(子元素float:left导致塌陷,父元素设置overflow:hidden激活BFC)
- 外边距重叠(因为属于同一个BFC的两个相邻的Box会发生margin重叠,所以给他们设置成两个不同BFC即可,用个div包起来设置overflow: hidden)
- 清除浮动
以下方式都会创建BFC
- 浮动元素float不为none
- 绝对定位元素absolute、fixed
- 行内快元素
- overflow auto scroll hidden
2.6清楚浮动
方法一:违类 .clearfix:after{ content: ""; display: block; height: 0; clear:both; visibility: hidden; } 方法二:加空div .clear{ clear:both; }
2.7flex弹性盒布局
分别列举用于父容器、子容器的属性
父容器:
主轴方向flex-direction: row/row-revese/column/column-revese
换行方式flex-wrap: nowrap/wrap/wrap-revese
flex-flow 属性是 flex-direction 和 flex-wrap 属性的复合属性
主轴对齐方式justify-content: flex-start/flex-end/center/space-between/space-around
交叉轴对齐方式align-items: flex-start/flex-end/center/baseline
多行轴线对齐方式align-content: flex-start/flex-end/center/space-between/space-around
子容器:
初始比例flex-grow:默认值0,数值
缩小比例flex-shrink:默认为1,数值
基础尺寸flex-basic:默认auto,可指定大小如100px
flex属性是flex-grow、flex-shrink、flex-basic的缩写,默认0,1,auto
flex: auto = flex: 1, 1, auto
flex: none = flex: 0, 0, auto
flex取值为单值,有单位即flex-basic,无单位即flex-grow
flex取值为双值,有单位即gorw&basic,无单位即grow&shrink
2.8position
这个想必大家应该耳熟能详
absolute,常对定位,相对第一个父元素的位置进行定位
注意:是相对于 static 定位以外的第一个父元素的 padding 来定位的
relative,相对定位,相对自己正常位置进行定位
fixed,固定定位,相对于浏览器窗口进行定位
sticky,粘性定位,relative和fixed的结合,需要设定阈值如top,在跨越阈值前是relative,跨越后为fixed
static,默认值,没有定位,正常展示在流中。
inherit,规定应该继承父元素的position属性。
2.9如何实现一个自适应正方形
移动端
方法一:利用css3的vw单位
.square { width: 10vw; height: 10vw; background: red; }
方法二:利用margin或者padding的百分比计算
.square { width: 10%; padding-bottom: 10%; height: 0; // 防止内容撑开多余的高度 background: red; }
方法三:利用rem(比较麻烦)
@function rem(value) { para: 100; @return para + rem; }
.square { width: rem(10); height: rem(10); background: red; }
rem单位的像素值是相对于根元素font-size的,根元素font-size根据视口宽度动态设置
设计稿元素尺寸/设计稿宽度=rem*HTML元素的font-size/视口宽度=rem/(视口宽度/HTML元素的font-size)
如果:
视口宽度 / HTML 元素的 font-size = 定值 N
就可以用同一份css代码实现在任何设备中自适应
rem 值 = N * (视觉稿元素尺寸 / 视觉稿画布宽度 )
所以只要确定一个N值,再完成两步,即可实现自适应:
1.根元素font-size根据视口宽度动态设置
var docEl = document.documentElement
docEl.style.fontSize = (docEl.clientWidth / 7.5) + 'px';
2.将视觉稿中元素的像素值转为rem单位
假如您的视觉稿画布宽度是 750,为了便于 rem 值的计算, 您可以选择设置 N = 7.5, 这样只需要将视觉稿中的尺寸值除以 100 就可以得到 rem 单位的 CSS 像素值
2.10css实现三角形
方法一:利用border属性
利用盒模型border属性边框交界处会呈现的平滑的斜线这个特点,通过设置不同颜色实现。(把其余设置透明即可)
.triangle { height:0; width:0; border-color:red transparent transparent transparent; border-style:solid; border-width:30px; }
方法二:利用伪类
和border原理一致
div:before{ content:""; border:10px solid transparent; border-bottom-color:black; position:absolute; left:20px; top:0; margin-top:-20px; }
用另一个背景色三角盖住,就完成了透明三角,效果如图
div:after{ content:""; border:10px solid transparent; border-bottom-color:white; position: absolute; top:0; left:20px; margin-top:-19px; }
梯形 { height: 0; width: 100px; border-top: 100px solid red; border-right: 37px solid transparent; }
2.11sass/scss&less
Less是一门css预处理语言,扩展了css语言,增加了变量、嵌套、mixin、函数、导入等功能,使得css更易维护和扩展,可以运行在node和浏览器端
sass是一款强化css的辅助工具,增加了变量、嵌套、mixin、函数、导入等高级功能,使得css更加强大与优雅,
Sass的缩排语法,对于习惯css前端来说不直观,因此sass进行了改良,sass3就变成了scss
区别:
1.编译环境不一样
less是基于js,是在客户端处理的;sass是基于ruby的,是在服务器端处理
2.变量符号不一样,less是@,sass是$
3.less没有输出设置,sass有四种nested, compact, compressed 和 expanded
4.sass支持条件语句if()else{},for{}循环等
@for $I from 1 through 5 {}
2.12css-loader&style-loader
Webpack默认只能解析js代码,要想解析其他类型的文件需要用到loader
Css-loader
css-loader只会把css模块加载到JS代码中,并不会使用这个模块
style-loader
Webpack中多个loader是从后往前的调用顺序,css-loader是生成一个包含css代码的数组,style-loader的作用是使用这个数组
具体是将css字符串还原成css dom,然后挂载到html页面中(放入styly标签中)
2.13css3
2.13.1动画
实现动画的方式有如下几种
- transition实现渐变动画
- transform转变动画
- animation实现自定义动画
1.transition 过渡transition是一个复合属性,包括
transition-property: 过渡属性(默认值为all)
transition-duration: 过渡持续时间(默认值为0s)
transiton-timing-function: 过渡函数(默认值为ease函数)
transition-delay: 过渡延迟时间(默认值为0s)
#test1{ transition-property: width,background; transition-delay: 200ms; transition-timing-function: linear; transition-duration: 2s; } 类似于 #test2{ transition: width 2s linear 200ms,background 2s linear 200ms; }
.test:hover{ width: 500px; background:pink; }
2.transform
transform 属性向元素应用 2D 或 3D 转换,该元素允许我们对元素进行旋转、缩放、移动、倾斜
transform: translate(x,y)平移
transform: scale(x,y)缩放
transform: rotateY(angle)旋转
transform: skew(5deg)倾斜
一般配合transition过度使用!
3.animation
动画只需要定义一些关键的帧,而其余的帧,浏览器会根据计时函数插值计算出来
@keyframes rotate{ from{ transform: rotate(0deg); } to{ transform: rotate(360deg); } }
@keyframes rotate{ 0%{ transform: rotate(0deg); } 50%{ transform: rotate(180deg); } 100%{ transform: rotate(360deg); } }
定义好关键帧,直接使用
animation: rotate 2s;
2.13.2渐变
可以让你在两个或多个指定的颜色之间显示平稳的过渡,css3定义两种类型的渐变
线性渐变:向下/向上/向左/向右/对角方向
background: linear-gradient(red, blue);
径向渐变:由中心定义
background-image: radial-gradient(red, yellow, green);
2.13.3透明度
opacity
2.14z-index
Z-index属性设置元素的堆叠顺序,仅能在定位元素(position)上奏效,更高堆叠顺序的元素会处于较低的元素前面,也就是会覆盖。
Css3硬件加速又叫做GPU加速,由于 GPU 中的 transform 等 CSS 属性不会触发 repaint,所以能大大提高网页的性能。
GPU扩展:
动画与帧
动画是由一帧帧的的图片组成的,大多数设备的刷新频率是60次/秒(16.6ms),也就是说浏览器对每一帧图片的渲染工作要在16ms内完成,超出这个时间,页面渲染就会卡顿,影响用户体验。我们看一下每一帧浏览器都做了什么。
1.js实现动画效果,DOM元素操作生成DOM Tree等
2.style(计算样式)确定每个DOM元素应该用什么css规则生成CSSOM Tree
DOM Tree和CSSOM Tree合并生成Render Tree再进行3、4过程
3.Layout(布局)计算每个DOM元素在最终屏幕显示的大小和位置。由于web页面的元素布局是相对的,所以任意一个元素的位置发生变动,都会联动的引起其他元素发生变化,这个过程叫做reflow.
4.Paint(绘制)在多个层上绘制DOM元素的文字、颜色、图像、边框和阴影等
5.Composite(渲染层合并)浏览器主进程将默认的图层和复合图层交给 GPU 进程,GPU 进程再将各个图层合成(composite),最后显示出页面
动画与图层
浏览器在获取render tree后,渲染书中包含大量元素,每一个渲染元素都会被分到一个图层里,具有动画的元素会做为复合层渲染,独立于主文档,好处是进行GPU加速提升渲染性能,但有个坑就是说,他会把z-index比自己大的兄弟元素也纳入到复合层来,导致动画渲染时,整个都进行layout、paint过程而卡顿。
使用3D硬件加速提升动画性能时,最好给元素增加一个z-index属性,人为干扰复合层的排序,可以有效减少chrome创建不必要的复合层,提升渲染性能,移动端优化效果尤为明显
哪些规则可以让浏览器为我们创建独立的图层使用GPU加速呢
只举例了常见的
1.3D或者透视变换(perspective,transform)css属性
2.元素A有一个z-index比自己小的元素B,且元素B是一个合成层(换句话说就是该元素在复合层上面渲染),则元素A会提升为合成层
2.15各种布局方式
流式布局:元素宽度用%,配合min-width、max-width保证元素不失真,定高。用于早期历史,应对不同尺寸的PC屏幕(那时屏幕差距不会太大);缺点明显,屏幕尺寸差异太大时,宽度拉大,但是高度和文字大小还是和原来一样。
自适应布局:利用@media媒体查询,为不同屏幕分辨率定义布局,即创建多个静态布局。但在每个静态布局中,页面元素不随窗口大小的调整发生变化。
响应式布局:通常是糅合了流式布局+弹性布局,再搭配媒体查询技术使用——分别为不同的屏幕分辨率定义布局,同时,在每个布局中,应用流式布局的理念,即页面元素宽度随着窗口调整而自动适配。
2.16移动端分辨率和像素知识
移动设备和PC设备最大的差异在于屏幕,这主要体现在屏幕尺寸和屏幕分辨率两个方面。
2.16.1屏幕
通常我们所指的屏幕,实际指的是屏幕对角线的长度(一般英寸来衡量)
屏幕分辨率是指纵横向上的像素点数一般用像素来度量px,表示屏幕水平和垂直方向的像素数,例如1920*1080指的是屏幕垂直和水平方向分别有1920和1080个像素点构成
分辨率分为两种
物理分辨率(设备像素):单纯指屏幕纵横向上的像素点数
逻辑分辨率(设备独立像素):随着设备的更新,1080P->2K屏幕,相同屏幕大小下一像素的物理大小在变小,从而引出问题,手机分辨率翻倍,我们的图像不就要被缩小一倍了吗。后来乔布斯提出了Retina Display(视网膜屏幕)概念,在iphone4使用的视网膜屏幕中,把2*2个像素当1个像素使用,这样让屏幕看起来更精致,元素的大小也不会改变。有了逻辑分辨率,不同机型的逻辑像素就不会差别太大了,但是依然会有适配问题(后面会说到)。
2.16.2设备像素比
设备像素比device pixel ratio简称dpr,即物理像素和逻辑像素(设备独立像素)的比值,可以通过dpr解决1像素适配问题。web中,浏览器提供了window.devicePixelRatio来获取dpr,css中,可使用媒体查询min-device-pixel-ratio
解决一像素问题
谷歌会把0.5px四舍五入成1px,firefox、safari能正常画出半个像素的边
/* 2倍屏 */ @media only screen and (-webkit-min-device-pixel-ratio: 2.0) { .border-bottom::after { -webkit-transform: scaleY(0.5); transform: scaleY(0.5); } }
2.16.3适配
首选设置meta
方案一rem
回顾上文rem部分
方案二vw、vh
有postcss-loader自动px转vw
三、js
3.1基础js-api
3.1.1判断数字是不是整数
Number.isInteger(3) // true
Math.round(number) === number // 四舍五入
Math.ceil(number) === number // 向上取整(大于等于),然后与自己做比较
Math.floor() // 向下取整(小于等于)
3.1.2数组增删改查去重等
增
arr.push() // 从尾部加入元素,返回数组长度
arr.unshift() // 从头部加入元素,返回数组长度
arr.concat() // 从尾部加入数组,返回新数组
arr.splice(1, 2, 5) // 起始位置、删除元素个数、添加的元素
splice、push、unshift都是在原数组上修改
删
arr.pop() // 从数组末尾删除一项并返回
arr.shift() // 从数组头部删除一项并返回
arr.slice(2,5) // 裁剪返回起始位置-结束位置之间的项目,不影响原数组
arr.splice(1, 2, 5)
改
arr.splice(1, 2, 5)
arr.map(item => item === 3)
查
indexOf() // 从开头向后找,找到返回下标
lastIndexOf() // 从后往前找,找不到返回-1
includes() // 返回bool
Math.max(1, 2, 3) // 查找最大值
去重
Array.from(new Set(arr))简化为[…new Set(arr)]
两个for嵌套遍历查重splice删除重复项
一次for循环,indexOf()/includes()查重,删除,或者push新数组
排序
按大小顺序排序、查最高最低值、随机排序 arr.sort((a, b) => { return a-b })可以入一个比值函数
sort(() => {return 0.5 - Math.random()}) // 随机排序
打乱
(arr) => { const stack = [] while(arr.length) { const index = parseInt(Math.random() * arr.length) // 利用数组长度*[0, 1),再parseInt取整,生成随机索引值 stack.push(arr[index]) arr.splice(index, i) // 删除原数组中随机生成的元素 } return stack }
sort(() => {return 0.5 - Math.random()})
2.1.3时间相关
时间戳转date New Date(时间戳)
date转时间戳 方法一:Math.round(date.getTime());
方法二:date.valueOf()
方法三:timestamp = date.parse("2015-08-09 08:01:36: 789")
date取年月日
date.getFullYear()
date.getMonth() + 1
date.getDate()
时间转时间戳
moment(params.beginTime).valueOf()
时间戳转时间字符串
moment(text).format('YYYY-MM-DD HH:mm:ss')
3.2原型原型链
js中原型、原型链的概念,是帮助我们更深入学习js的必要一步。js开发者想理解js继承,new关键字原理,甚至封装组件、优化代码,弄明白js中原型、原型链更是前提条件。
四个概念
1.js分为函数对象和普通对象,每个对象都有_proto_属性,但是只有函数对象才有prototype属性
2.Object、Function都是js内置的函数, 类似的还有我们常用到的Array、RegExp、Date、Boolean、Number、String
3.属性_proto_是一个对象,有两个属性,constructor和_proto_
4.原型对象prototype有一个默认的constructor属性,用于记录实例是由哪个构造函数创建
两个准则
1.Person.prototype.constructor=Person 原型对象(即Person.prototype)的constructor指向构造函数本身
2.person.proto=Person.prototype 实例(即person)的_proto_和对象原型指向同一个地方
意义:动态获取,减少内存
原型对象的作用,是用来存放实例中共有的那部分属性、方法,实例们动态或得构造函数之后添加的属性、方法,可以大大减少内存消耗。
3.3js创建对象的方法
方法一:字面量
const student = { age: 12, sex: ‘boy’, }
方法二:Object()
const stu = new Object() stu.age = 12
方法三:工厂模式
在函数内创建一个对象,赋予属性及方法后再将对象返回
function createObject(age, sex) { return { age, sex, } }
cosnt person = createObject(12, ‘boy’)
方法四:构造函数
没有显示地创建对象 直接将属性和方法赋值给了this对象 没有return语句 函数首字母大写(为了区别普通函数) 调用函数时用到new操作符
function Student(age, sex) { this.age = age this.sex = sex this.sayHi = function(){ console.log(hi) } }
const person1 = new Student(12, ‘boy’)
console.log(person1 instanceof Object); //true console.log(person1 instanceof Student); //true
构造函数胜于工厂模式的地方是构造函数可以将它的实例标示为一种特定的类型。
3.3new运算符
new运算符创建一个用户定义的对象类型的实例
new调用时会经历以下步骤
1.创建一个新对象({})
2.将构造函数的作用域赋值给了新对象(因此this就指向了新对象)
3.执行函数中的代码(为这个新对象添加属性)
4.返回新对象
手写new
function _new(obj, ...rest){ // 基于obj的原型创建一个新的对象 const newObj = Object.create(obj.prototype);
// 添加属性到新创建的newObj上, 并获取obj函数执行的结果. const result = obj.apply(newObj, rest);
// 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象 return typeof result === 'object' ? result : newObj; }
3.4事件流(冒泡捕获)
描述页面中接收事件的顺序,分为三个阶段
事件捕获阶段:该阶段是浏览器在寻找被触发事件的元素对象,这个阶段不触发事件函数
处于目标阶段:该阶段是浏览器找到了被触发事件的元素对象,这个阶段会触发事件函数
事件冒泡阶段:该阶段是浏览器从被触发事件的元素对象向上冒泡,直至到达document对象,这个阶段会触发所经过元素的相同类型事件函数
事件委托:不直接在dom上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发。如ul li在ul上添加监听。
3.5作用域、闭包
3.5.1作用域
var b = 'programmer'; function foo() { console.log(b); // 输出"programmer" } foo();
在使用变量时,先从函数作用域找,再到全局作用域找,顺着链条从下往上找,这条链条,就叫做作用域链
console.log(name); // 输出undefined var name = 'iceman';
在上一行输出name没有报错,输出undefined说明输出的时候变量已经存在了,只是没有赋值
js是有编译过程的,上面代码两步动作,1.编译器在当前作用域中声明一个变量name。2.运行时引擎在作用域中查找该变量,找到变量为其赋值。
作用域是由代码写在哪里决定的。
3.5.2闭包
定义:闭包是指有权访问另一个函数作用域中的变量的函数
定义:从技术角度讲,所有函数都是闭包:他们都是对象,他们都关联到作用域链
定义:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行
function fn1() { var name = 'iceman'; function fn2() { console.log(name); } return fn2; } var fn3 = fn1(); fn3();
总结:某个函数在定义时的词法作用域之外的地方被调用,闭包可以使该函数极限访问定义时的词法作用域。
注意点:
闭包的每次调用都会返回一个新的函数,即使传入相同的参数
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量
闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来(私有变量)
用途:
读取函数内部的变量,这些变量始终在内存中(因为引用关系一直存在)。-----所以在不用变量之后需要手动释放变量。 能够封装对象的私有属性和方法
3.6类与继承
开发游戏时需要重复调用函数来完成创建角色,移动角色。会出现很多的重复代码,因此需要一个类来统一这些代码。
构造函数法
function Student(age, sex) { var age = age this.sex = sex this.sayAge = function(){ // 用闭包实现私有属性 console.log(age) } }
const person1 = new Student(12, ‘boy’)
缺点:每次实例化对象,就会执行构造器的代码,这些属性和方法被拷贝到实例化出来的对象中。吃内存
原型链继承法
Student.prototype.saySex = function(){ // 用闭包实现私有属性 return this.sex }
在原型对象中定义共有属性方法,降低吃内存现象 场景较多不做列举了,感兴趣可自行搜索
3.7同步异步
js是和浏览器交互,你不能一边添加元素,一边删除元素,所以是单线程的。
js万一一行解析很久,那么下面代码会被阻塞,对用户就是很卡。所以需要异步。
js是单线程的,但是浏览器的内核多线程的,其中就是监听异步事件的线程,
异步执行机制:
首先判断js代码是同步还是异步,同步就进入主进程,异步就进入event table
异步任务在event table中注册函数,当满足触发条件后,被推入event queue
同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主进程 中
以上三步循环执行,这就是event loop
3.8 ES各版本代表特性
3.8.1 ES6
class类(可继承extends)
import/export(模块化)
块级作用域(let/const)
箭头函数(this指向定义时的外部)
函数参数支持默认值设置
模版字符串
解构赋值
延展操作符…
对象属性简写(不能指定属性名)
promise(链式then)
没有依赖关系,并发执行
await Promise.all([charCount(data1),charCount(data2)]);
set集合map字典
set可以用来数组去重,求并集、交集
map类似对象,不过key可以不为字符串,可以是对象、数字等
hasOwnProperty判断对象中是否有这个属性
3.8.2 ES7
includes()
指数运算符**
console.log(2**10);// 输出1024
3.8.3 ES8
async/await实现原理是在Generator基础上递归自执行
Object.values() 返回object自身属性的所有值
Object.entries() 返回给定对象自身可枚举属性的键值对的数组
for(let [key,value] of Object.entries(obj1)){
console.log(key: ${key} value:${value})
}
3.8.4 ES9
异步迭代
async function process(array) { for await (let i of array) { doSomething(i); } }
Promise.finally
另外还有正则的几个属性,不做列举了
3.8.5 ES10
flat(n)将多维数组扁平化,接受一个深度的参数,
flatMap(callback)好比是map + flat
Object.fromEntries()和Object.entries相反
trimStart()删除字符串开头的空格
trimEnd()删除字符串结尾的空格
3.8.6 ES11
可选链操作符(如果是null/undefined直接返回undefined)
let nestedProp = obj?.first?.second;
空位合并操作符??
const x = null; const y = x ?? 500; console.log(y); // 500 const n = 0 const m = n ?? 9000; console.log(m) // 0
Promise.allSettled(优化promise.all任一reject,调用中断的问题)
Promise.allSettled([ Promise.reject({ code: 500, msg: '服务异常' }), Promise.resolve({ code: 200, list: [] }), Promise.resolve({ code: 200, list: [] }) ]).then(res => { console.log(res) /* 0: {status: "rejected", reason: {…}} 1: {status: "fulfilled", value: {…}} 2: {status: "fulfilled", value: {…}} */ // 过滤掉 rejected 状态,尽可能多的保证页面区域数据渲染 RenderContent( res.filter(el => { return el.status !== 'rejected' }) ) })
Dynamic import支持import动态导入模块
onclick事件时才import('/modules/my-module.js’),从而达到首屏优化
BigInt
globalThis
3.8.7 ES12
逻辑赋值运算符
a ||= b如果a为真,则返回a,如果为假返回b
数字分隔符100_000_000允许用_分隔数字