50个JS的高级知识点
1. undeclared 与 undefined 的区别?
- undefined: 声明了变量,但是没有赋值
- undeclared: 没有声明变量就直接使用
var a // undefined
b; // b is not defined
2. let & const 与 var 的区别?
- var 存在变量提升,可重复声明同一变量,声明的变量均可改
- let 没有变量提升,不可重复声明同一变量,声明的变量均可改
- const 没有变量提升,不可重复声明同一变量,声明的基本数据类型不可改,引用类型可改属性,不可只声明变量而不复制
3. 暂时性死区问题
var a = 100;
if(1){
a = 10;
// 在当前块作用域中存在a使用let/const 声明的情况,给a赋值10时,只会在当前作用域找变量a
// 而这时,还未到声明时候,所以控制台 抛出引用错误:Cannot access 'a' before initialization
let a = 1;
}
4. 获取DOM元素有哪些方法
注意:
document
可以换成dom节点
,那么搜索范围在dom节点
的子节点内
方法 | 描述 | 返回 |
---|---|---|
document.getElementById(id) | 通过id获取dom | 符合条件的dom对象 |
document.getElementsByTagName(tagName) | 通过标签名获取dom | 符合条件的所有dom对象组成的类数组 |
document.getElementsByClassName(class) | 通过class获取dom | 符合条件的所有dom对象组成的类数组 |
document.getElementsByName(name) | 通过标签的属性name获取dom | 符合条件的所有dom对象组成的类数组 |
document.querySelector(选择器) | 通过选择器获取dom | 符合条件的第一个dom对象 |
document.querySelectorAll(选择器) | 通过选择器获取dom | 符合条件的所有dom对象组成的类数组 |
5. 操作DOM元素有哪些方法
标题 | 描述 |
---|---|
createElement | 创建一个标签节点 |
createTextNode | 创建一个文本节点 |
cloneNode(deep) | 复制一个节点,连同属性与值都复制,deep为true时,连同后代节点一起复制,否则只复制当前节点 |
createDocumentFragment | 创建一个文档碎片节点 |
appendChild | 追加子元素 |
insertBefore | 将元素插入前面 |
removeChild | 删除子元素 |
replaceChild | 替换子元素 |
getAttribute | 获取节点的属性 |
createAttribute | 创建属性 |
setAttribute | 设置节点属性 |
removeAttribute | 删除节点属性 |
element.attributes | 将属性生成类数组对象 |
6. DOM的类型有哪几种?
7. js的作用域及作用域链
8. 数组的splice 与 slice 有什么区别
方法 | 参数 | 描述 |
---|---|---|
splice | splice(start,num,item1,item2..) | 从start索引开始,截取num个元素,并插入item1、item2到原数组里,影响原数组 |
slice | slice(start,end) | 从start开始,截取到end-1,如果没有end,则截取到最后一个元素,不影响原数组 |
9. substr 和 substring 的区别?
方法 | 参数 | 描述 |
---|---|---|
substr | substr(start, length) | 返回从start位置开始length长度的字符串 |
substring | substring(start, end) | 返回从start位置到end-1位置的字符串 |
10. includes 比 indexOf 好在哪
includes
可以检测 NaN
,indexOf
不能检测NaN
11. for循环中使用定时器
for(var i = 0; i<3;i++){
setTimeout(function(){
console.log(i);
},0); // 3,3,3
}
解决方法一:
for(let i = 0; i<3;i++){
setTimeout(function(){
console.log(i);
},0); // 1,2,3
}
解决方法二:
for(let i = 0; i<3;i++){
(function(i){
setTimeout(function(){
console.log(i);
},0); // 1,2,3
})()
}
12. Promise解决了什么问题
- 解决了回调地狱问题
- 代码可读性提高
- 你可以信任Promise,它的状态只会改变一次并且不可逆
13. async/await 解决了什么问题
对于 async/await ,我总结为一句话async/await 是generator + Promise 的语法糖,它用同步的方式执行异步代码
15. JS延迟加载的方法有哪些?
- 1、<script async src="script.js"></script>:给script标签加async属性,则加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)
- 2、<script defer src="script.js"></script>:给script标签加defer属性,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成
- 3、动态创建script标签:等到DOMContentLoaded 事件触发时,生成一个script标签,渲染到页面上上
- 4、setTimeout定时器延迟代码执行
16. new 操作符的原理
分析下new的整个过程
- 1.创建一个空对象
- 2.继承构造函数的原型
- 3.this指向obj,并调用构造函数
- 4.返回对象
简单实现下new:
function myNew(fn, ..args){
// 第一步
const obj = {};
// 第二步
obj._proto_ = fn.prototype
// 第三步
fn.apply(obj,args)
// 第四步
return obj
}
17. 什么是文档碎片
- 是什么:一个容器,用于暂时存放创建的
dom
元素,使用document.createDocumentFragment()
创建 - 有什么用:将需要添加的大量元素 先添加到文档碎片 中,再将文档碎片添加到需要插入的位置,大大减少
dom
操作,提高性能
var oFragmeng = document.createDocumentFragment();
for(var i=0;i<10000;i++)
{
var op = document.createElement("span");
var oText = document.createTextNode(i);
op.appendChild(oText);
//先附加在文档碎片中
oFragmeng.appendChild(op);
}
//最后一次性添加到document中
document.body.appendChild(oFragmeng);
19. 宏任务与微任务有哪些
宏任务
* | 浏览器 | node |
---|---|---|
I/O | ✅ | ✅ |
setTimeout | ✅ | ✅ |
setInterval | ✅ | ✅ |
setImmediate | ❌ | ✅ |
requestAnimationFrame | ✅ | ❌ |
微任务
* | 浏览器 | node |
---|---|---|
I/O | ✅ | ✅ |
Promise.prototype.then catch finally | ✅ | ✅ |
process.nextTick | ❌ | ✅ |
MutationObserver | ✅ | ❌ |
21. Object.defineProperty 方法
22. 防抖和节流有哪些应用场景
操作 | 描述 | 场景 |
---|---|---|
防抖 | 频繁去触发一个事件,但是只触发最后一次。以最后一次为准 | 1、电脑息屏时间,每动一次电脑又重新计算时间2、input框变化频繁触发事件可加防抖3、频繁点击按钮提交表单可加防抖 |
节流 | 频繁去出发一个时间,但是只能每隔一段时间触发一次 | 1、滚动频繁请求列表可加节流2、游戏里长按鼠标,但是动作都是每隔一段时间做一次 |
23. 什么是高阶函数
js的函数其实都是指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称为高阶函数。
24. 什么是函数柯里化
是把接收多个参数的函数变换成接收一个单一参数的函数,并且返回接收余下的参数而且返回结果的新函数技术。
// 普通的add函数
function add(x, y){
return x+y
}
// currying 后
funtion curryingAdd(x){
return funtion(y){
return x+y
}
}
add(1,2) //3
curryingAdd(1)(2)
好处
- 参数复用
- 延迟执行
其实Function.prototype.bind 就是柯里化的实现例子
function sayKey(key){
console.log(this[key])
}
const person={
name:'Sunshine',
age:23
}
// call 不是柯里化
sayKey.call(person,'name') // 立即输出 Sunshine
// bind是柯里化
const say = sayKey.bind(person) // 不执行
// 想执行再执行
say('name') // 输出 Sunshine
25. 什么是 compose
简单的compose函数
const compose = (a,b)=>c=>a(b(c));
例子: 统计单词个数
const space = (str) => str.split(' ' );
const len = (arr) => arr.length;
// 普通写法
console.log(len(space('i am linsanxin'))) // 3
// compose 写法
const compose = (...fn)=> value => {
return fn.reduce((value, fn)=>{
return fn(value)
}, value)
}
const computed = compose(space, len)
console.log(computed('i am linsanxin')) // 3
26. 箭头函数与普通函数的区别
- 箭头函数不可用作构造函数,不能使用new
- 箭头函数没有自己的this
- 箭头函数没有 arguments 对象
- 箭头函数没有原型对象
27. Symbol的应用场景
场景1 使用Symbol来作为对象属性名
平常我们的对象属性都是字符串,其实也可用Symbol来当做属性名
const gender = Symbol('gender');
const obj = {
name: 'sunshine',
age: 23,
[gender]: '男'
}
console.log(obj[gender]) // 男
但是Symbol 作为属性的属性不会被枚举出来,这就是JSON.stringfy(obj)
时,Symbol属性会被排除在外的原因
其实想获取 Symbol 属性也不是没有办法
// 方法一
console.log(Object.getOwnPeopertySymbols(obj)) // [Symbol(gender)]
// 方法二
console.log(Reflect.ownKeys(obj)) // ['name', 'age', Symbol(gender)]
场景2 使用Symbol 来代替常量
// 赋值
const one = 'oneXin'
const two = 'twoXin'
function fun(key){
switch (key){
case one:
return 'one'
break;
case tow:
return 'two'
break;
}
}
如果变量少的话还好,如果变量多的时候,赋值变量名很烦,可以利用Symbol的唯一性
const one = Symbol()
const two = Symbol()
应用场景3 使用Symbol 定义类的私有属性
28. AMD 和 CMD 的区别
模块化 | 代表应用 | 特点 |
---|---|---|
AMD | require.js | 1. AMD的api默认一个当多个用 2. 依赖前置,异步执行 |
CMD | sea.js | 1. CMD的api严格区分,推崇职责单一 2. 依赖就近,按需加载,同步执行 |
29. Commonjs 和 ES6 Module 的区别
- Commonjs是拷贝输出,ES6模块化是引用输出
- Commonjs是运行时加载,ES6模块化是编译时输出接口
- Commonjs是单个值导出,ES6模块化可以多个值导出
- Commonjs是动态语法可写在函数体中,ES6模块化静态语法只能写在顶层
- Commonjs的this是模块化,ES6模块化的this是undefined
30. 为什么Commonjs 不适用于浏览器
var math = require('math')
math.add(2, 3)
第二行math.add(2, 3)
,在第一行之后运行,因此必须等math.js加载完成。而前端加载时间取决于网速的快慢,等待时间浏览器会处于假死状态,因此,浏览器的模块不能采用‘同步加载’,只能采用‘异步加载’,这就是AMD规范诞生的背景。
36. 深度遍历与广度遍历的区别
对于算法来说无非就是时间换空间,空间换时间
- 1、深度优先不需要记住所有的节点, 所以占用空间小, 而广度优先需要先记录所有的节点占用空间大
- 2、深度优先有回溯的操作(没有路走了需要回头)所以相对而言时间会长一点
- 3、深度优先采用的是堆栈的形式, 即先进后出
- 4、广度优先则采用的是队列的形式, 即先进先出
37. js中的设计模式
38. forEach如何跳出循环
forEach是不用通过break 或者 return 来实现跳出循环的,forEach的回调函数形成了一个作用域,在里面使用return
并不会跳出,只会被当做continue
,可以利用try catch
function getItemById(arr, id){
var item = null;
try{
arr.forEach(function (curItem, i){
if(curItem.id == id){
item = curItem;
throw Error();
}
})
}catch(e){}
return item;
}
39、JS中如何将页面重定向到另一个页面?
- 1、使用 location.href:window.location.href =“[www.onlineinterviewquestions.com/”]
- 2、使用 location.replace: window.location.replace(" [www.onlineinterviewquestions.com/];
41. 鼠标事件有哪些
注明:鼠标左中右键看
event
对象上的button
属性,对应1,2,3
事件 | 说明 |
---|---|
click | 点击鼠标左键,在用户焦点在按钮并按下enter,也会触发 |
dbclick | 双击鼠标左键触发 |
mousedown | 单击鼠标任意一个按键都触发 |
mouseout | 鼠标指针位于某个元素上且将要移出元素边界时触发 |
mouseover | 鼠标指针移出某个元素到另一个元素上时触发 |
mouseup | 松开任意鼠标按键时触发 |
mousemove | 鼠标在某个元素上时持续发生 |
mouseenter | 鼠标进入某个元素边界时触发 |
mouseleave | 鼠标离开某个元素边界时触发 |
42. 键盘事件
事件 | 说明 |
---|---|
onkeydown | 某个键盘按键被按下时触发 |
onkeyup | 某个键盘按键被松开时触发 |
onkeypress | 某个按键被按下时触发,不监听功能键,如ctrl,shift |
43、JS中鼠标事件的各个坐标
属性 | 说明 | 兼容性 |
---|---|---|
offsetX | 以当前的目标元素左上角为原点,定位x轴坐标 | 除Mozilla外都兼容 |
offsetY | 以当前的目标元素左上角为原点,定位y轴坐标 | 除Mozilla外都兼容 |
clientX | 以浏览器可视窗口左上角为原点,定位x轴坐标 | 都兼容 |
clientY | 以浏览器可视窗口左上角为原点,定位y轴坐标 | 都兼容 |
pageX | 以doument对象左上角为原点,定位x轴坐标 | 除IE外都兼容 |
pageY | 以doument对象左上角为原点,定位y轴坐标 | 除IE外都兼容 |
screenX | 以计算机屏幕左上顶角为原点,定位x轴坐标(多屏幕会影响) | 全兼容 |
screenY | 以计算机屏幕左上顶角为原点,定位y轴坐标 | 全兼容 |
layerX | 最近的绝对定位的父元素(如果没有,则为 document 对象)左上顶角为元素,定位 x 轴坐标 | Mozilla 和 Safari |
layerY | 最近的绝对定位的父元素(如果没有,则为 document 对象)左上顶角为元素,定位 y 轴坐标 | Mozilla 和 Safari |
44. js中元素视图的各个尺寸
属性 | 说明 |
---|---|
offsetLeft | 获取当前元素到定位父节点的left方向的距离 |
offsetTop | 获取当前元素到定位父节点的top方向的距离 |
offsetWidth | 获取当前元素 width + 左右padding + 左右border-width |
offsetHeight | 获取当前元素 height + 上下padding + 上下border-width |
clientWidth | 获取当前元素 width + 左右padding |
clientHeight | 获取当前元素 height + 上下padding |
scrollWidth | 当前元素内容真实的宽度,内容不超出盒子宽度时为盒子的clientWidth |
scrollHeight | 当前元素内容真实的高度,内容不超出盒子高度时为盒子的clientHeight |
45. window视图的各个尺寸
属性 | 说明 |
---|---|
innerWidth | innerWidth 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏) |
innerHeight | innerHeight 浏览器可视窗口可视区高度(不包括浏览器控制台、菜单栏、工具栏) |
46. Document 文档视图的各个尺寸
属性 | 说明 |
---|---|
document.documentElement.clientWidth | 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏) |
document.documentElement.clientHeight | 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏) |
document.documentElement.offsetHeight | 获取整个文档的高度(包含body的margin) |
document.body.offsetHeight | 获取整个文档的高度(不包含body的margin) |
document.documentElement.scrollTop | 获取文档的滚动top方向的距离(当窗口发生滚动时发生滚动时值改变) |
document.documentElement.scrollLeft | 获取文档的滚动left方向的距离(当窗口发生滚动时值改变) |
9个高级JavaScript方法
1. getBoundingClientRect
Element.getBoundingClientRect()
方法返回元素的大小及其相对视口的位置。返回的是一个对象,对象李包含了8个属性:left, right, top, bottom, width, height, x, y
可以根据getBoundingClientRect
方法判断当前元素是否完整出现在视口里
// html
<div id="box"></div>
body {
height: 3000px;
width: 3000px;
}
#box {
width: 300px;
height: 300px;
background-color: red;
margin-top: 300px;
margin-left: 300px;
}
// js
const box = document.getElementById('box')
window.onscroll = function () {
// box完整出现在视口里才会输出true,否则为false
console.log(checkInView(box))
}
function checkInView(dom) {
const { top, left, bottom, right } = dom.getBoundingClientRect()
console.log(top, left, bottom, right)
console.log(window.innerHeight, window.innerWidth)
return top >= 0 &&
left >= 0 &&
bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
right <= (window.innerWidth || document.documentElement.clientWidth)
}
根据这个用处,咱们可以实现:懒加载和无限滚动
注意,该方法每次scroll都得重新计算,性能耗费打,引起重绘回流。
2. IntersectionObserver
2.1 是什么
IntersectionObserver
接口提供了一种异步观察目标元素与祖先元素或顶级文档视窗交叉状态的方法。祖先元素与视窗被称为根(root)
通俗点说:IntersectionObserver
是用来监听某个元素与视口的交叉状态的。如下图:一开始整个元素都在视口内,那么元素与视口交叉状态就是100%,而后滚动,元素只显示一半,那么交叉状态就是50%。
2.2 用法
// 接收两个参数 callback option
let io = new IntersectionObserver(callback, option);
// 开始观察(可观察多个元素)
io.observe(document.getElementById('example1'));
// 停止观察某个元素
io.unobserve(element);
// 关闭观察
io.disconnect();
2.3 callback
callback
一般有两种触发情况,一种是目标刚刚进入视口,另外一种是完全离开视口。
let io = new IntersectionObsever(
entries=>{
console.log(entries);
}
);
callback
函数的参数是(entries
)一个数组,每个成员都是一个 IntersectionObserverEnter
对象。
2.4 IntersectionObserverEnter 对象
{
time: 3893.92,
rootBounds: ClientRect {
bottom: 920,
height: 1024,
left: 0,
right: 1024,
top: 0,
width: 920
},
boundingClientRect: ClientRect {
// ...
},
intersectionRect: ClientRect {
// ...
},
intersectionRatio: 0.54,
target: element
}
属性解析:
-
time
: 可见性发生变化的时间,是一个高精度时间戳,单位为毫秒 -
target
: 被观察的目标元素,是一个DOM节点对象 -
rootBounds
: 根元素的矩形区域的信息,getBoundingClientRect()
方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
-
boundingClientRect
: 目标元素的矩形区域信息 -
intersectionRect
: 目标元素与视口(或根元素)的交叉区域的信息 -
intersectionRatio
: 目标元素的可见比例:即intersectionRect
占boundingClientRect
的比例,完全可见时为1,完全不可见时小于等于0
2.5 option
第二个参数里有两个重要的属性:threshold和root
threshold
属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0]
,即交叉比例达到0时触发回调函数。
new IntersectionObserver(
entries=>{ /* ... */ },
{ threshold:[0,0.25,0.5,0.75,1] }
)
用户可以自定义这个数组。比如[0, 0.25, 0.5, 0.75, 1]就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。
root
属性指定目标元素所在的容器节点(即根元素)。注意,容器元素必须是目标元素的祖先节点。
new IntersectionObserver({entries=>{/*...*/}},{
threshold: [0],
root: document.getElementById('#container')
})
2.6 完整例子
body {
height: 3000px;
width: 3000px;
}
#box1 {
width: 300px;
height: 300px;
background-color: red;
margin-top: 100px;
margin-left: 300px;
}
#box2 {
width: 300px;
height: 300px;
background-color: red;
margin-top: 100px;
margin-left: 300px;
}
<div id="box1"></div>
<div id="box2"></div>
const io = new IntersectionObserver(entries => {
console.log(entries)
}, {
threshold: [0, 0.25, 0.5, 0.75, 1]
// root: xxxxxxxxx
})
io.observe(document.getElementById('box1'))
io.observe(document.getElementById('box2'))
2.6 使用场景
可以像getBoundingClientRect
那样判断元素是否在视口,做到懒加载和无限滚动功能,且不会引起重绘回流
3. createNodeIterator
节点原生遍历,详细参数
)
const body = document.getElementsByTagName('body')[0]
const it = document.createNodeIterator(body)
let root = it.nextNode()
while(root){
console.log(root)
root = it.nextNode()
}
4. getComputedStyle
window.getComputedStyle()
方法返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有css属性值。私有的css属性值可以通过对象提供的API或通过简单地使用CSS属性名称进行索引来访问。
window.getCoumtedStyle(element, perudoElement)
-
element
必填,要获取的样式元素 -
pseudoElement
可选,伪类元素,当不查询伪类元素的时候可以忽略或者传入null
配合 getPropertyValue
可以获取到具体样式
// html
#box {
width: 640px;
height: 200px;
background-color: yellow;
}
<div id="box"></div>
//js
const box = document.getElementById('box')
const styles = window.getComputedStyle(box)
const height = styles.getPropertyValue('height')
const width = styles.getPropertyValue('width')
console.log(height, width)
5. requestAnimationFrame
JS动画,参考requestAnimationFrame理解与实践
6. requestIdleCallback
requestIdleCallback回调的执行的前提条件是当前浏览器处于空闲状态。
7. DOMContentLoaded
7.1 是什么
当初始的HTML文档被完全加载和解析之后,DOMContentLoaded
事件被触发,而无需等待样式表、图像和子框架的完全加载。
浏览器向服务器请求到了HTML 文档后便开始解析,产物是DOM,到这里HTML文档被加载和解析完成了入股有CSS的会根据CSS生成CSSOM,然后再由DOM和CSSOM合并产生渲染树。有了渲染树,知道了所有节点的样式,下面便根据这些节点以及样式计算它们在浏览器中确切的大小和位置,这就是布局阶段,节点绘制到浏览器的过程如下图所示:
js可以阻塞DOM生成,也就是说当浏览器解析HTML文档时,如果遇到
<body>
<script type="text/javascript">
console.log(document.getElementById('ele')); // null
</script>
<div id="ele"></div>
<script type="text/javascript">
console.log(document.getElementById('ele')); // <div id="ele"></div>
</script>
</body>
另外,因为js可以查询任何对象的样式,所以意味着在CSS解析完成,也就是CSSOM生成之后,js才能被执行。
- 当文档中没有脚本时,浏览器解析完文档便出发
DOMContentLoaded
事件 - 如果文档中包含脚本,则脚本会阻塞文档的解析,而脚本需要等CSSOM构建完成才能执行。
- 在任何时候
DOMConetentLoaded
的触发都不需要等待图片等其他资源加载完成
7.2 异步脚本
如果我们想要页面尽快显示,那我们可以使用异步脚本。H5中定义了两个异步脚本的方法:defer 和async。
同步脚本(标签不包含async和defer),当HTML文档解析时遇见的(同步)脚本,则停止解析,先去加载脚本,然后执行,执行结束后继续解析HTML文档。过程如下图:
watermark4.png
defer 脚本:
当HTML 文档解析时遇到defer脚本,则在后台加载脚本,文档解析过程不中断,而等文档解析结束之后,defer脚本执行。另外,defer脚本的执行顺序与定义时的位置有关。过程如下图:
watermark5.jpg
async 脚本:
当HTML 文档解析时遇到async脚本,则在后台加载脚本,文档解析时不中断。脚本加载完成后,文档停止解析,脚本执行,执行结束后文档继续解析。过程如下图:
watermark6.jpg
defer 与 DOMContentLoaded
如果 script 标签中包含 defer,那么这一块脚本将不会影响 HTML 文档的解析,而是等到 HTML 解析完成后才会执行。而 DOMContentLoaded 只有在 defer 脚本执行结束后才会被触发。 所以这意味着什么呢?HTML 文档解析不受影响,等 DOM 构建完成之后 defer 脚本执行,但脚本执行之前需要等待 CSSOM 构建完成。在 DOM、CSSOM 构建完毕,defer 脚本执行完成之后,DOMContentLoaded 事件触发。
async 与 DOMContentLoaded
如果 script 标签中包含 async,则 HTML 文档构建不受影响,解析完毕后,DOMContentLoaded 触发,而不需要等待 async 脚本执行、样式表加载等等。
7.3 DOMContentLoaded 与load
当HTML文档解析完成后就会触发DOMContentLoaded,而所有资源加载完成之后,load时间才会被触发
另外,我们在jQuery 中常用到的
(document).ready(function(){/*..*/})
其实监听的就是DOMContentLoaded事件, 而(document).load(function(){/*..*/})
监听的事load事件
7.4 使用
document.addEventListener("DOMContentLoaded", function(event){
console.log("DOM fully loaded and parsed");
})
8. MutationObserver
MutationObserver
是一个内建对象,它观察DOM元素,并在检测到更改时触发回调
8.2 用法
// 选择需要观察变动的节点
const targetNode = document.getElementById('some-id');
// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };
// 当观察到变动时执行的回调函数
const callback = function(mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for(let mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('A child node has been added or removed.');
}
else if (mutation.type === 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}
}
};
// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);
// 以上述配置开始观察目标节点
observer.observe(targetNode, config);
// 之后,可停止观察
observer.disconnect();
8.3 config
config
是一个具有布尔选项的对象,该布尔选项表示“将对哪些更改做出反应”:
childList
—— node
的直接子节点的更改,
subtree
—— node
的所有后代的更改,
attributes
—— node
的特性(attribute
),
attributeFilter
—— 特性名称数组,只观察选定的特性。
characterData
—— 是否观察 node.data
(文本内容)
其他几个选项:
attributeOldValue
—— 如果为 true,则将特性的旧值和新值都传递给回调(参见下文),否则只传新值(需要 attributes
选项),
characterDataOldValue
—— 如果为 true,则将 node.data
的旧值和新值都传递给回调(参见下文),否则只传新值(需要 characterData
选项)。
9. Promise.any
9.1 是什么
Promise.any()
接收一个Promise
可迭代对象,只要其中一个 promise
成功,就返回那个已经成功的 promise
。如果可迭代对象中没有一个 promise
成功(即所有的promise
都被拒绝/失败),就返回一个失败的promise
和AggregateError
类型的实例,它是Error的一个子类,用于把单一的错误集合在一起。本质上,这个方法和Promise.All()
是相反的。
const promise1 = new Promise((resolve, reject)=>{
setTimeout(reject,100,'promise 1 rejected');
});
const promise2 = new Promise((resolve, reject)=>{
setTimeout(resolve, 400, 'promise 2 resolved at 400ms')
})
const promise3 = new Promise((resolve, reject)=>{
setTimeout(resolve, 700, 'promise 3 resolved at 800ms')
})
(async=>{
try{
let value = await Promise.any([promise1, promise2, promise3]);
console.log(value);
} catch(error){
console.log(error)
}
})();
// promise 2 resolved at 400 ms