返回首页

04-ES6+代码示例集

分类:02-ES6+现代特性
发布于:
阅读时间:112 分钟

ES6+代码示例集

📋 学习目标

  • 通过实际代码示例理解ES6+特性的使用
  • 掌握Promise和异步编程的实际应用
  • 学会使用生成器函数和迭代器
  • 理解对象操作和数组处理的高级技巧
  • 掌握正则表达式的新特性
  • 了解ES2015-ES2021的重要功能

🔄 Promise与异步编程

1. Promise基础使用

// 基础Promise示例
let pro = new Promise(function(resolve, reject) {
    setTimeout(() => {
        resolve('操作成功');
    }, 1000);
});

pro.then((res) => {
    console.log("成功:", res);
}).catch((err) => {
    console.log("失败:", err);
});

// Promise封装Ajax请求
function ajax(url) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.open("get", url, true);
        xhr.send();

        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve(xhr.responseText);
                } else {
                    reject(new Error(`请求失败: ${xhr.status}`));
                }
            }
        };
    });
}

// 使用Promise的Ajax
ajax("https://api.example.com/data")
    .then(res => {
        console.log("数据获取成功:", res);
        return JSON.parse(res);
    })
    .then(data => {
        console.log("解析后的数据:", data);
    })
    .catch(err => {
        console.error("请求错误:", err);
    });

// Promise.finally示例
function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("data-1111");
        }, 1000);
    });
}

fetchData()
    .then(data => {
        console.log(data);
    })
    .catch(err => {
        console.error("错误:", err);
    })
    .finally(() => {
        console.log("操作完成,无论成功失败");
        // 隐藏加载动画等
    });

2. Promise.allSettled

// ES11新增的Promise.allSettled
// 返回一个在所有给定的promise都已经fulfilled或rejected的promise
function fetchMultipleData() {
    const requests = [
        fetch('/api/users'),
        fetch('/api/products'),
        fetch('/api/orders')
    ];

    return Promise.allSettled(requests);
}

fetchMultipleData().then(results => {
    results.forEach((result, index) => {
        if (result.status === 'fulfilled') {
            console.log(`请求${index + 1}成功:`, result.value);
        } else {
            console.error(`请求${index + 1}失败:`, result.reason);
        }
    });
});

// 对比Promise.all和Promise.allSettled
const promises = [
    Promise.resolve('成功1'),
    Promise.reject('失败1'),
    Promise.resolve('成功2')
];

// Promise.all - 有一个失败就立即失败
Promise.all(promises)
    .catch(error => {
        console.log('Promise.all 失败:', error); // 立即失败
    });

// Promise.allSettled - 等待所有完成
Promise.allSettled(promises)
    .then(results => {
        console.log('Promise.allSettled 结果:', results);
        // [
        //   { status: 'fulfilled', value: '成功1' },
        //   { status: 'rejected', reason: '失败1' },
        //   { status: 'fulfilled', value: '成功2' }
        // ]
    });

⚡ 生成器函数与迭代器

1. 基础生成器函数

// 生成器函数基础语法
function* gen() {
    let res = yield "aaa";
    console.log("第一个请求的结果" + res);
    let res2 = yield "bbb";
    console.log("第二个请求的结果" + res2);
    return "生成器结束";
}

// 手动执行生成器
let g = gen();
console.log(g.next()); // { value: "aaa", done: false }
console.log(g.next("第一个yield的结果")); // { value: "bbb", done: false }
console.log(g.next("第二个yield的结果")); // { value: "生成器结束", done: true }

// 自动执行生成器的函数
function AutoRun(gen) {
    let g = gen();

    function next(data) {
        let res = g.next(data);
        if (res.done) return res.value;

        // 如果返回的是Promise,等待完成后继续
        if (res.value && typeof res.value.then === 'function') {
            res.value.then(function(data) {
                next(data);
            }).catch(function(error) {
                g.throw(error);
            });
        } else {
            next(res.value);
        }
    }

    next();
}

// 使用AutoRun执行生成器
AutoRun(function*() {
    try {
        let data1 = yield ajax('/api/data1');
        console.log('数据1:', data1);

        let data2 = yield ajax('/api/data2');
        console.log('数据2:', data2);

        let data3 = yield ajax('/api/data3');
        console.log('数据3:', data3);
    } catch (error) {
        console.error('请求失败:', error);
    }
});

