高级程序设计4杂记

141 阅读1分钟

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');