手写 Promise 实现

最小可用版 Promise:状态机、then/catch、all/race/any/resolve

<script>
    class MyPromise{
        constructor(initFn){
            this.state = 'pending';
            this.value = null;
            this.errReason = null;
            this.onSuccessCallbacks = [];
            this.onErrorCallbacks = [];
            const resolve = (value) => {
                if(this.state === 'pending'){
                    this.state = 'fulfilled';
                    this.value = value;
                    this.onSuccessCallbacks.forEach(callback => callback());
                }
            }
            const reject = (reason) => {
                if(this.state === 'pending'){
                    this.state = 'rejected';
                    this.errReason = reason;
                    this.onErrorCallbacks.forEach(callback => callback());
                }
            }
            try{
                initFn(resolve, reject);
            }catch(error){
                reject(error);
            }
        }
        then(onSuccess, onError){
            if(this.state === 'fulfilled'){
                onSuccess && onSuccess(this.value);
            }
            if(this.state === 'rejected'){
                onError && onError(this.errReason);  // ← 检查是否存在
            }
            if(this.state==='pending'){
                if(onSuccess){
                    this.onSuccessCallbacks.push(() => {
                        onSuccess(this.value);
                    });
                }
                if(onError){  // ← 检查是否存在
                    this.onErrorCallbacks.push(() => {
                        onError(this.errReason);
                    });
                }
            }
            return this
        }
        catch(onError){
            // catch 应该检查状态
            if(this.state === 'rejected'){
                onError && onError(this.errReason);
            }
            if(this.state === 'pending'){
                if(onError){
                    this.onErrorCallbacks.push(() => {
                        onError(this.errReason);
                    });
                }
            }
            return this
        }
        static all(promises){
            return new MyPromise((resolve,reject)=>{
                const results = [];
                let completedCount = 0;
                if(promises.length===0){
                    resolve(results);
                    return;
                }
                promises.forEach((promise,index)=>{
                    MyPromise.resolve(promise).then(res=>{
                        results.push({
                            index,
                            promise:promise,
                            res:res
                        })
                        completedCount++;
                        if(completedCount===promises.length){
                            resolve(results);
                        }
                    },(err)=>{
                        reject(err);
                    })
                })
            })
        }
        
        static race(promises){
            return  new MyPromise((resolve,reject)=>{
                promises.forEach(promise=>{
                    //只要有一个成功,就返回
                    // 执行单个promise的then方法,但是会把外层的resolve和reject传给单个promise的then方法,作用是调用时返回的参数是单个promise对应的resolve赋值时里面的value值
                    MyPromise.resolve(promise).then(resolve,reject)
                })
            })
        }
        static any(promises){
            return new MyPromise((resolve,reject)=>{
                if(promises.length===0){
                    reject('All promises were rejected');
                    return;
                }
                let rejectedCount = 0;
                promises.forEach((promise,index)=>{
                    MyPromise.resolve(promise).then((res)=>{
                        resolve(res);
                    },(err)=>{
                        rejectedCount++;
                        if(rejectedCount===promises.length){
                            reject('All promises were rejected');
                        }
                    })
                })
            })
        }
        static resolve(value){
            //判断是否为MyPromise的实例
            if(value instanceof MyPromise){
                return value;
            }
            //如果不是,则包装成MyPromise的实例
            return new MyPromise((resolve)=>{
                resolve(value);
            })
        }
    }

const p1 = new MyPromise((resolve, reject) => {
    resolve('success');
});
const p2 = new MyPromise((resolve, reject) => {
    reject('error');
});
const p3 = new MyPromise((resolve, reject) => {
    resolve('success');
});
p1.then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});
MyPromise.all([p1,p2,p3]).then(res=>{

}).catch(err=>{
    console.log(err);
});
</script>

SHA-256 哈希生成

浏览器环境下使用 Web Crypto API 生成 SHA-256 哈希值

<script>
    async function sha256Browser(text) {
        // 编码为 Uint8Array
        const encoder = new TextEncoder();
        const data = encoder.encode(text);
        
        // 计算哈希
        const hashBuffer = await crypto.subtle.digest('SHA-256', data);
        
        // 转换为十六进制
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
        
        return hashHex;
    }

    // 使用示例
    sha256Browser('hello').then(hash => {
        console.log('SHA-256:', hash);
        // document.getElementById('result').textContent = hash;
    });
</script>

事件冒泡、捕获与委托

演示事件流的三个阶段:捕获、目标、冒泡以及 stopPropagation 的作用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="outer">
        <button id="btn">点击我</button>
    </div>