2. 异步迭代器

// ES10新增的异步迭代器
async function* asyncGen() {
    yield new Promise(resolve => setTimeout(() => resolve('延迟1秒'), 1000));
    yield new Promise(resolve => setTimeout(() => resolve('延迟2秒'), 2000));
    yield new Promise(resolve => setTimeout(() => resolve('延迟3秒'), 3000));
}

// 使用for await...of遍历异步迭代器
async function testAsyncIteration() {
    let g = asyncGen();

    console.log("开始异步遍历:", Date.now());

    for await (let item of g) {
        console.log("当前时间:", Date.now());
        console.log("获取到值:", item);
    }

    console.log("遍历结束:", Date.now());
}

testAsyncIteration();

// 同步迭代器示例
let arr = [1, 2, 3];
let i = arr[Symbol.iterator]();

console.log(i.next()); // { value: 1, done: false }
console.log(i.next()); // { value: 2, done: false }
console.log(i.next()); // { value: 3, done: false }
console.log(i.next()); // { value: undefined, done: true }

// for...of循环本质上就是使用迭代器
for (let item of arr) {
    console.log(item); // 1, 2, 3
}

🎯 对象操作高级技巧

1. 对象方法和属性

// 对象扩展方法
let obj = {
    name: "tzd",
    age: 100
};

console.log(Object.keys(obj));    // ["name", "age"]
console.log(Object.values(obj));  // ["tzd", 100]
console.log(Object.entries(obj)); // [["name", "tzd"], ["age", 100]]

// 对象快速转化为Map
let m = new Map([["a", 1], ["b", 2]]);
let M = new Map(Object.entries(obj));
console.log(M); // Map(2) { 'name' => 'tzd', 'age' => 100 }

// 获取对象所有属性的描述符
console.log(Object.getOwnPropertyDescriptors(obj));

2. 属性描述符和深拷贝

// 对象属性描述符示例
let obj1 = {
    name: "tzd",
    location: {
        province: "辽宁",
        city: "大连"
    },
    get city() {
        return this.location.city;
    },
    set city(value) {
        this.location.city = value;
    },
    get name() {
        console.log("name getter被调用");
        return this._name;
    },
    set name(value) {
        console.log("name setter被调用");
        this._name = value;
    }
};

// Object.assign浅拷贝
let shallowCopy = Object.assign({}, obj1);
console.log("浅拷贝:", shallowCopy);

// 使用属性描述符进行深拷贝
let obj2 = {};
Object.defineProperties(obj2, Object.getOwnPropertyDescriptors(obj1));
console.log("深拷贝:", obj2);

// 验证深拷贝
console.log("原对象location =<mark> 拷贝对象location:",
            obj1.location </mark>= obj2.location); // false

3. 对象的剩余参数和扩展运算符

// ES9新增:对象的剩余参数rest与扩展运算符spread
let obj3 = {
    name: "tzd",
    age: 13,
    location: "北京",
    job: "developer"
};

// 对象解构和剩余参数
let { name, ...other } = obj3;
console.log(name);    // "tzd"
console.log(other);   // { age: 13, location: "北京", job: "developer" }

// 扩展运算符合并对象
let defaults = {
    method: "GET",
    headers: {
        "Content-Type": "application/json"
    },
    timeout: 5000
};

let userOptions = {
    url: "/api/data",
    method: "POST",
    timeout: 10000
};

let finalOptions = {
    ...defaults,
    ...userOptions
};

console.log(finalOptions);
// {
//   url: "/api/data",
//   method: "POST",
//   headers: { "Content-Type": "application/json" },
//   timeout: 10000
// }

// Ajax函数使用默认参数和扩展运算符
function ajax(options) {
    let defaultOptions = {
        method: "GET",
        async: true,
        timeout: 5000,
        headers: {}
    };

    // 合并默认参数和用户参数
    options = { ...defaultOptions, ...options };
    console.log("最终配置:", options);
}

ajax({
    url: "/api",
    method: "POST",
    timeout: 3000
});

🔤 字符串处理新特性

