87、事件代理
事件代理 也就是 事件委托
不是直接给标签添加事件 是给标签的父级添加事件 通过 事件对象 判断触发事件的标签对象是谁 执行不同的函数程序的语法形式
委托的优点
减少内存消耗
试想一下,若果我们有一个列表,列表之中有大量的列表项,我们需要在点击列表项的时候响应一个事件
如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能;
因此,比较好的方法就是把这个点击事件绑定到他的父层,也就是 ul 上,然后在执行事件的时候再去匹配判断目标元素;
所以事件委托可以减少大量的内存消耗,节约效率。
动态绑定事件
比如上述的例子中列表项就几个,我们给每个列表项都绑定了事件;
在很多时候,我们需要通过 AJAX 或者用户操作动态的增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件;
如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的;
所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。
88、不卡顿
如何在不卡住页面的情况下渲染数据,也就是说不能一次性将几万条 都渲染出来,而应该一次渲染部分 DOM,那么就可以通过 requestAnimationFrame 来 每 16 ms 刷新一次。
<ul>控件</ul>
<script>
setTimeout(() => {
// 插入十万条数据
const total = 100000
// 一次插入 20 条,如果觉得性能不好就减少
const once = 20
// 渲染数据总共需要几次
const loopCount = total / once
let countOfRender = 0
let ul = document.querySelector("ul");
function add() {
// 优化性能,插入不会造成回流
const fragment = document.createDocumentFragment();
for (let i = 0; i < once; i++) {
const li = document.createElement("li");
li.innerText = Math.floor(Math.random() * total);
fragment.appendChild(li);
}
ul.appendChild(fragment);
countOfRender += 1;
loop();
}
function loop() {
if (countOfRender < loopCount) {
window.requestAnimationFrame(add);
}
}
loop();
}, 0);
89、JavaScript中的instanceof
JavaScript中变量的类型判断常常使用typeof运算符,但使用typeof时存在一个缺陷,就是判断引用类型存储值时,无论引用的是什么类型的对象,它都返回 object。ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题。instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。
1.instanceof运算符用法
var strObj = new String("字符串");
console.log(strObj instanceof String);// true
该段代码判断的是变量strObj是否为String对象的实例,strObj 是 String 对象的实例,因此是”true”。尽管不像 typeof 方法那样灵活,但是在 typeof 方法返回 “object” 的情况下,instanceof 方法就很有用。
// 判断 foo 是否是 Foo 类的实例
function Foo(){}
var foo = new Foo();
console.log(foo instanceof Foo)
2.instanceof在继承关系中使用
// 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例
function Aoo(){}
function Foo(){}
Foo.prototype = new Aoo(); //JavaScript 原型继承
var foo = new Foo();
console.log(foo instanceof Foo)//true
console.log(foo instanceof Aoo)//true
foo作为构造函数Foo的实例,因为构造函数Foo原型继承了构造函数Aoo,因此返回true。该代码中是判断了一层继承关系中的父类,在多层继承关系中,instanceof 运算符同样适用。
3.instanceof运算符代码
function instance_of(L, R) { //L 表示左表达式,R 表示右表达式
var O = R.prototype; // 取 R 的显示原型
L = L.__proto__; // 取 L 的隐式原型
while (true) {
if (L === null)
return false;
if (O === L) // 这里重点:当 O 严格等于 L 时,返回 true
return true;
L = L.__proto__;
}
}
90、forEach中的await
不知道你是否写过类似的代码:
function test() {
let arr = [3, 2, 1]
arr.forEach(async item => {
const res = await fetch(item)
console.log(res)
})
console.log('end')
}
function fetch(x) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(x)
}, 500 * x)
})
}
test()
我当时期望的打印顺序是
3
2
1
end
结果现实与我开了个玩笑,打印顺序居然是
end
1
2
3
为什么?
其实原因很简单,那就是 forEach 只支持同步代码。
我们可以参考下 Polyfill 版本的 forEach,简化以后类似就是这样的伪代码
while (index < arr.length) {
callback(item, index) //也就是我们传入的回调函数
}
从上述代码中我们可以发现,forEach 只是简单的执行了下回调函数而已,并不会去处理异步的情况。并且你在 callback 中即使使用 break 也并不能结束遍历。
怎么解决?
一般来说解决的办法有2种,for...of和for循环。
使用 Promise.all 的方式行不行,答案是:不行
从上述代码中我们可以发现,forEach 只是简单的执行了下回调函数而已,并不会去处理异步的情况。并且你在 callback 中即使使用 break 也并不能结束遍历。
怎么解决?
一般来说解决的办法有2种,for...of和for循环。
使用 Promise.all 的方式行不行,答案是:不行
可以看到并没有按照我们期望的输出。
这样可以生效的原因是 async 函数肯定会返回一个 Promise 对象,调用 map 以后返回值就是一个存放了 Promise 的数组了,这样我们把数组传入 Promise.all 中就可以解决问题了。但是这种方式其实并不能达成我们要的效果,如果你希望内部的 fetch 是顺序完成的,可以选择第二种方式。
第1种方法是使用 for...of
async function test() {
let arr = [3, 2, 1]
for (const item of arr) {
const res = await fetch(item)
console.log(res)
}
console.log('end')
}
这种方式相比 Promise.all 要简洁的多,并且也可以实现开头我想要的输出顺序。
但是这时候你是否又多了一个疑问?为啥 for...of 内部就能让 await 生效呢。
因为 for...of 内部处理的机制和 forEach 不同,forEach 是直接调用回调函数,for...of 是通过迭代器的方式去遍历。
async function test() {
let arr = [3, 2, 1]
const iterator = arr[Symbol.iterator]()
let res = iterator.next()
while (!res.done) {
const value = res.value
const res1 = await fetch(value)
console.log(res1)
res = iterator.next()
}
console.log('end')
}
第2种方法是使用 for循环
async function test() {
let arr = [3, 2, 1]
for (var i=0;i<arr.length;i++) {
const res = await fetch(arr[i])
console.log(res)
}
console.log('end')
}
function fetch(x) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(x)
}, 500 * x)
})
}
test()
第3种方法是使用 while循环