</body>
<script>
    // 场景1:按钮阻止了冒泡
    document.getElementById('btn').addEventListener('click', (event) => {
      console.log('按钮被点击');
      event.stopPropagation(); // 阻止冒泡!

    });
    
    // 不加 capture: true - 收不到事件
    document.getElementById('outer').addEventListener('click', () => {
      console.log('外层容器(冒泡)- 这里不会执行!');
    });
    
    // 加 capture: true - 仍然能收到事件
    document.getElementById('outer').addEventListener('click', (event) => {
      console.log('外层容器(捕获)- 这里正常执行!');
    }, { capture: true });
    
    // 输出:
    // 外层容器(捕获)- 这里正常执行!
    // 按钮被点击
    // 外层容器(冒泡)- 不会执行(被stopPropagation阻止)
</script>
</html>

手写 Ajax 封装

原生 XMLHttpRequest 封装,支持请求头、成功/失败回调

export function ajax(data) {
  let xhr = new XMLHttpRequest();
  //  初始化请求
  xhr.open(data.method || "GET", data.url, true);
  //   设置请求头
  if (data.headers) {
    for (let key in data.headers) {
      xhr.setRequestHeader(key, data.headers[key]);
    }
  }
  //   发送请求
  xhr.send(data.data || null);
  //   处理响应
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      //请求完成
      if (xhr.status === 200) {
        //请求成功
        data.success && data.success(xhr.response);
      } else {
        //请求失败
        data.error && data.error(xhr.status);
      }
    }
  };
}

Web Components 自定义元素

使用原生 JS 创建可复用的自定义 HTML 元素,支持 Shadow DOM 样式隔离

// 创建自定义元素类webComponents.js
class MyButton extends HTMLElement {
  constructor() {
    super();

    // 创建Shadow DOM(样式隔离)
    const shadow = this.attachShadow({ mode: "open" });

    // 定义组件的HTML和CSS
    shadow.innerHTML = `
        <style>
          button {
            padding: 10px 20px;
            background: blue;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
          }
          button:hover {
            background: darkblue;
          }
        </style>
        <button><slot></slot></button>
      `;
  }
}
customElements.define("my-button", MyButton);



//其他页面引入
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Web Components 示例</title>
</head>
<body>
  <h1>Web Components 演示</h1>
  
  <!-- 像普通HTML标签一样使用 -->
  <my-button>
    <div>惦记我</div>
  </my-button>
  <my-button>另一个按钮</my-button>

  <!-- 引入外部 JS 文件 -->
  <script src="./webComponents.js"></script>
</body>
</html>

WeakMap 弱引用示例

演示 WeakMap 如何避免内存泄漏,对比 Map 的强引用特性

<script>
    const map = new Map()
    function createUser(){
        const user = {
            name: '张三',
            age: 18
        }
        map.set(user, '李四')
        return user
    }
    let user = createUser()
    console.log(map.get(user))
    user = null
    console.log(map.get(user))

    // 1. 创建一个 WeakMap(弱映射-弱映射)
    const weakMap = new WeakMap();
    
    // 2. 创建一个对象作为键
    function createObj(){
        const obj = {
            name: '张三',
            age: 18
        }
        weakMap.set(obj, '李四')
        return obj
    }
    // 实现原理
    // 1.user变量强引入了user对象
    // 2.weakmap以键的形式弱引入user对象
    // 3.weakmap以值的形式强引入额外数据
    // 4.user= null,不再指向对象,强引用断开,剩下唯一的weakmap弱引用
    // 5.垃圾回收器回收weakmap弱引入的user对象。
    // 6.weakmap映射删除,额外数据失去引用,同时被垃圾回收

</script>

this 指向详解

严格模式、隐式绑定、显式绑定(call/apply/bind)、new 绑定与箭头函数

<script>
    // this指向问题
    // 严格模式下,this指向undefined
    // 1.es6函数内套function,function里面会出现严格模式,自带严格模式
    // 2.es6函数内套箭头函数,箭头函数里面不会出现严格模式,不自带
    
    // 隐式绑定 直接调用函数
    const user = {
        name: '张三',
        age: 18,
        sayHello: function(){
            console.log(this)
        }
    }
    user.sayHello()
    
    // 隐式绑定丢失
    // 赋值函数,后续调用会丢失this,从而this指向window

    // 显示绑定 call、apply、bind
    function createUser(){
        console.log(this.name)
    }
    createUser.call(user)
    createUser.apply(user)
    let user1 = createUser.bind(user)
    user1()
    
    // new 绑定(构造函数) this指向构造函数
    function Animal(){
        this.name = '动物'
        console.log(this)
    }
    let dog = new Animal()
    
    // 箭头函数没有this指向
    let fn1 = () => {}
</script>