类属性懒加载

74 阅读1分钟

原始博文

class MyClass {
    constructor() {
        this.data = someExpensiveComputation();
    }
}
function someExpensiveComputation(){
    console.log("🚀");
    const result = Array(10000000).fill("test").map((item,index)=>item+index)
    return result[0];
}

在对象的constructor中创建属性时,如果属性的赋值需要耗费大量算力,每次实例化均需要进行计算。如上,当data属性不需要在constructor中被使用时,因为计算了页不见得被使用,那么每次实例化时进行计算就显得不那么高效。我们可以在调用date的时候再进行赋值操作,使耗费大量计算的操作发生在调用时。

一种简单的方法

class MyClass {
    get data() {
        return someExpensiveComputation();
    }
}

使用getter来获取类的属性,只有在调用new MyClass().data时才会执行someExpensiveComputation。这种方式中明显的缺陷是每次通过实例调用data均会执行someExpensiveComputation函数。当data需要被频繁调用时,这种懒加载方式得不偿失。

我们使用上面两种方式的混合模式,getter在初次调用时将实例的data赋值(数据缓存在实例this中,覆盖setter)。实例化时,不会执行someExpensiveComputation,首次通过实例调用instanceName.data时,会执行someExpensiveComputation(🚀被打印出来,同时instanceName.data被赋值,someExpensiveComputation的执行结果被缓存),

class MyClass {
    constructor() {
        this.name = "nameValue"
    }
    get data() {
        const actualData = someExpensiveComputation();

        Object.defineProperty(this, "data", { // 覆盖getter
            value: actualData,
            writable: false,
            configurable: false,
            enumerable: false
        });

        return actualData;
    }
}
const instance1 = new MyClass();
(调用getter)
instance1.data => 🚀 "test0"
(调用实例的data缓存)
instance1.data => "test0"

但是,这种混合模式改变了属性的可枚举型,使得不可枚举的属性data变得可枚举。

const instance2 = new MyClass();
Object.keys(instance2) => ["name"]
instance2.hasOwnProperty("name") => true
instance2.hasOwnProperty("data") => false
// 调用getter
instance2.data => 🚀 "test0"
Object.keys(instance2) => ["name"]
instance2.hasOwnProperty("name") => true
instance2.hasOwnProperty("data") => true //发生改变