1. 字符串填充方法

// ES8新增:padStart()和padEnd()
// 方法可以使得字符串达到固定长度,有两个参数:字符串目标长度和填充内容

let str = "5";

// padStart:在字符串开头填充
console.log(str.padStart(2, "0"));    // "05"
console.log(str.padStart(4, "x"));    // "xxx5"
console.log("abc".padStart(6, "0"));  // "000abc"

// padEnd:在字符串末尾填充
console.log(str.padEnd(2, "0"));      // "50"
console.log(str.padEnd(4, "x"));      // "5xxx"
console.log("abc".padEnd(6, "0"));    // "abc000"

// 实际应用:生成月份列表
let monthList = [];
for (let i = 1; i <= 12; i++) {
    // 转化为字符串并填充到2位
    monthList.push(String(i).padStart(2, "0"));
}
console.log(monthList); // ["01", "02", "03", ..., "12"]

// 实际应用:格式化价格
function formatPrice(price) {
    return ${price.toFixed(2).padStart(8, '0')}`;
}

console.log(formatPrice(9.5));    // "¥00009.50"
console.log(formatPrice(123.45)); // "¥00123.45"

2. 字符串清理方法

// ES10新增:trimStart()和trimEnd()
let messyString = "   Hello World!   ";

console.log("'" + messyString.trimStart() + "'");  // 'Hello World!   '
console.log("'" + messyString.trimEnd() + "'");    // '   Hello World!'
console.log("'" + messyString.trim() + "'");       // 'Hello World!'

// 等同的方法名
console.log("'" + messyString.trimLeft() + "'");   // 'Hello World!   '
console.log("'" + messyString.trimRight() + "'");  // '   Hello World!'

// 实际应用:清理用户输入
function cleanInput(input) {
    return input.trim().replace(/\s+/g, ' ');
}

console.log(cleanInput("  Hello    there!  ")); // "Hello there!"

🎨 数组操作新特性

1. flat和flatMap

// ES10新增:数组的flat()和flatMap()

// flat()方法:将嵌套数组"拉平"
let nestedArray = [1, [2, 3], [4, [5, 6]]];
console.log(nestedArray.flat());        // [1, 2, 3, [4, [5, 6]]]
console.log(nestedArray.flat(2));      // [1, 2, 3, 4, [5, 6]]
console.log(nestedArray.flat(Infinity)); // [1, 2, 3, 4, 5, 6]

// flatMap()方法:先映射再拉平
let sentences = [
    "Hello world",
    "How are you",
    "Fine thank you"
];

// 使用map + flat的组合
let words1 = sentences.map(sentence => sentence.split(' ')).flat();
console.log(words1); // ["Hello", "world", "How", "are", "you", "Fine", "thank", "you"]

// 使用flatMap一步完成
let words2 = sentences.flatMap(sentence => sentence.split(' '));
console.log(words2); // ["Hello", "world", "How", "are", "you", "Fine", "thank", "you"]

// 实际应用:处理嵌套的数据结构
let categories = [
    {
        name: "电子产品",
        items: ["手机", "电脑", "平板"]
    },
    {
        name: "服装",
        items: ["T恤", "裤子", "鞋子"]
    }
];

// 使用flatMap获取所有商品
let allItems = categories.flatMap(category =>
    category.items.map(item => ({
        category: category.name,
        item: item
    }))
);

console.log(allItems);
// [
//   { category: "电子产品", item: "手机" },
//   { category: "电子产品", item: "电脑" },
//   { category: "电子产品", item: "平板" },
//   { category: "服装", item: "T恤" },
//   { category: "服装", item: "裤子" },
//   { category: "服装", item: "鞋子" }
// ]

🔍 正则表达式增强

1. 具名组匹配

// ES9新增:正则表达式具名组匹配
let str = "今天是2022-10-22";
let reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;

let match = reg.exec(str);
console.log(match);

// 通过具名组访问匹配结果
console.log(match.groups.year);   // "2022"
console.log(match.groups.month);  // "10"
console.log(match.groups.day);    // "22"

// 使用具名组进行替换
let newStr = str.replace(reg, '$<day>/$<month>/$<year>');
console.log(newStr); // "22/10/2022"

// 实际应用:解析URL参数
let url = "https://example.com/search?q=javascript&lang=en&page=1";
let urlRegex = /(?<domain>[^/]+)\/search\?(?<params>.+)/;
let urlMatch = urlRegex.exec(url);

if (urlMatch) {
    console.log("域名:", urlMatch.groups.domain); // "example.com"
    console.log("参数:", urlMatch.groups.params); // "q=javascript&lang=en&page=1"
}

2. 正则表达式中的反向断言

// ES9新增:正向和反向断言

// 正向先行断言:(?=pattern)
let str1 = "JavaScript123Python456Java";
let positiveLookahead = /\w+(?=\d+)/g;
console.log(str1.match(positiveLookahead)); // ["JavaScript", "Python", "Java"]

// 正向否定断言:(?!pattern)
let positiveNegation = /\w+(?!\d+)/g;
console.log(str1.match(positiveNegation)); // ["ava", "ython", "ava"]

// 反向先行断言:(?<=pattern)
let str2 = "100$200$300$";
let negativeLookbehind = /(?<=\$)\d+/g;
console.log(str2.match(negativeLookbehind)); // ["100", "200", "300"]

// 反向否定断言:(?<!pattern)
let str3 = "abc100def200ghi";
let negativeNegation = /(?<!\d)\d+/g;
console.log(str3.match(negativeNegation)); // ["100", "200"]

3. dotAll模式

// ES2018新增:dotAll模式,使.可以匹配换行符

// 传统模式下,.不匹配换行符
let text1 = `Hello
World`;
console.log(/Hello.World/.test(text1)); // false

// 使用dotAll模式
console.log(/Hello.World/s.test(text1)); // true

// 实际应用:匹配多行代码块
let code = `
function test() {
    console.log('Hello');
    return true;
}
`;

// 使用dotAll模式匹配整个函数
let functionRegex = /function[\s\S]*?}/;  // 传统方式
let functionRegexDotAll = /function.*?/s;  // dotAll方式

console.log(functionRegex.exec(code));
console.log(functionRegexDotAll.exec(code));

🛠️ 对象转换新方法

1. Object.fromEntries

// ES10新增:Object.fromEntries(),允许将数组转化为对象

// 用处1:Map转对象
let m = new Map();
m.set("name", "tiecui");
m.set("age", 13);
console.log(Object.fromEntries(m));
// { name: "tiecui", age: 13 }

// 用处2:URLSearchParams转对象
let str = "name=xiaoming&age=18&city=beijing";
let searchParams = new URLSearchParams(str);
console.log(Object.fromEntries(searchParams));
// { name: "xiaoming", age: "18", city: "beijing" }

// 用处3:对象转换处理
let obj4 = {
    "a": ["a1", "a2", "a3"],
    "b": ["b1", "b2"],
    "c": ["c1"]
};

// 统计每个键对应的数组长度
let entries = Object.entries(obj4).map(([key, value]) => [key, value.length]);
let result = Object.fromEntries(entries);
console.log(result); // { a: 3, b: 2, c: 1 }

// 实际应用:查询字符串处理
function parseQueryString(queryString) {
    if (queryString.startsWith('?')) {
        queryString = queryString.slice(1);
    }

    const params = new URLSearchParams(queryString);
    const result = Object.fromEntries(params);

    // 尝试转换数字和布尔值
    Object.keys(result).forEach(key => {
        const value = result[key];
        if (value === 'true') result[key] = true;
        else if (value === 'false') result[key] = false;
        else if (!isNaN(value) && value !== '') result[key] = Number(value);
    });

    return result;
}

console.log(parseQueryString('?name=张三&age=25&active=true'));
// { name: "张三", age: 25, active: true }

📊 Symbol新特性

1. Symbol.description

// ES10新增:Symbol.description属性
let s1 = Symbol("name");
console.log(s1.description); // "name"

let s2 = Symbol();
console.log(s2.description); // undefined

let s3 = Symbol("这是一个很长的描述");
console.log(s3.description); // "这是一个很长的描述"

// 实际应用:调试和日志记录
function createDebugSymbol(type) {
    const symbol = Symbol(type);
    console.log(`创建Symbol: ${symbol.description}, ${symbol.toString()}`);
    return symbol;
}

const USER_SYMBOL = createDebugSymbol('User');
const PRODUCT_SYMBOL = createDebugSymbol('Product');

🌐 模块化新特性

1. import.meta

// ES11新增:import.meta
// 返回一个对象,包含当前模块的信息

// 在模块文件中使用
console.log(import.meta.url); // 返回当前模块的文件URL

// 获取当前脚本的目录
const currentDir = new URL('.', import.meta.url).href;
console.log('当前目录:', currentDir);

// 动态获取资源路径
function getImagePath(imageName) {
    return new URL(`./images/${imageName}`, import.meta.url).href;
}

console.log('图片路径:', getImagePath('logo.png'));

// 条件加载模块
async function loadModule(moduleName) {
    if (import.meta.env === 'development') {
        const module = await import(`./modules/${moduleName}.dev.js`);
        return module;
    } else {
        const module = await import(`./modules/${moduleName}.prod.js`);
        return module;
    }
}

2. 导出和重新导出

// ES11新增:无损地重新导出模块

// 原来的方式(会丢失模块信息)
// export { default as User } from './User';

// 新的方式(保留模块信息)
export * as obj from "./module";

// 实际应用:创建统一的导出入口
// 在一个index.js文件中重新导出所有模块
export * as utils from './utils.js';
export * as components from './components.js';
export * as services from './services.js';

// 也可以选择性重新导出
export { User, Product } from './models.js';
export { default as Database } from './database.js';

// 使用时
import { utils, components, User } from './index.js';

🔄 观察者模式实现

1. 完整的观察者模式

// 观察者模式实现
class Subject {
    constructor() {
        this.observers = [];
    }

    // 添加观察者
    add(observer) {
        this.observers.push(observer);
    }

    // 通知所有观察者
    notify() {
        this.observers.forEach((item) => {
            console.log("通知观察者:", item);
            item.update();
        });
    }

    // 移除观察者
    remove(observer) {
        this.observers = this.observers.filter(item => item !== observer);
    }
}

class Observer {
    constructor(name) {
        this.name = name;
    }

    update() {
        console.log("观察者被通知:", this.name);
    }
}

// 使用观察者模式
const subject = new Subject();
const observer1 = new Observer("tzd");
const observer2 = new Observer("小明");

subject.add(observer1);
subject.add(observer2);

// 通知所有观察者
subject.notify();

// 移除一个观察者
subject.remove(observer1);
console.log("移除tzd后再次通知:");
subject.notify();

🎯 综合应用示例

1. 异步任务队列管理器

// 基于Promise和生成器的任务队列管理器
class AsyncTaskQueue {
    constructor() {
        this.tasks = [];
        this.isRunning = false;
    }

    // 添加任务
    addTask(task) {
        this.tasks.push({
            id: Date.now() + Math.random(),
            task,
            status: 'pending'
        });
    }

    // 执行所有任务
    async runAll() {
        if (this.isRunning) {
            throw new Error('任务队列正在运行中');
        }

        this.isRunning = true;
        const results = [];

        for (const taskItem of this.tasks) {
            try {
                taskItem.status = 'running';
                const result = await taskItem.task();
                taskItem.status = 'completed';
                results.push({ success: true, result, task: taskItem });
            } catch (error) {
                taskItem.status = 'failed';
                results.push({ success: false, error, task: taskItem });
            }
        }

        this.isRunning = false;
        return results;
    }

    // 使用生成器逐个执行任务
    async *runWithGenerator() {
        for (const taskItem of this.tasks) {
            try {
                taskItem.status = 'running';
                const result = yield taskItem.task();
                taskItem.status = 'completed';
                yield { success: true, result, task: taskItem };
            } catch (error) {
                taskItem.status = 'failed';
                yield { success: false, error, task: taskItem };
            }
        }
    }

    // 获取任务状态
    getTaskStatus() {
        return this.tasks.map(task => ({
            id: task.id,
            status: task.status
        }));
    }

    // 清空队列
    clear() {
        if (!this.isRunning) {
            this.tasks = [];
        }
    }
}

// 使用任务队列管理器
async function demonstrateTaskQueue() {
    const queue = new AsyncTaskQueue();

    // 添加任务
    queue.addTask(() => {
        return new Promise(resolve => {
            setTimeout(() => resolve('任务1完成'), 1000);
        });
    });

    queue.addTask(() => {
        return new Promise(resolve => {
            setTimeout(() => resolve('任务2完成'), 800);
        });
    });

    queue.addTask(() => {
        return new Promise((resolve, reject) => {
            setTimeout(() => reject(new Error('任务3失败')), 600);
        });
    });

    // 执行所有任务
    const results = await queue.runAll();
    console.log('执行结果:', results);

    // 查看任务状态
    console.log('任务状态:', queue.getTaskStatus());
}

demonstrateTaskQueue();

2. 响应式数据管理

// 使用Proxy和观察者模式实现响应式数据管理
class ReactiveData {
    constructor(initialData = {}) {
        this.data = this.createReactiveObject(initialData);
        this.subscribers = new Map();
    }

    // 创建响应式对象
    createReactiveObject(obj) {
        return new Proxy(obj, {
            get: (target, key) => {
                const value = target[key];

                // 如果是对象,递归创建响应式
                if (value && typeof value === 'object' && !value._isReactive) {
                    target[key] = this.createReactiveObject(value);
                }

                return target[key];
            },

            set: (target, key, value) => {
                const oldValue = target[key];
                target[key] = value;

                // 通知订阅者
                this.notifySubscribers(key, value, oldValue);

                return true;
            }
        });
    }

    // 订阅数据变化
    subscribe(key, callback) {
        if (!this.subscribers.has(key)) {
            this.subscribers.set(key, new Set());
        }
        this.subscribers.get(key).add(callback);
    }

    // 取消订阅
    unsubscribe(key, callback) {
        if (this.subscribers.has(key)) {
            this.subscribers.get(key).delete(callback);
        }
    }

    // 通知订阅者
    notifySubscribers(key, newValue, oldValue) {
        if (this.subscribers.has(key)) {
            this.subscribers.get(key).forEach(callback => {
                callback(newValue, oldValue, key);
            });
        }

        // 通知通配符订阅者
        if (this.subscribers.has('*')) {
            this.subscribers.get('*').forEach(callback => {
                callback(newValue, oldValue, key);
            });
        }
    }

    // 计算属性
    computed(fn) {
        const dependencies = new Set();
        const proxy = new Proxy({}, {
            get(target, key) {
                dependencies.add(key);
                return this.data[key];
            }.bind(this)
        });

        const result = fn(proxy);

        // 自动订阅依赖的数据
        dependencies.forEach(key => {
            this.subscribe(key, () => {
                // 依赖变化时重新计算
                const newResult = fn(proxy);
                console.log(`计算属性更新: ${newResult}`);
            });
        });

        return result;
    }
}

// 使用响应式数据管理
const reactive = new ReactiveData({
    user: {
        name: '张三',
        age: 25
    },
    settings: {
        theme: 'light',
        language: 'zh-CN'
    }
});

// 订阅数据变化
reactive.subscribe('user.name', (newValue, oldValue) => {
    console.log(`用户名从 ${oldValue} 变为 ${newValue}`);
});

reactive.subscribe('settings.theme', (newValue, oldValue) => {
    console.log(`主题从 ${oldValue} 变为 ${newValue}`);
    // 可以在这里更新UI
});

// 计算属性:显示用户信息
const userInfo = reactive.computed((data) => {
    return `${data.user.name} (${data.user.age}岁) - ${data.settings.theme}主题`;
});

console.log('初始用户信息:', userInfo);

// 修改数据,会自动触发通知
reactive.data.user.name = '李四';
reactive.data.settings.theme = 'dark';

🎯 小结

  • 掌握了Promise的各种用法和异步编程模式
  • 学会了生成器函数和迭代器的实际应用
  • 理解了对象操作的高级技巧和深拷贝方法
  • 掌握了字符串和数组的新特性
  • 学会了正则表达式的增强功能
  • 了解了模块化编程的新特性
  • 实践了观察者模式和响应式编程

这些ES6+的新特性让JavaScript代码更加简洁、高效和易维护,是现代Web开发的重要基础。


下一步学习TypeScript进阶类型