1.行内元素、块级元素如何转换?
答: display: inline 把某元素转换成行内元素 ==》 不独占一行,不可以设置宽高 display: inline-block 把某元素转换成行内块元素 ==》不独占一行,可以设置宽高 display: block; 把某元素转换成块元素。 ==》独占一行,并且可以设置宽高
2.页面导入样式时,使用link和@import有什么区别?
答: 区别一:link先有,后有@import(兼容性link比@import好) 区别二:加载顺序差别,浏览器先加载的标签link,后加载@import
3.img标签的title和alt有什么区别?
答: 区别一: title: 鼠标移入到图片显示的值 alt: 图片无法加载时显示的值 区别二: 在seo的层面上,蜘蛛抓取不到图片的内容,所以前端在写img标签的时候为了增加seo效果要加入alt属性来描述这张图是什么内容或关键词。
4.png、jpg、gif、webp这些图片格式解释一下,分别什么时候用?
答: png: 无损压缩,尺寸体积要比jpg/jpeg的大,适合做小图标。 jpg: 采用压缩算法,有一点失真,比png体积要小。适合做中大图片。 gif: 一般做动图。 webp:同时支持有损或者无损压缩,相同质量的图片,webp具有更小的体积,兼容性不是特别好。
5.标准盒模型和IE盒模型的区别?
答: 标准盒模型宽度和高度只包括content的。 box-sizing: content-box IE盒模型的宽度和高度包括content+padding+border box-sizing: border-box 通过css如何转换盒子模型: box-sizing: content-box; // 转为标准盒模型 box-sizing: border-box // 转为IE盒模型
6.line-height和height的区别?
答: height就是元素的高度值,是一个固定值,就是这个盒子的高度。 line-height就是每一行文字的高,如果文字换行则整个盒子高度会增大(行数 * 行高)
7.css选择符有哪些?哪些属性可以继承?
答: CSS选择符:
- 通配( * )
- id选择器( # )
- 类选择器( . )
- 标签选择器(div、p、h1...)
- 相邻选择器(+)
- 后代选择器(ul li)
- 子元素选择器( > )
- 属性选择器(a[href])
css属性哪些可以继承: 文字系列:font-size、color、line-height、text-align... 不可继承属性:border、padding、margin...
8.css优先级算法如何计算?
答:优先级比较:!import->内联样式->id选择器->类选择器->标签选择器&伪元素选择器->通配 css权重计算:
- 第一:内联样式(style) 权重值:1000
- 第二:id选择器 权重值:100
- 第三:类选择器 权重值:100
- 第四:标签选择器&伪元素选择器 权重值1
- 第五:通配选择器、>、+ 权重0
9.display有哪些值?说明他们的作用。
- none 隐藏元素
- block 把元素转换成块元素
- inline 把某元素转换成内联元素
- inline-block 把某元素转换为内联块元素
10.对BFC规范(块级格式化上下文:block formatting context)理解?
答: BFC就是页面上一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。
- 1.了解BFC:块级格式化上下文
- 2.BFC的原则: 如果一个元素具有BFC,那么内部元素再怎么弄,都不会影响到外面的元素。
- 3.如何触发BFC: float的值非none overflow的值为非visible display的值为: inline-block、block等 position: fixed、abosolute
11.清除浮动的方式有哪些?
- 1.给父元素触发BFC
- 2.创建一个盒子,添加样式clear:both
- 3.after方式 ul: after { content: ''; display: block; clear: both; }
12.position有哪些值?分别是根据什么定位?
答:
- 1.statci[默认] 没有定位,元素出现在正常流中
- 2.fixed 固定定位,根据游览器窗口定位。
- 3.relative: 相对自身定位,不脱离文档流。
- 4.absolute: 相当于第一个有relative的父元素定位,脱离文档流。
13.写一个左中右布局占满屏幕,其中左、右两块固定宽200,中间自适应宽,要求先加载中间块,请写出结构及样式。
- 双飞翼布局:
<style>
.left {
float: left;
width: 200px;
height: 200px;
background: red;
}
.right {
float: right;
width: 200px;
height: 200px;
background: aqua;
}
.middle {
width: auto;
height: 200px;
background: aliceblue;
}
</style>
<div class="left">左</div>
<div class="right">右</div>
<div class="middle">中</div>
14.opacity和rgba的区别?
答: 共同性:都可以实现透明效果
- 1.opacity 取值范围0到1之间,0表示透明,1表示不透明
- 2.rgba R表示红色,G表示绿色,B表示蓝色,A表示透明度0-1之间 区别:
- 1.opacity会继承父元素的opacity属性,而RGBA设置的元素的后代元素是不会继承不透明属性的。
15.延迟加载js的方式有哪些?
答:
- defer: 会异步下载js脚本,但是等html全部解析完成,才会执行js脚本,顺序执行js脚本。
- async: 会异步下载js脚本,下载完js脚本以后,立即执行js脚本,有多个js脚本时,不是顺次执行的(谁下载完,谁先执行)
16.介绍js微任务和宏任务?
答:
- js是单线程的语言。
- js代码执行流程:同步执行完,才开始执行事件循环的内容,进入事件循环:请求、定时器、事件等。。。
- 事件循环中包括:【微任务、宏任务】 微任务:promise.then... 宏任务: setTimeout... 流程:同步代码-》事件循环【微任务和宏任务】-》全部微任务-》宏任务。。。。
17. 继承的方式有哪些?
答:
// 1.ES6的继承
class Parent {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class Child extends Parent {
constructor() {
super();
this.name = '1111';
}
}
const child = new Child();
console.log(child.name)
// 2.原型链继承
function Parent1() {
this.name = '123';
this.age = 12;
}
function Child1() {
}
Child1.prototype = new Parent1();
const child1 = new Child1();
console.log(child1.name)
// 3.借用构造函数
function Parent2() {
this.name = 'max';
this.age = 14;
}
function Child2() {
Parent2.call(this);
}
const child2 = new Child2();
console.log(child2.name)
//4.组合是继承
function Parent3() {
this.name = 'yuyu'
this.age = 14;
}
function Child3() {
Parent3.call(this);
}
Child3.prototype = new Parent3();
const child3 = new Child3();
console.log(child3.name)
18.说一下call、apply、bind区别?
答:
共同点: 功能一致,都是改变this指针的指向
不同点:
- call、apply会立即执行,而bind返回的是一个函数,需要调用才行。 = 参数不同:apply第二个参数是数组,call和bind有多个参数需要挨个写。
19.sort背后原理是什么?
答: v8引擎sort函数只给出了两种排序,插入排序和快速排序,数量小于10的数组使用插入排序,比10大的数组使用快排。 现在用的是冒泡排序。
const arr = [
{name: 'max', age: 15},
{name: 'yuyu', age: 48},
{name: 'miaomiao', age: 2},
]
const temp = arr.sort((a, b) => {
return a.age - b.age;
})
console.log(temp)
20.localStorage和sessionStorage和cookie的区别?
答: 公共点:都是在客户端存放数据
区别:
1.数据存放的有限期
- sessionStorage: 仅在当前游览器窗口关闭之前有效。
- localStorage: 始终有效,窗口或浏览器关闭也一直保存,所以叫持久化存储。
- cookie: 只在设置的cookie过期时间之前有效,即使窗口或者浏览器关闭也有效。
2.localStorage、sessionStorage不可设置过期时间。cookie 有过期时间,可以设置过期。
3.存储大小的限制
- cookie存储量不能超过4k
- localstorage、sessionStorage不能超过5M
21. 什么是语义化标签?
答:
- 易读性和维护性更好
- seo会更好,更容易被蜘蛛爬取。
22.如何关闭IOS键盘的首字母自动大写?
答: <input type="text" autocapitalize='off'>
23.怎么让Chrome浏览器支持小于12px的文字?
答:Chrome默认字体是16px,最小支持的是12px的字体。 通过缩放的方式:
div span {
display: inline-block;
-webkit-transform:scale(1.6);
}
<div>
<span>11111</span>
</div>
22.rem 和 em的区别?
答:
- em针对于父元素的font-size
- rem针对根(html)元素的font-size
23.webkit表单输入框placeholder的颜色值?
<style>
input::-webkit-input-placeholder {
color: red;
}
</style>
<input type="" name="" placeholder="默认值">
23.介绍一下响应式(媒体查询)?
答:
- 是什么? 一个RUL可以响应多个类型客户端
- 语法结构
@media only screen and (max-width: 1000px) {
ul li: last-child {
display: none;
}
}
only: 可以排除不支持媒体查询的游览器 screen: 设备类型 max-width | max-height min-width | min-height
- 响应式图片【性能优化】
<picture>
<source srcset="1.jpg" media='(min-width:1000px)'>
<source srcset="2.jpg" media='(min-width:700px)'>
</picture>
24.什么时候采用响应式布局?
答:数据不是特别多,用户量不是特别大,纯展示类的项目适合响应式布局。 例如:公司的官网,专题页面等。特别追求性能的项目,不太适合响应式,因为如果添加了很多响应式,加载速度会变慢。
25.pc+移动端应该做什么样的布局方案?
答: 注意:访问量比较大的,类似于淘宝网,pc是一套,会加入一点响应式,移动端是另外一套,会使用自适应地布局方式。
26.var、let、const区别?
答:
- 共同点:三者都可以用来声明变量。
- 区别点:
- var是具有变量提升机制的,而let和const是不会发生变量提升的。
- var可以多次声明同一个变量,let和const不能重复声明一个变量。
- var、let声明变量的,const用来常量。var和let声明的变量可以多次赋值,而const不可以再次赋值。
- var的变量没有自身的作用域,let和const是有自身作用域的。
27.如何将多个对象合并?
答:
- 方式一:使用Object.assign(a, b)
const a = {a:1, b:4}
const b = {b:2, c:3}
let obj1 = Object.assign(a, b);
console.log(obj1)
- 方式二:使用拓展运算符
const a = {a:1, b:4}
const b = {b:2, c:3}
const c = {...a, ...b};
console.log(c)
- 方式三:
const a = {a:1, b:4}
const b = {b:2, c:3}
function extend(target, source) {
for (const key in target) {
target[key] = source[key];
}
return target;
}
console.log(extend(a, b))
28.箭头函数和普通函数有什么区别?
答:
-
this指向的问题
箭头函数中的this只在箭头函数定义时就决定的,而且不可修改(call、apply、bind)
-
箭头函数不能new(不能当作构造函数)
-
箭头函数没有prototype
-
箭头函数没有arguments
29.对象字面量创建对象new Object()或{} 与 Object.create(null) 创建对象有什么区别
考点: 对于 Object.create(null)方法的理解
参考: Object.create()方法解析,请点击
答案:
-
1、对象字面量创建对象 ,new Object() 和 { } 效果是一样
-
- 通过Object.create(null)创建的对象是非常纯净的,原型链的属性和方法都不会携带。这就非常适合数组对象开发的时候,从对象中取值,提高循环效率。
var obj = Object.create(null) ;
console.log(obj); // 打印可以发现,obj 没有__proto__ 属性
- 如果需要使用到对象的继承属性和方法,那就使用 obj = {} 或 obj = new Object() 这种方式
30.虚拟dom的优点是什么?(为什么引入虚拟dom?)
答:
- 1.可以减少DOM操作:
- a.虚拟DOM可以将多次操作合并为一次操作,比如一个页面如果有500次变化,没有虚拟DOM的就会渲染500次,而虚拟DOM只需要渲染一次,从这点上来看,页面越复杂,虚拟DOM的优势越大
- b.虚拟DOM可以借助DOM diff算法,对比新旧虚拟DOM树的差异,只将差异更新到DOM中,以最小化成本更新。
- 2.支持跨平台:虚拟DOM本质上是一个对象,可以在兼容不同平台,抹平平台之间的差异,实现支持跨平台使用。
31,什么是虚拟DOM?
答:我们打印出来虚拟DOM以后,实际上可以发现虚拟DOM是一个对象,对象内有type、props、children等属性。type是表示标签类型,props是标签内部的属性,children为节点中子节点的内容。
32.React中组件为什么要大写?
答:因为浏览器无法识别react,也不会识别jsx,甚至连ES6一些语法都无法识别,要想让浏览器识别,就需要借助babel,通过babel对jsx进行转换为对应的js对象,才能让浏览器识别,此时就会有个依据去判断是原生DOM标签还是React组件,而这个依据就是标签的首字母。如果首字母是小写,则是原生标签,反之,则是React组件。
33.虚拟DOM一定会提高性能吗?
答:不一定,虚拟DOM渲染是先将真实DOM转换为JS对象,然后通过DOM diff算法处理完毕以后,再去转换为真实DOM,也就是说他始终会创建JS对象。对于首次加载页面的情况,需要先创建所有的虚拟DOM,然后再把虚拟DOM转换为真实DOM,这样会多一层计算,不会比直接渲染真实DOM更快。
34.DOM diff算法做了哪些优化策略?
答:
- 1.虚拟DOM树节点比较时,只比较同一层的节点,不跨层进行比较。
- 2.若发现虚拟DOM树当前比较的节点类型不一样,直接判断为脏组件,从而直接删除该节点以及它的所有子节点。
- 3.同一层级的一组子节点,可以通过设置唯一的key来进行区分。
35.能使用数组的index作为虚拟DOM的节点的key吗?
答:不能,key是用于渲染对象的排序,所以必须是用唯一标记的值,假如使用数组的index的话,如果删除数组中间任意一个数,后面的数补上来,导致index发生变化,无法找到对应的虚拟DOM节点。可以使用UUID作为唯一的key。
36.将下述ES6代码转为ES5格式?
class Example {
constructor(name) {
this.name = name;
}
init() {
const fun = () => { console.log(this.name) }
fun();
}
}
const e = new Example('Hello');
e.init();
ES5:
function Example(name) {
this.name = name;
}
Example.prototype.init = function() {
var _this = this;
var fun = function() {
console.log(_this.name);
};
fun();
};
var e = new Example('Hello');
e.init();
转换后的代码使用了闭包来模拟箭头函数的行为,以确保在fun()函数中,this引用的是正确的对象实例。这是因为箭头函数在定义时捕获了外部this的上下文,而普通函数的this
37.JS 中继承实现的几种方式?
JS 的继承随着语言的发展,从最早的对象冒充到现在的圣杯模式,涌现出了很多不同的继承方式。每一种新的继承方式都是对前一种继承方式不足的一种补充。
答:
- 原型链继承
-
重点:让新实例的原型等于父类的实例。
-
特点:实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!)
-
缺点:
- 1、新实例无法向父类构造函数传参。
- 2、继承单一。
- 3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
- 借用构造函数继承
-
重点:用 call( ) 和 apply( ) 将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))
-
特点:
-
1、只继承了父类构造函数的属性,没有继承父类原型的属性。
-
2、解决了原型链继承缺点1、2、3。
-
3、可以继承多个构造函数属性(call多个)。
-
4、在子实例中可向父实例传参。
-
-
缺点:
-
1、只能继承父类构造函数的属性。
-
2、无法实现构造函数的复用。(每次用每次都要重新调用)
-
3、每个新实例都有父类构造函数的副本,臃肿。
-
- 组合模式(又被称之为伪经典模式)
- 重点:结合了两种模式的优点,传参和复用
- 特点:
- 1、可以继承父类原型上的属性,可以传参,可复用。
- 2、每个新实例引入的构造函数属性是私有的。
- 缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
- 寄生组合式继承(圣杯模式)
-
重点:修复了组合继承的问题
38.(滴滴、饿了么)写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
答:key的作用就是更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的。使用key能更快的查找到旧的结点,否则只能通过遍历去寻找。
39. ES5继承和ES6继承有什么区别?
class声明会提升,但不会初始化赋值。Foo进入暂时性死区,类似于let、const声明变量。
const bar = new Bar(); // it's ok
function Bar() {
this.bar = 42;
}
const foo = new Foo(); // ReferenceError: Foo is not defined
class Foo {
constructor() {
this.foo = 42;
}
}
class声明内部会启用严格模式。
// 引用一个未声明的变量
function Bar() {
baz = 42; // it's ok
}
const bar = new Bar();
class Foo {
constructor() {
fol = 42; // ReferenceError: fol is not defined
}
}
const foo = new Foo();
class的所有方法(包括静态方法和实例方法)都是不可枚举的。
// 引用一个未声明的变量
function Bar() {
this.bar = 42;
}
Bar.answer = function() {
return 42;
};
Bar.prototype.print = function() {
console.log(this.bar);
};
const barKeys = Object.keys(Bar); // ['answer']
const barProtoKeys = Object.keys(Bar.prototype); // ['print']
class Foo {
constructor() {
this.foo = 42;
}
static answer() {
return 42;
}
print() {
console.log(this.foo);
}
}
const fooKeys = Object.keys(Foo); // []
const fooProtoKeys = Object.keys(Foo.prototype); // []
40. React setState 笔试题,下面的代码输出什么?
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
//0 0 2 3
解析:
1、第一次和第二次都是在 react 自身生命周期内,触发时 isBatchingUpdates 为 true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,所以打印时获取的都是更新前的状态 0。
2、两次 setState 时,获取到 this.state.val 都是 0,所以执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次。设置完成后 state.val 值为 1。
3、setTimeout 中的代码,触发时 isBatchingUpdates 为 false,所以能够直接进行更新,所以连着输出 2,3。
41.Async/Await 如何通过同步的方式实现异步?
答:async awiat 是一种语法糖,基于Generator 函数和自动执行器实现。
function getData(){
return new Promise(resolve=>{
setTimeout(() => {
console.log('done');
resolve();
}, 1000);
})
}
function print(){
console.log('print');
}
//async await 函数
function downloading(){
function * loadingData(){ //Generator 函数
var x1 = yield getData();
var x2 = yield print();
return 1;
}
function start(fn){ //自动执行器实现
return new Promise((resolve,reject)=>{
var it = fn();
function run(value){
var result = it.next(value);
if(result.done){
resolve(result.value);
return;
}
Promise.resolve(result.value).then(data=>{
run(data);
})
}
run();
})
}
return start(loadingData);
}
downloading().then(v=>{console.log(v)})
42.简单讲解一下 http2 的多路复用?(http1.0、http1.1、http2.0发送http请求的区别是什么?)
答: http/1.0:如需要发送多个请求必须创建多个 TCP 连接,并且浏览器对于单域名请求有数量限制(一般6个),其连接无法被复用
http/1.1:引入流水线(Pipelining)技术,但先天 FIFO(先进先出)机制导致当前请求的执行依赖于上一个请求执行的完成,容易引起报头阻塞,并没有从根本上解决问题
http/2:重新定义底层 http 语义映射,允许同一个连接上使用请求和响应双向数据流。同一域名只需占用一个 TCP 连接,通过数据流(Stream)以帧为基本协议单位,从根本上解决了问题,避免了因频繁创建连接产生的延迟,减少了内存消耗,提升了使用性能。
43.A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态?
答:这时候对于A机器来说,如果是刚打开网页的时候,那么浏览器选项卡左上角图标这时候会一直转圈,如果在短时间内B机器重新启动服务了,A机器会连接成功,如果超过了A机器的等待时间,这条请求会挂掉
44. 介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块?
答:
-
发出
npm install命令 -
查询node_modules目录之中是否已经存在指定模块
-
若存在,不再重新安装
-
若不存在
- npm 向 registry 查询模块压缩包的网址
- 下载压缩包,存放在根目录下的
.npm目录里 - 解压压缩包到当前项目的
node_modules目录
-
45.浏览器的渲染过程是怎么样的?
浏览器渲染过程如下:
- 解析HTML,生成DOM树,解析CSS,生成CSSOM树
- 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
- Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
- Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
- Display:将像素发送给GPU,展示在页面上。
46. 介绍下观察者模式和订阅-发布模式的区别,各自适用于什么场景?
答:
观察者模式和发布订阅模式都有观察者和发布者这两个对象
观察者模式没有中介,发布者和订阅者必须知道对方的存在
发布订阅模式有中介,发布者和订阅者不需要知道对方是谁,只要通过中介进行信息的传递和过滤就可以了。
47. cookie 和 token 都存放在 header 中,为什么不会劫持 token?
答:
cookie:登陆后后端生成一个sessionid放在cookie中返回给客户端,并且服务端一直记录着这个sessionid,客户端以后每次请求都会带上这个sessionid,服务端通过这个sessionid来验证身份之类的操作。所以别人拿到了cookie拿到了sessionid后,就可以完全替代你。
token:登陆后后端不返回一个token给客户端,客户端将这个token存储起来,然后每次客户端请求都需要开发者手动将token放在header中带过去,服务端每次只需要对这个token进行验证就能使用token中的信息来进行下一步操作了。
xss:用户通过各种方式将恶意代码注入到其他用户的页面中。就可以通过脚本获取信息,发起请求之类的操作。
csrf:跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。csrf并不能够拿到用户的任何信息,它只是欺骗用户浏览器,让其以用户的名义进行操作。
csrf例子:假如一家银行用以运行转账操作的URL地址如下: www.examplebank.com/withdraw?ac…
那么,一个恶意攻击者可以在另一个网站上放置如下代码:<img src="<http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman>">
如果有账户名为Alice的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失1000资金。
上面的两种攻击方式,如果被xss攻击了,不管是token还是cookie,都能被拿到,所以对于xss攻击来说,cookie和token没有什么区别。但是对于csrf来说就有区别了。
以上面的csrf攻击为例:
- cookie:用户点击了链接,cookie未失效,导致发起请求后后端以为是用户正常操作,于是进行扣款操作。
- token:用户点击链接,由于浏览器不会自动带上token,所以即使发了请求,后端的token验证不会通过,所以不会进行扣款操作。
这是个人理解的为什么只劫持cookie不劫持token的原因。
48.Virtual DOM 真的比操作原生 DOM 快吗?谈谈你的想法。
答:虽然 Virtual DOM 可以带来一些性能上的优势,但并不意味着它一定比原生 DOM 快。由于 Virtual DOM 需要构建虚拟 DOM 树、进行差异比较和应用更新等过程,这些额外的操作也会带来一定的性能开销。而且在一些简单的场景下,直接操作原生 DOM 可能会更加高效。
49.(京东)下面代码中 a 在什么情况下会打印 1?
var a = ?;
if(a == 1 && a == 2 && a == 3){
console.log(1);
}
答:因为对象隐式转换的时候会调用对象的valueOf和toString方法,只需要改写其中一个即可。
var a = {
i: 1,
toString() {
return a.i++;
}
}
if( a == 1 && a == 2 && a == 3 ) {
console.log(1);
}
50.实现一个sleep函数?
比如 sleep(1000) 意味着等待1000毫秒,可从 Promise、Generator、Async/Await 等角度实现
const sleep = (time) => {
return new Promise((resolve, reject) => {
setTimeout(resolve, time);
});
};
sleep(1000).then(() => {
console.log('sleep end');
});