什么状况会阻塞渲染?
首先渲染的前提是生成渲染树,所以 HTML 和 CSS 必定会阻塞渲染。如果你想渲染的越快,你越应该升高一开始须要渲染的文件大小,并且扁平层级,优化选择器。而后当浏览器在解析到 script 标签时,会暂停构建 DOM,实现后才会从暂停的中央从新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都倡议将 script 标签放在 body 标签底部的起因。
当然在当下,并不是说 script 标签必须放在底部,因为你能够给 script 标签增加 defer 或者 async 属性。当 script 标签加上 defer 属性当前,示意该 JS 文件会并行下载,然而会放到 HTML 解析实现后程序执行,所以对于这种状况你能够把 script 标签放在任意地位。对于没有任何依赖的 JS 文件能够加上 async 属性,示意 JS 文件下载和解析不会阻塞渲染。
对浏览器的缓存机制的了解
浏览器缓存的全过程:
- 浏览器第一次加载资源,服务器返回 200,浏览器从服务器下载资源文件,并缓存资源文件与 response header,以供下次加载时比照应用;
- 下一次加载资源时,因为强制缓存优先级较高,先比拟以后工夫与上一次返回 200 时的时间差,如果没有超过 cache-control 设置的 max-age,则没有过期,并命中强缓存,间接从本地读取资源。如果浏览器不反对 HTTP1.1,则应用 expires 头判断是否过期;
- 如果资源已过期,则表明强制缓存没有被命中,则开始协商缓存,向服务器发送带有 If-None-Match 和 If-Modified-Since 的申请;
- 服务器收到申请后,优先依据 Etag 的值判断被申请的文件有没有做批改,Etag 值统一则没有批改,命中协商缓存,返回 304;如果不统一则有改变,间接返回新的资源文件带上新的 Etag 值并返回 200;
-
如果服务器收到的申请没有 Etag 值,则将 If-Modified-Since 和被申请文件的最初批改工夫做比对,统一则命中协商缓存,返回 304;不统一则返回新的 last-modified 和文件并返回 200;
很多网站的资源前面都加了版本号,这样做的目标是:每次降级了 JS 或 CSS 文件后,为了避免浏览器进行缓存,强制扭转版本号,客户端浏览器就会从新下载新的 JS 或 CSS 文件,以保障用户可能及时取得网站的最新更新。
HTTP 状态码
- 1xx 信息性状态码 websocket upgrade
-
2xx 胜利状态码
- 200 服务器已胜利解决了申请
- 204(没有响应体)
- 206(范畴申请 暂停持续下载)
-
3xx 重定向状态码
- 301(永恒):申请的页面已永恒跳转到新的 url
- 302(长期):容许各种各样的重定向,个别状况下都会实现为到
GET
的重定向,然而不能确保POST
会重定向为POST
- 303 只容许任意申请到
GET
的重定向 - 304 未修改:自从上次申请后,申请的网页未修改过
- 307:
307
和302
一样,除了不容许POST
到GET
的重定向
-
4xx 客户端谬误状态码
- 400 客户端参数谬误
- 401 没有登录
- 403 登录了没权限 比方管理系统
- 404 页面不存在
- 405 禁用申请中指定的办法
-
5xx 服务端谬误状态码
- 500 服务器谬误:服务器外部谬误,无奈实现申请
- 502 谬误网关:服务器作为网关或代理呈现谬误
- 503 服务不可用:服务器目前无奈应用
- 504 网关超时:网关或代理服务器,未及时获取申请
前端贮存的⽅式有哪些?
- cookies:在 HTML5 规范前本地贮存的次要⽅式,长处是兼容性好,申请头⾃带 cookie ⽅便,毛病是⼤⼩只有 4k,⾃动申请头加⼊ cookie 节约流量,每个 domain 限度 20 个 cookie,使⽤起来麻烦,须要⾃⾏封装;
- localStorage:HTML5 加⼊的以键值对 (Key-Value) 为规范的⽅式,长处是操作⽅便,永久性贮存(除⾮⼿动删除),⼤⼩为 5M,兼容 IE8+;
- sessionStorage:与 localStorage 根本相似,区别是 sessionStorage 当⻚⾯敞开后会被清理,⽽且与 cookie、localStorage 不同,他不能在所有同源窗⼝中共享,是会话级别的贮存⽅式;
- Web SQL:2010 年被 W3C 废除的本地数据库数据存储⽅案,然而支流浏览器(⽕狐除外)都曾经有了相干的实现,web sql 相似于 SQLite,是真正意义上的关系型数据库,⽤ sql 进⾏操作,当咱们⽤ JavaScript 时要进⾏转换,较为繁琐;
- IndexedDB:是被正式纳⼊ HTML5 规范的数据库贮存⽅案,它是 NoSQL 数据库,⽤键值对进⾏贮存,能够进⾏疾速读取操作,⾮常适宜 web 场景,同时⽤ JavaScript 进⾏操作会⾮常便。
网络劫持有哪几种,如何防备?
⽹络劫持分为两种:
(1)DNS 劫持: (输⼊京东被强制跳转到淘宝这就属于 dns 劫持)
- DNS 强制解析: 通过批改运营商的本地 DNS 记录,来疏导⽤户流量到缓存服务器
- 302 跳转的⽅式: 通过监控⽹络出⼝的流量,分析判断哪些内容是能够进⾏劫持解决的, 再对劫持的内存发动 302 跳转的回复,疏导⽤户获取内容
(2)HTTP 劫持: (拜访⾕歌然而⼀直有贪玩蓝⽉的⼴告), 因为 http 明⽂传输, 运营商会批改你的 http 响应内容(即加⼴告)
DNS 劫持因为涉嫌守法,曾经被监管起来,当初很少会有 DNS 劫持,⽽ http 劫持仍然⾮常盛⾏,最无效的方法就是全站 HTTPS,将 HTTP 加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。
快排 – 工夫复杂度 nlogn~ n^2 之间
题目形容: 实现一个快排
实现代码如下:
function quickSort(arr) {if (arr.length < 2) {return arr;}
const cur = arr[arr.length - 1];
const left = arr.filter((v, i) => v <= cur && i !== arr.length - 1);
const right = arr.filter((v) => v > cur);
return [...quickSort(left), cur, ...quickSort(right)];
}
// console.log(quickSort([3, 6, 2, 4, 1]));
参考 前端进阶面试题具体解答
同步和异步的区别
- 同步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,那么这个过程会始终期待上来,直到音讯返回为止再持续向下执行。
- 异步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,这个时候过程会持续往下执行,不会阻塞期待音讯的返回,当音讯返回时零碎再告诉过程进行解决。
如何进攻 XSS 攻打?
能够看到 XSS 危害如此之大,那么在开发网站时就要做好进攻措施,具体措施如下:
- 能够从浏览器的执行来进行预防,一种是应用纯前端的形式,不必服务器端拼接后返回(不应用服务端渲染)。另一种是对须要插入到 HTML 中的代码做好充沛的本义。对于 DOM 型的攻打,次要是前端脚本的不牢靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能呈现的恶意代码状况进行判断。
- 应用 CSP,CSP 的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行,从而避免恶意代码的注入攻打。
- CSP 指的是内容安全策略,它的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行。咱们只须要配置规定,如何拦挡由浏览器本人来实现。
- 通常有两种形式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的形式
- 对一些敏感信息进行爱护,比方 cookie 应用 http-only,使得脚本无奈获取。也能够应用验证码,防止脚本伪装成用户执行一些操作。
数组的遍历办法有哪些
办法 | 是否扭转原数组 | 特点 |
---|---|---|
forEach() | 否 | 数组办法,不扭转原数组,没有返回值 |
map() | 否 | 数组办法,不扭转原数组,有返回值,可链式调用 |
filter() | 否 | 数组办法,过滤数组,返回蕴含符合条件的元素的数组,可链式调用 |
for…of | 否 | for…of 遍历具备 Iterator 迭代器的对象的属性,返回的是数组的元素、对象的属性值,不能遍历一般的 obj 对象,将异步循环变成同步循环 |
every() 和 some() | 否 | 数组办法,some()只有有一个是 true,便返回 true;而 every()只有有一个是 false,便返回 false. |
find() 和 findIndex() | 否 | 数组办法,find()返回的是第一个符合条件的值;findIndex()返回的是第一个返回条件的值的索引值 |
reduce() 和 reduceRight() | 否 | 数组办法,reduce()对数组正序操作;reduceRight()对数组逆序操作 |
实现一个宽高自适应的正方形
- 利用 vw 来实现:
.square {
width: 10%;
height: 10vw;
background: tomato;
}
- 利用元素的 margin/padding 百分比是绝对父元素 width 的性质来实现:
.square {
width: 20%;
height: 0;
padding-top: 20%;
background: orange;
}
- 利用子元素的 margin-top 的值来实现:
.square {
width: 30%;
overflow: hidden;
background: yellow;
}
.square::after {
content: '';
display: block;
margin-top: 100%;
}
事件是什么?事件模型?
事件是用户操作网页时产生的交互动作,比方 click/move,事件除了用户触发的动作外,还能够是文档加载,窗口滚动和大小调整。事件被封装成一个 event 对象,蕴含了该事件产生时的所有相干信息(event 的属性)以及能够对事件进行的操作(event 的办法)。
事件是用户操作网页时产生的交互动作或者网页自身的一些操作,古代浏览器一共有三种事件模型:
- DOM0 级事件模型,这种模型不会流传,所以没有事件流的概念,然而当初有的浏览器反对以冒泡的形式实现,它能够在网页中间接定义监听函数,也能够通过 js 属性来指定监听函数。所有浏览器都兼容这种形式。间接在 dom 对象上注册事件名称,就是 DOM0 写法。
- IE 事件模型,在该事件模型中,一次事件共有两个过程,事件处理阶段和事件冒泡阶段。事件处理阶段会首先执行指标元素绑定的监听事件。而后是事件冒泡阶段,冒泡指的是事件从指标元素冒泡到 document,顺次查看通过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过 attachEvent 来增加监听函数,能够增加多个监听函数,会按程序顺次执行。
- DOM2 级事件模型,在该事件模型中,一次事件共有三个过程,第一个过程是事件捕捉阶段。捕捉指的是事件从 document 始终向下流传到指标元素,顺次查看通过的节点是否绑定了事件监听函数,如果有则执行。前面两个阶段和 IE 事件模型的两个阶段雷同。这种事件模型,事件绑定的函数是 addEventListener,其中第三个参数能够指定事件是否在捕捉阶段执行。
如何判断一个对象是否属于某个类?
- 第一种形式,应用 instanceof 运算符来判断构造函数的 prototype 属性是否呈现在对象的原型链中的任何地位。
- 第二种形式,通过对象的 constructor 属性来判断,对象的 constructor 属性指向该对象的构造函数,然而这种形式不是很平安,因为 constructor 属性能够被改写。
- 第三种形式,如果须要判断的是某个内置的援用类型的话,能够应用 Object.prototype.toString() 办法来打印对象的[[Class]] 属性来进行判断。
什么是 margin 重叠问题?如何解决?
问题形容: 两个块级元素的上外边距和下外边距可能会合并(折叠)为一个外边距,其大小会取其中外边距值大的那个,这种行为就是外边距折叠。须要留神的是,浮动的元素和相对定位 这种脱离文档流的元素的外边距不会折叠。重叠只会呈现在 垂直方向。
计算准则: 折叠合并后外边距的计算准则如下:
- 如果两者都是负数,那么就去最大者
- 如果是一正一负,就会正值减去负值的绝对值
- 两个都是负值时,用 0 减去两个中绝对值大的那个
解决办法: 对于折叠的状况,次要有两种:兄弟之间重叠 和父子之间重叠(1)兄弟之间重叠
- 底部元素变为行内盒子:
display: inline-block
- 底部元素设置浮动:
float
- 底部元素的 position 的值为
absolute/fixed
(2)父子之间重叠
- 父元素退出:
overflow: hidden
- 父元素增加通明边框:
border:1px solid transparent
- 子元素变为行内盒子:
display: inline-block
- 子元素退出浮动属性或定位
代码输入后果
const promise = new Promise((resolve, reject) => {resolve('success1');
reject('error');
resolve('success2');
});
promise.then((res) => {console.log('then:', res);
}).catch((err) => {console.log('catch:', err);
})
输入后果如下:
then:success1
这个题目考查的就是 Promise 的状态在发生变化之后,就不会再发生变化。开始状态由pending
变为resolve
,阐明曾经变为已实现状态,上面的两个状态的就不会再执行,同时上面的 catch 也不会捕捉到谬误。
僵尸过程和孤儿过程是什么?
- 孤儿过程 :父过程退出了,而它的一个或多个过程还在运行,那这些子过程都会成为孤儿过程。孤儿过程将被 init 过程(过程号为 1) 所收养,并由 init 过程对它们实现状态收集工作。
- 僵尸过程:子过程比父过程先完结,而父过程又没有开释子过程占用的资源,那么子过程的过程描述符依然保留在零碎中,这种过程称之为僵死过程。
如何实现浏览器内多个标签页之间的通信?
实现多个标签页之间的通信,实质上都是通过中介者模式来实现的。因为标签页之间没有方法间接通信,因而咱们能够找一个中介者,让标签页和中介者进行通信,而后让这个中介者来进行音讯的转发。通信办法如下:
- 应用 websocket 协定,因为 websocket 协定能够实现服务器推送,所以服务器就能够用来当做这个中介者。标签页通过向服务器发送数据,而后由服务器向其余标签页推送转发。
- 应用 ShareWorker 的形式,shareWorker 会在页面存在的生命周期内创立一个惟一的线程,并且开启多个页面也只会应用同一个线程。这个时候共享线程就能够充当中介者的角色。标签页间通过共享一个线程,而后通过这个共享的线程来实现数据的替换。
- 应用 localStorage 的形式,咱们能够在一个标签页对 localStorage 的变动事件进行监听,而后当另一个标签页批改数据的时候,咱们就能够通过这个监听事件来获取到数据。这个时候 localStorage 对象就是充当的中介者的角色。
- 应用 postMessage 办法,如果咱们可能取得对应标签页的援用,就能够应用 postMessage 办法,进行通信。
Node 中的 Event Loop 和浏览器中的有什么区别?process.nextTick 执行程序?
Node 中的 Event Loop 和浏览器中的是齐全不雷同的货色。
Node 的 Event Loop 分为 6 个阶段,它们会依照 程序 重复运行。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量达到零碎设定的阈值,就会进入下一阶段。
(1)Timers(计时器阶段):首次进入事件循环,会从计时器阶段开始。此阶段会判断是否存在过期的计时器回调(蕴含 setTimeout 和 setInterval),如果存在则会执行所有过期的计时器回调,执行结束后,如果回调中触发了相应的微工作,会接着执行所有微工作,执行完微工作后再进入 Pending callbacks 阶段。
(2)Pending callbacks:执行推延到下一个循环迭代的 I / O 回调(零碎调用相干的回调)。
(3)Idle/Prepare:仅供外部应用。
(4)Poll(轮询阶段):
- 当回调队列不为空时:会执行回调,若回调中触发了相应的微工作,这里的微工作执行机会和其余中央有所不同,不会等到所有回调执行结束后才执行,而是针对每一个回调执行结束后,就执行相应微工作。执行完所有的回调后,变为上面的状况。
- 当回调队列为空时(没有回调或所有回调执行结束):但如果存在有计时器(setTimeout、setInterval 和 setImmediate)没有执行,会完结轮询阶段,进入 Check 阶段。否则会阻塞并期待任何正在执行的 I / O 操作实现,并马上执行相应的回调,直到所有回调执行结束。
(5)Check(查问阶段):会查看是否存在 setImmediate 相干的回调,如果存在则执行所有回调,执行结束后,如果回调中触发了相应的微工作,会接着执行所有微工作,执行完微工作后再进入 Close callbacks 阶段。
(6)Close callbacks:执行一些敞开回调,比方 socket.on(‘close’, …)等。
上面来看一个例子,首先在有些状况下,定时器的执行程序其实是 随机 的
setTimeout(() => { console.log('setTimeout')}, 0)setImmediate(() => { console.log('setImmediate')})
对于以上代码来说,setTimeout
可能执行在前,也可能执行在后
- 首先
setTimeout(fn, 0) === setTimeout(fn, 1)
,这是由源码决定的 - 进入事件循环也是须要老本的,如果在筹备时候破费了大于 1ms 的工夫,那么在 timer 阶段就会间接执行
setTimeout
回调 - 那么如果筹备工夫破费小于 1ms,那么就是
setImmediate
回调先执行了
当然在某些状况下,他们的执行程序肯定是固定的,比方以下代码:
const fs = require('fs')
fs.readFile(__filename, () => {setTimeout(() => {console.log('timeout');
}, 0)
setImmediate(() => {console.log('immediate')
})
})
在上述代码中,setImmediate
永远 先执行。因为两个代码写在 IO 回调中,IO 回调是在 poll 阶段执行,当回调执行结束后队列为空,发现存在 setImmediate
回调,所以就间接跳转到 check 阶段去执行回调了。
下面都是 macrotask 的执行状况,对于 microtask 来说,它会在以上每个阶段实现前 清空 microtask 队列,
setTimeout(() => {console.log('timer21')
}, 0)
Promise.resolve().then(function() {console.log('promise1')
})
对于以上代码来说,其实和浏览器中的输入是一样的,microtask 永远执行在 macrotask 后面。
最初来看 Node 中的 process.nextTick
,这个函数其实是独立于 Event Loop 之外的,它有一个本人的队列,当每个阶段实现后,如果存在 nextTick 队列,就会 清空队列中的所有回调函数,并且优先于其余 microtask 执行。
setTimeout(() => {console.log('timer1')
Promise.resolve().then(function() {console.log('promise1')
})
}, 0)
process.nextTick(() => {console.log('nextTick')
process.nextTick(() => {console.log('nextTick')
process.nextTick(() => {console.log('nextTick')
process.nextTick(() => {console.log('nextTick')
})
})
})
})
对于以上代码,永远都是先把 nextTick 全副打印进去。
JavaScript 类数组对象的定义?
一个领有 length 属性和若干索引属性的对象就能够被称为类数组对象,类数组对象和数组相似,然而不能调用数组的办法。常见的类数组对象有 arguments 和 DOM 办法的返回后果,还有一个函数也能够被看作是类数组对象,因为它含有 length 属性值,代表可接管的参数个数。
常见的类数组转换为数组的办法有这样几种:
(1)通过 call 调用数组的 slice 办法来实现转换
Array.prototype.slice.call(arrayLike);
(2)通过 call 调用数组的 splice 办法来实现转换
Array.prototype.splice.call(arrayLike, 0);
(3)通过 apply 调用数组的 concat 办法来实现转换
Array.prototype.concat.apply([], arrayLike);
(4)通过 Array.from 办法来实现转换
Array.from(arrayLike);
对 AJAX 的了解,实现一个 AJAX 申请
AJAX 是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的 异步通信,从服务器获取 XML 文档从中提取数据,再更新以后网页的对应局部,而不必刷新整个网页。
创立 AJAX 申请的步骤:
- 创立一个 XMLHttpRequest 对象。
- 在这个对象上 应用 open 办法创立一个 HTTP 申请,open 办法所须要的参数是申请的办法、申请的地址、是否异步和用户的认证信息。
- 在发动申请前,能够为这个对象 增加一些信息和监听函数。比如说能够通过 setRequestHeader 办法来为申请增加头信息。还能够为这个对象增加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变动时会触发 onreadystatechange 事件,能够通过设置监听函数,来解决申请胜利后的后果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接管实现,这个时候能够通过判断申请的状态,如果状态是 2xx 或者 304 的话则代表返回失常。这个时候就能够通过 response 中的数据来对页面进行更新了。
- 当对象的属性和监听函数设置实现后,最初调 用 sent 办法来向服务器发动申请,能够传入参数作为发送的数据体。
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
// 创立 Http 申请
xhr.open("GET", url, true);
// 设置状态监听函数
xhr.onreadystatechange = function() {if (this.readyState !== 4) return;
// 当申请胜利时
if (this.status === 200) {handle(this.response);
} else {console.error(this.statusText);
}
};
// 设置申请失败时的监听函数
xhr.onerror = function() {console.error(this.statusText);
};
// 设置申请头信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
// 发送 Http 申请
xhr.send(null);
应用 Promise 封装 AJAX:
// promise 封装实现:function getJSON(url) {
// 创立一个 promise 对象
let promise = new Promise(function(resolve, reject) {let xhr = new XMLHttpRequest();
// 新建一个 http 申请
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function() {if (this.readyState !== 4) return;
// 当申请胜利或失败时,扭转 promise 的状态
if (this.status === 200) {resolve(this.response);
} else {reject(new Error(this.statusText));
}
};
// 设置谬误监听函数
xhr.onerror = function() {reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置申请头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 申请
xhr.send(null);
});
return promise;
}
扩大运算符的作用及应用场景
(1)对象扩大运算符
对象的扩大运算符 (…) 用于取出参数对象中的所有可遍历属性,拷贝到以后对象之中。
let bar = {a: 1, b: 2};
let baz = {...bar}; // {a: 1, b: 2}
上述办法实际上等价于:
let bar = {a: 1, b: 2};
let baz = Object.assign({}, bar); // {a: 1, b: 2}
Object.assign
办法用于对象的合并,将源对象 (source)
的所有可枚举属性,复制到指标对象 (target)
。Object.assign
办法的第一个参数是指标对象,前面的参数都是源对象。(如果指标对象与源对象有同名属性,或多个源对象有同名属性,则前面的属性会笼罩后面的属性)。
同样,如果用户自定义的属性,放在扩大运算符前面,则扩大运算符外部的同名属性会被笼罩掉。
let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}}; // {a: 2, b: 4}
利用上述个性就能够很不便的批改对象的局部属性。在 redux
中的 reducer
函数规定必须是 一个纯函数 ,reducer
中的 state
对象要求不能间接批改,能够通过扩大运算符把批改门路的对象都复制一遍,而后产生一个新的对象返回。
须要留神:扩大运算符对对象实例的拷贝属于浅拷贝。
(2)数组扩大运算符
数组的扩大运算符能够将一个数组转为用逗号分隔的参数序列,且每次只能开展一层数组。
console.log(...[1, 2, 3])
// 1 2 3
console.log(...[1, [2, 3, 4], 5])
// 1 [2, 3, 4] 5
上面是数组的扩大运算符的利用:
- 将数组转换为参数序列
function add(x, y) {return x + y;}
const numbers = [1, 2];
add(...numbers) // 3
- 复制数组
const arr1 = [1, 2];
const arr2 = [...arr1];
要记住:扩大运算符 (…) 用于取出参数对象中的所有可遍历属性,拷贝到以后对象之中,这里参数对象是个数组,数组外面的所有对象都是根底数据类型,将所有根底数据类型从新拷贝到新的数组中。
- 合并数组
如果想在数组内合并数组,能够这样:
const arr1 = ['two', 'three'];const arr2 = ['one', ...arr1, 'four', 'five'];// ["one", "two", "three", "four", "five"]
- 扩大运算符与解构赋值联合起来,用于生成数组
const [first, ...rest] = [1, 2, 3, 4, 5];first // 1rest // [2, 3, 4, 5]
须要留神:如果将扩大运算符用于数组赋值,只能放在参数的最初一位,否则会报错。
const [...rest, last] = [1, 2, 3, 4, 5]; // 报错 const [first, ...rest, last] = [1, 2, 3, 4, 5]; // 报错
- 将字符串转为真正的数组
[...'hello'] // ["h", "e", "l", "l", "o"]
- 任何 Iterator 接口的对象,都能够用扩大运算符转为真正的数组
比拟常见的利用是能够将某些数据结构转为数组:
// arguments 对象
function foo() {const args = [...arguments];
}
用于替换 es5
中的 Array.prototype.slice.call(arguments)
写法。
- 应用
Math
函数获取数组中特定的值
const numbers = [9, 4, 7, 1];
Math.min(...numbers); // 1
Math.max(...numbers); // 9