Skip to content

扩展内置类

继承内置类

1. 扩展 Array

javascript
class MyArray extends Array {
    // 添加新方法
    first() {
        return this[0];
    }

    last() {
        return this[this.length - 1];
    }

    // 添加新的 static 方法
    static fromRange(start, end) {
        return new MyArray(...Array(end - start + 1).keys()).map(x => x + start);
    }
}

const arr = new MyArray(1, 2, 3, 4, 5);
console.log(arr.first()); // 1
console.log(arr.last()); // 5
console.log(arr instanceof Array); // true
console.log(arr instanceof MyArray); // true

// 使用静态方法
const range = MyArray.fromRange(5, 10);
console.log(range); // MyArray(6) [5, 6, 7, 8, 9, 10]

2. 扩展 Map

javascript
class ExtendedMap extends Map {
    // 返回键的数组
    keys() {
        return [...super.keys()];
    }

    // 返回值的数组
    values() {
        return [...super.values()];
    }

    // 添加多个键值对
    setMultiple(entries) {
        for (const [key, value] of entries) {
            this.set(key, value);
        }
        return this;
    }

    // 获取第一个键值对
    firstEntry() {
        const iterator = super.entries();
        return iterator.next().value;
    }
}

const map = new ExtendedMap();
map.set('a', 1).set('b', 2);
console.log(map.keys()); // ['a', 'b']
console.log(map.firstEntry()); // ['a', 1]

map.setMultiple([['c', 3], ['d', 4]]);
console.log(map.size); // 4

3. 扩展 Promise

javascript
class TimedPromise extends Promise {
    // 记录执行时间
    static async withTimer(executor) {
        const start = Date.now();
        
        try {
            const result = await new Promise(executor);
            const end = Date.now();
            return {
                result,
                time: end - start
            };
        } catch (error) {
            const end = Date.now();
            throw {
                error,
                time: end - start
            };
        }
    }

    // 创建一个超时 Promise
    static timeout(ms, value) {
        return new TimedPromise((resolve) => {
            setTimeout(() => resolve(value), ms);
        });
    }
}

// 使用扩展的 Promise
async function test() {
    const result = await TimedPromise.withTimer(async (resolve) => {
        await new Promise(r => setTimeout(r, 1000));
        resolve("完成");
    });
    
    console.log(`操作耗时: ${result.time}ms, 结果: ${result.result}`);
    
    const timeoutResult = await TimedPromise.timeout(500, "超时完成");
    console.log(timeoutResult);
}

test();

继承内置类的特殊行为

1. 返回值重写

javascript
class MyArray extends Array {
    // 重写 Array 方法
    static get [Symbol.species]() {
        return Array;
    }
    
    // 自定义 filter 方法
    customFilter(callback) {
        console.log('使用自定义 filter');
        return this.filter(callback);
    }
}

const myArray = new MyArray(1, 2, 3, 4, 5);
const filtered = myArray.filter(x => x > 2);

console.log(filtered instanceof Array); // true
console.log(filtered instanceof MyArray); // false (因为我们重写了 Symbol.species)

const customFiltered = myArray.customFilter(x => x > 2);
console.log(customFiltered instanceof Array); // true
console.log(customFiltered instanceof MyArray); // false

2. 内置方法调用子类构造函数

javascript
class MyArray extends Array {
    constructor(...args) {
        console.log('MyArray constructor called');
        super(...args);
    }
}

// 直接调用构造函数
const arr1 = new MyArray(1, 2, 3);
console.log(arr1); // MyArray(3) [1, 2, 3]

// map 方法创建新实例时也会调用构造函数
const arr2 = arr1.map(x => x * 2);
console.log(arr2); // MyArray(3) [2, 4, 6]

// concat 方法也会调用构造函数
const arr3 = arr1.concat([4, 5]);
console.log(arr3); // MyArray(5) [1, 2, 3, 4, 5]

最佳实践与注意事项

1. 性能考虑

javascript
// 不要盲目扩展内置类
class HeavyArray extends Array {
    constructor(...args) {
        super(...args);
        this.metadata = {
            createdAt: new Date(),
            id: Math.random().toString(36)
        };
    }
    
    // 每次调用都会生成大量数据的方法可能导致性能问题
    analyze() {
        return {
            sum: this.reduce((a, b) => a + b, 0),
            average: this.reduce((a, b) => a + b, 0) / this.length,
            min: Math.min(...this),
            max: Math.max(...this),
            // ... 其他计算
        };
    }
}

// 对于大型数组,这种扩展可能导致性能问题

2. 最佳实践

  1. 谨慎扩展内置类,考虑组合而不是继承
  2. 了解 Symbol.species 的作用
  3. 避免覆盖原始类的核心功能
  4. 为扩展类添加有意义的方法和属性
  5. 考虑扩展对性能的影响
  6. 测试所有继承的方法
  7. 记录继承类的行为差异
  8. 考虑使用工具函数代替扩展类