首页 > 解决方案 > 理解 JS 中静态属性的作用域

问题描述

首先给出我的情况的一些背景......我有一个包含一些模型等的库,用于我的应用程序,然后是一个添加一些 UI 的电子应用程序,电子应用程序还加载一个自定义 JS 文件,它允许添加额外的逻辑。

在我的 Workspace 模型上,我使用单例模式(我知道,我知道)来存储工作区的一个实例。当应用程序启动时,它正在加载工作区,然后调用 document.head.appendChild 来加载我的自定义脚本。

在自定义脚本中,我需要相同的模型库来获取我的 Workspace 类。但是,Workspace.instance 会返回 null,尽管它之前肯定不是 null。

相比之下,如果在加载脚本之前我执行 global.workspace = workspace,那么脚本可以很好地访问实例。那么谁能告诉我发生了什么事?据我了解,静态属性和方法本质上具有全局范围,尽管这似乎表明并非如此。

在我的图书馆:

export default class Workspace {
    static instance: Workspace = null;

    startupData: any;

    constructor(startupData: any) {
        this.startupData = startupData;
        Workspace.instance = this;
    }
}

在我的应用程序中:

import Workspace from 'mymodels';

const workspace = new Workspace(startupData);
global.workspace = workspace;
console.log(Workspace.instance);    //Returns my workspace instance

const script = document.createElement('script');
script.onload = showUI;
script.src = 'file:///' + workspace.startupData.logic;
document.head.appendChild(script);

在我的自定义脚本中:

const mymodels = require('mymodels');

console.log(mymodels.Workspace.instance);    //Returns null
console.log(global.workspace);               //Returns my workspace instance

标签: javascripttypescriptelectrondynamic-loading

解决方案


当应用程序启动时,它正在加载工作区,然后调用 document.head.appendChild 来加载我的自定义脚本。

如果使用本机模块,那会很好,但我怀疑您正在使用某种捆绑器(Webpack 等)。我怀疑您最终会加载两个不同的模块副本:一个作为初始应用程序包的一部分,然后另一个由浏览器的模块处理本地加载。由于这些是单独的模块(据浏览器所知),因此您最终会得到其属性的两个副本Workspace,因此也有两个副本。instance

根据捆绑器的不同,通常有一种方法可以告诉它处理这个问题,但细节因捆绑器而异。


但是,回答范围问题:像您这样的公共静态属性instance可以在任何可以访问其包含类(构造函数)的地方访问,因为它是该函数对象的属性。

如果“范围”是指“生命周期”,则静态属性是在创建类(构造函数)时创建的,并在释放类(构造函数)时释放(或通过 删除属性delete)。


顺便说一句,您的Workspace班级不是单身人士。每次执行时new Workspace,您都会创建一个新实例(并覆盖 中的先前值Workspace.instance。要使其成为单例,您需要在构造函数中添加一个检查:

constructor(startupData: any) {
    if (Workspace.instance) {
        return Workspace.instance; // Return the singleton
    }
    this.startupData = startupData;
    Workspace.instance = this;
}

或者更好的是,通过简单地直接导出一个对象,让你的单例更加地道(从 JavaScript 的角度来看):

export default const workspace = {
    // ...properties and methods...
};

或者如果您需要class私有字段等功能:

export default const workspace = new class Workspace {
    // ...constructor (if desired), properties, and methods...
}();

推荐阅读