1.对象本来不可用(没有内置遍历器)for...of循环,可以手动内置遍历器,使其可用
const obj = {
a: 'a',
b: 'b',
c: 'c',
[Symbol.iterator]: function*(){
yield this.a;
yield this.c;
yield this.b
}
};
for(const i of obj){
console.log(i) //a,c,b
}
2.与Map类似,Set使用严格对象相等的标准来检查值的匹配性
const set = new Set();
const functionVal = function () {};
set.add(functionVal);
console.log(set.has(function () {})); //false
3.与weakMap类似,使用数组初始化弱集合,只要有一个值无效就会抛错,原始值可以先包装成对象再用作值。
const ws = new WeakSet([{},[],'chenshun']) //Invalid value used in weak set
const ws = new WeakSet([{},[],new String('chenshun')])
4.使用弱集合的一个例子
const disabledElements = new WeakSet();
const loginButton = document.querySelector('#login');
disabledElements.add(loginButton);
5.有4种原生集合类型定义了默认迭代器(没有对象):Array,所有定型数组,Map,Set。意味着都可以使用for-of循环,都兼容扩展操作符
6.自定义迭代器:为了让一个可迭代对象能够创建多个迭代器,必须每创建一个迭代器就对应一个新的计数器,可以用闭包来实现
class Counter {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() {
let count = 1,
limit = this.limit;
return {
next() {
if(count <= limit){
return {done: false, value: count++};
} else{
return {done: true, value: undefined};
}
}
}
}
}
7.尾递归优化的条件:(1)代码在严格模式下执行;(2)外部函数的返回值是对尾调用函数的调用;(3)尾调用函数返回后不需要执行额外的逻辑;(4)尾调用函数不是引用外部函数作用域中自由变量的闭包。如下例子使用递归计算斐波那契数列,fib(n)的栈帐数的内存复杂度是O(2^n)。
function fib(n){
if(n < 2){
return n
}
return fib(n - 1) + fib(n - 2); //返回语句中有一个相加操作故不符合尾递归优化
}
解决这个问题有不同的策略,比如把递归改写成迭代循环模式。不过,也可以保持递归实现,但将其重构为满足优化条件的形式。为此可以使用两个嵌套的函数,外部函数作为基础骨架,内部函数执行递归:
"use strict";
// 基础框架
function fib(n){
return fibImpl(0, 1, n);
}
// 执行递归
function fibImpl(a, b, n){
if(n === 0){
return a;
}
return fibImpl(b, a + b, n - 1);
}
8.在一些早期浏览器中,把HTML元素保存在某个闭包的作用域中,就相当于宣布该元素不能被销毁。以下代码创建一个闭包,即element元素的事件处理程序,而这个处理程序又创建了一个循环引用。匿名函数引用着assignHandler()的活动对象,阻止了对element的引用计数归零。只要这个匿名函数存在,element的引用计数就至少等于1。也就是说内存不会被回收。
function assignHandler() {
let element = document.getElementById('someElement');
element.onclick = () => console.log(element.id);
}
可以做如下优化
function assignHandler() {
let element = document.getElementById('someElement');
let id = element.id;
element.onclick = () => console.log(id);
element = null
}
9.取消期约
<!DOCTYPE html>
<html>
<body>
<button id="start">开始</button>
<button id="cancel">取消</button>
<script>
class CancelToken {
constructor(cancelFn) {
this.promise = new Promise((resolve, reject) => {
cancelFn(() => {
setTimeout(console.log, 0, 'delay cancelled');
resolve();
});
});
}
}
const startButton = document.querySelector("#start");
const cancelButton = document.querySelector("#cancel");
function cancelableDeleyedResolve(deley) {
setTimeout(console.log, 0, 'set delay');
return new Promise((resolve, reject) => {
const id = setTimeout((() => {
setTimeout(console.log, 0, 'delayed resolve');
resolve()
}), deley);
const cancelToken = new CancelToken((cancelCallback) =>
cancelButton.addEventListener('click', cancelCallback)); // 点击后会马上打印'delay cancelled'
cancelToken.promise.then(() => clearTimeout(id));
})
}
startButton.addEventListener('click', () => cancelableDeleyedResolve(10000));
</script>
</body>
</html>
10.期约进度
class TrackablePromise extends Promise { constructor(executor) {
const notifyHandlers = [];
super((resolve, reject) => {
return executor(resolve, reject, (status) => {
notifyHandlers.map((handler) => handler(status));
});
});
this.notifyHandlers = notifyHandlers;
}
notify(notifyHandler) {
this.notifyHandlers.push(notifyHandler);
return this;
}}
let p = new TrackablePromise((resolve, reject, notify) => {
function countdown(x) {
if(x > 0) {
notify(`${20 * x}% remaining`);
setTimeout(() => countdown(x - 1), 1000);
}else{
resolve();
}
}
countdown(5);
})
p.notify((x) => setTimeout(console.log, 0, 'progress', x));
p.then(() => setTimeout(console.log, 0, 'completed'));
/**
* progress 80% remaining
* progress 60% remaining
* progress 40% remaining
* progress 20% remaining
* completed
*/
11.模拟鼠标事件
<!DOCTYPE html>
<html>
<body>
<div class="btn-wrapper">
<button type="button" id="myBtn">新点击</button>
</div>
<script>
const btn = document.getElementById('myBtn');
const btnWrapper = document.querySelector(".btn-wrapper");
const event = document.createEvent('MouseEvents'); // 传入字符串MouseEvents模拟鼠标事件
// 传入参数一一对应
event.initEvent('newclick', true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
btnWrapper.addEventListener('newclick', ()=>{ // 冒泡
console.log('点击div')
})
btn.addEventListener('newclick', ()=>{
console.log('点击btn');
})
btn.dispatchEvent(event); // 输出点击btn 点击div
</script>
</body>
</html>
12.模拟键盘事件
<!DOCTYPE html><html>
<body>
<div class="input-wrapper">
<input type="text" id="myTextBox">
</div>
<script>
const myTextBox = document.querySelector('#myTextBox');
const inputWrapper = document.querySelector('.input-wrapper');
let event;
if(document.implementation.hasFeature('KeyboardEvent', '3.0')){
event = document.createEvent('KeyboardEvent');
event.initKeyboardEvent('newKeydown', true, true, document.defaultView, 'a', 0, 'Shift', 0);
myTextBox.addEventListener('newKeydown', ()=>{
console.log('触发模拟键盘事件');
})
inputWrapper.addEventListener('newKeydown', ()=>{
console.log('冒泡');
})
}
myTextBox.dispatchEvent(event); //输出触发模拟键盘事件 冒泡
</script>
</body>
</html>
13.模拟其它事件
let event = document.createEvent('HTMLEvents');
event.initEvent('focus', true, false);
target.dispatchEvent('event');