JS class 中的 static 使用及问题解析

562 阅读1分钟

JS class 中的 static 使用及问题解析

最近业务代码中在尝试使用类的方法在做代码复用,踩到一个关于 static 的小坑。

描述

demo 代码如下:

class Test {
    static defaultOptions = {
        arr: []
    }
    constructor(options) {
        this.data = Object.assign(Test.defaultOptions, options)
    }
    add(num) {
        this.data.arr.push(num)
    }
    getArr() {
        return this.data.arr
    }
}

const test1 = new Test()
test1.add(1)

const test2 = new Test()
console.log(JSON.stringify(test2.getArr()))

这段代码按照正常的逻辑来讲,重新实例化了对象,那么应该是输出一个空数组,但是在实际的运行过程中会发现,其实输出的是 [1] ,那这里问题出在哪里呢?

分析

static 关键字定义了静态方法或字段,或静态初始化块。静态属性不能在类的实例上直接访问。相反,它们是在类本身上被访问的。

这是 MDN 官网对 class 上 static 关键字的定义,我们在使用的过程用 static 关键字声明变量来存储默认值是没问题的,但这个问题出在我合并默认值 defaultOptions 和传参 options 的过程中,因为我在初始化示例的过程中没有传值,所以本质上 data.arr 指向的就是 defaultOptions.arr 而这个值是存在于类本身的,并不是在每次初始化示例的时候重置,所有就会导致上述的问题。

解决

既然找到了问题的所在,那么解决问题就很快了,可以用使用深拷贝直接在 Object.assign 的时候使用拷贝值,也可以使用工厂函数来生成默认值,确保每一份实例都是一个独立的默认值。

class Test {
    static get defaultOptions() {
        return {
            arr: []
        };
    }
    
    constructor(options = {}) {
        this.data = Object.assign({}, Test.defaultOptions, options);
    }
    
    add(num) {
        this.data.arr.push(num);
    }
    
    getData() {
        return this.data;
    }
}

const test1 = new Test();
test1.add(1);

const test2 = new Test();
console.log(JSON.stringify(test2.getData())); // 输出: {"arr":[]}