创建多个 javascript 模块的实例
Creating an instance of multiple javascript modules
这与 javascript 代码有关。我的应用程序有许多子应用程序,有时在一个页面上使用多次。一个示例是允许用户搜索某些数据并显示搜索结果的应用程序。该应用程序可以在页面的多个位置使用,以搜索不同类型的数据。
每个子应用程序通常包含许多 javascript 个模块,每个模块都在一个单独的文件中。我经历过许多不同的模块模式,试图创建多个 modules/files 的单独实例,但没有成功。网上有很多关于如何创建对象的多个实例、使用工厂模式等的建议,但我无法使它在我的应用程序中使用名称 space 结构和模块模式。请参阅下面的示例。
问题是如何创建 SubAppA 的多个独立实例,包括其所有子模块。
(New file)
var MainApp = MainApp || {};
MainApp.SubAppA = MainApp.SubAppA || {};
MainApp.SubAppA.Config = (function () {
function A () { ... };
function B () { ... };
return {
A : A,
B : B
}
})();
(New file)
var MainApp = MainApp || {};
MainApp.SubAppA = MainApp.SubAppA || {};
MainApp.SubAppA.GetData = (function () {
function A () { ... };
function B () { ... };
return {
A : A,
B : B
}
})();
(New file)
var MainApp = MainApp || {};
MainApp.SubAppA = MainApp.SubAppA || {};
MainApp.SubAppA.DisplayData = (function () {
etc.....
非常感谢
--- MikeM 提出的解决方案后的附加信息 -----
谢谢 MikeM,您的回答让我有了更好的理解,但是当尝试使用我现有的名称 space 结构来实现它时,我无法让模块相互通信。我尝试了以下方法:
//Solution $S.AS - New file
var $S = $S || {};
$S.AS = $S.AS || {};
$S.AS.DataStore = function () {
var _SomeVar = "Default";
function SetVar (Data) {
_SomeVar = Data;
};
function GetVar () {
return _SomeVar;
};
return {
SetVar : SetVar,
GetVar : GetVar
}
};
//Solution $S.AS - New file
var $S = $S || {};
$S.AS = $S.AS || {};
$S.AS.ManageData = function () {
function StoreData (Data) {
console.log($S.AS.DataStore); //outputs f ()
//Does now work since DataStore is now a function
//$S.AS.DataStore.SetVar(Data);
$S.AS.DataStore().SetVar(Data);
};
function DisplayData () {
//Does now work since DataStore is now a function
//var SomeVar = $S.AS.DataStore.GetVar();
//Does not work, still outputs "Default"
var SomeVar = $S.AS.DataStore().GetVar();
console.log(SomeVar);
};
return {
StoreData : StoreData,
DisplayData : DisplayData
}
};
//Solution $S.AS - New file - The contructor function for AS
var MainApp = MainApp || {};
MainApp.S = MainApp.S || {};
MainApp.S.AS = MainApp.S.AS || {};
MainApp.S.AS = function () {
this.DataStore = $S.AS.DataStore();
this.ManageData = $S.AS.ManageData();
//additional modules
};
//Main/Page specific code - creating the different instances
MainApp.S.AS_1 = new MainApp.S.AS();
MainApp.S.AS_2 = new MainApp.S.AS();
//Attemps to store and retrieve data
//Stores AAA in the DataStore module
MainApp.S.AS_1.ManageData.StoreData("AAA");
//Stores BBB in the DataStore module
MainApp.S.AS_2.ManageData.StoreData("BBB");
//Not working, ouputs: "Default" (Desired result is "AAA")
MainApp.S.AS_1.ManageData.DisplayData();
//Not working, ouputs: "Default" (Desired result is "BBB");
MainApp.S.AS_2.ManageData.DisplayData();
我想我明白为什么会输出“默认”(调用是对页面加载时存储的原始变量进行调用)但不知道如何修复它。
对于上下文,我有一个自定义 php 脚本,它连接页面所需的所有 JS 文件,然后将它们作为单个标记添加到页面。我认为这会加快脚本加载速度,特别是因为我的大部分页面都有 50+ JS files/modules。页面的典型名称 space 结构如下所示(但包含更多模块):
MainApp = {
//Page specific or utility modules
ModuleA : [ func / module ],
ModuleB : [ func / module ],
ModuleC : [ func / module ],
ModuleD : [ func / module ],
//Resulable applications consisting of multiple modules
SubAppA : {
ModuleA : [ func / module ],
ModuleB : [ func / module ],
ModuleC : [ func / module ],
ModuleD : [ func / module ],
},
SubAppB : {
ModuleA : [ func / module ],
ModuleB : [ func / module ],
ModuleC : [ func / module ],
ModuleD : [ func / module ],
}
}
我希望我能以某种方式保留这个结构以避免模块名称冲突的风险。我很高兴改变模块本身的结构(例如,从 IIFE 到其他东西)以获得问题的解决方案。
谢谢
创建“SubAppA 的多个独立实例,包括其所有子模块”的一种方法的简单示例,其中模块在多个文件中定义:
// test.js
import { MainApp } from './mainApp.js';
import { SubApp } from './subApp.js';
MainApp.SubAppA = new SubApp();
console.log(MainApp.SubAppA.Config.A()); // 1
MainApp.SubAppB = new SubApp();
console.log(MainApp.SubAppB.Config.B()); // -1
// subApp.js
import { config } from './config.js';
import { getData } from './getData.js';
export function SubApp() {
this.Config = config();
this.GetData = getData();
}
// config.js
export function config() {
let counter = 0;
function A() { return ++counter };
function B() { return --counter };
return {
A: A,
B: B
}
}
作为单个文件:
const MainApp = {};
function SubApp() {
this.Config = config();
this.GetData = getData();
}
function config() {
let counter = 0;
function A() { return ++counter };
function B() { return --counter };
return {
A: A,
B: B
}
}
function getData() {
let counter = 0;
function A() { return ++counter };
function B() { return --counter };
return {
A: A,
B: B
}
}
MainApp.SubAppA = new SubApp();
console.log(MainApp.SubAppA.Config.A()); // 1
console.log(MainApp.SubAppA.GetData.A()); // 1
console.log(MainApp.SubAppA.Config.B()); // 0
MainApp.SubAppB = new SubApp();
console.log(MainApp.SubAppB.Config.B()); // -1
console.log(MainApp.SubAppB.GetData.B()); // -1
console.log(MainApp.SubAppB.Config.A()); // 0
与您自己的代码的重要区别在于,我已将立即调用函数表达式 (IIFE) 替换为每次创建 SubApp 时都会创建闭包的普通函数。
为响应您的编辑而添加:
为了使您在编辑中添加的代码起作用,您需要确保在创建新的 ManageData
对象时传递对父对象的引用:
$S.AS.ManageData = function (owner) {
function StoreData (Data) {
owner.DataStore.SetVar(Data);
};
function DisplayData () {
var SomeVar = owner.DataStore.GetVar();
console.log(SomeVar);
};
return {
StoreData : StoreData,
DisplayData : DisplayData
}
};
// ...
MainApp.S.AS = function () {
this.DataStore = $S.AS.DataStore();
this.ManageData = $S.AS.ManageData(this);
};
我鼓励您使用 ES 模块来构建代码并避免命名冲突。 可以从 roll-up.js 等模块打包器创建单个文件。
感谢 MikeM,现在可以使用了,非常有帮助!是的,我知道我需要更仔细地研究 ES 模块。在一个完全不同的领域工作了 17 年后,我最近学会了编码以测试一个想法,因此必须走一些捷径......
下面是我的实现步骤,以防对其他不太熟悉 Javascript 模块的人有所帮助。
问题的目的是:
- 允许一个应用程序的多个实例存在于一个页面上,每个应用程序由多个模块组成
- 允许模块调用其他模块中的方法
- 使应用程序的实例化(创建新版本)变得容易
与以下解决方案相关的一些注意事项:
- 而不是静态调用其他模块中的方法(例如 ReuseApp.App1.Display.DisplayData(Data),每个模块存储新创建的应用程序实例的顶级对象的内部引用(例如 _App.Display.DisplayData(数据).
- 创建的模块没有立即调用的功能(即没有 IIFE 模式)。
- 需要一个引用所有必需模块的构造函数。此函数会将新创建的对象 (this) 发送到每个模块,例如this.Config = ReuseApps.App1.Config(这个);
- 每个模块将此引用作为参数(App)并将其存储在模块(_App)中。 _App会在调用其他模块方法时使用。
分步指南:
步骤 A:使用以下模式创建模块(如果不需要,请忽略多级命名空间):
//Separate file e.g. Config.js
const ReuseApps = ReuseApps || {};
ReuseApps.App1 = ReuseApps.App1 || {};
ReuseApps.App1.Config = function (App) {
let _App; //Used to call other module methods
let _Settings = {};
function Init (Settings) {
_Settings = Settings;
//Configure app e.g. store element refs, add event handlers etc
var Data = GetDataFromSomeWhere();
//Call another module using the _App reference
_App.Display.DisplayData(Data);
}
_App = App;
Return {
Init : Init
}
}
//Separate file e.g. Display.js
const ReuseApps = ReuseApps || {};
ReuseApps.App1 = ReuseApps.App1 || {};
ReuseApps.App1.Display = function (App) {
let _App; //Used to call other module methods
function DisplayData (Data) {
//Display Data in DOM
}
_App = App;
return {
DisplayData : DisplayData
}
}
步骤 B:创建创建应用程序新实例所需的构造函数
//can be in separate file e.g. app1_create.js
function App1Create () {
this.Config = ReuseApps.App1.Config(this);
this.Display = ReuseApps.App1.Display(this);
//etc more modules …
}
步骤 C:在主代码中创建上述应用程序的单独实例
//Create new instance using constructur function
//(Assumes the MainApp namespace exists already)
MainApp.ViewDataList = new App1Create();
//If application needs to be initiated
var Settings = { some settings };
MainApp.ViewDataList.Config.Init(Settings);
这与 javascript 代码有关。我的应用程序有许多子应用程序,有时在一个页面上使用多次。一个示例是允许用户搜索某些数据并显示搜索结果的应用程序。该应用程序可以在页面的多个位置使用,以搜索不同类型的数据。 每个子应用程序通常包含许多 javascript 个模块,每个模块都在一个单独的文件中。我经历过许多不同的模块模式,试图创建多个 modules/files 的单独实例,但没有成功。网上有很多关于如何创建对象的多个实例、使用工厂模式等的建议,但我无法使它在我的应用程序中使用名称 space 结构和模块模式。请参阅下面的示例。
问题是如何创建 SubAppA 的多个独立实例,包括其所有子模块。
(New file)
var MainApp = MainApp || {};
MainApp.SubAppA = MainApp.SubAppA || {};
MainApp.SubAppA.Config = (function () {
function A () { ... };
function B () { ... };
return {
A : A,
B : B
}
})();
(New file)
var MainApp = MainApp || {};
MainApp.SubAppA = MainApp.SubAppA || {};
MainApp.SubAppA.GetData = (function () {
function A () { ... };
function B () { ... };
return {
A : A,
B : B
}
})();
(New file)
var MainApp = MainApp || {};
MainApp.SubAppA = MainApp.SubAppA || {};
MainApp.SubAppA.DisplayData = (function () {
etc.....
非常感谢
--- MikeM 提出的解决方案后的附加信息 -----
谢谢 MikeM,您的回答让我有了更好的理解,但是当尝试使用我现有的名称 space 结构来实现它时,我无法让模块相互通信。我尝试了以下方法:
//Solution $S.AS - New file
var $S = $S || {};
$S.AS = $S.AS || {};
$S.AS.DataStore = function () {
var _SomeVar = "Default";
function SetVar (Data) {
_SomeVar = Data;
};
function GetVar () {
return _SomeVar;
};
return {
SetVar : SetVar,
GetVar : GetVar
}
};
//Solution $S.AS - New file
var $S = $S || {};
$S.AS = $S.AS || {};
$S.AS.ManageData = function () {
function StoreData (Data) {
console.log($S.AS.DataStore); //outputs f ()
//Does now work since DataStore is now a function
//$S.AS.DataStore.SetVar(Data);
$S.AS.DataStore().SetVar(Data);
};
function DisplayData () {
//Does now work since DataStore is now a function
//var SomeVar = $S.AS.DataStore.GetVar();
//Does not work, still outputs "Default"
var SomeVar = $S.AS.DataStore().GetVar();
console.log(SomeVar);
};
return {
StoreData : StoreData,
DisplayData : DisplayData
}
};
//Solution $S.AS - New file - The contructor function for AS
var MainApp = MainApp || {};
MainApp.S = MainApp.S || {};
MainApp.S.AS = MainApp.S.AS || {};
MainApp.S.AS = function () {
this.DataStore = $S.AS.DataStore();
this.ManageData = $S.AS.ManageData();
//additional modules
};
//Main/Page specific code - creating the different instances
MainApp.S.AS_1 = new MainApp.S.AS();
MainApp.S.AS_2 = new MainApp.S.AS();
//Attemps to store and retrieve data
//Stores AAA in the DataStore module
MainApp.S.AS_1.ManageData.StoreData("AAA");
//Stores BBB in the DataStore module
MainApp.S.AS_2.ManageData.StoreData("BBB");
//Not working, ouputs: "Default" (Desired result is "AAA")
MainApp.S.AS_1.ManageData.DisplayData();
//Not working, ouputs: "Default" (Desired result is "BBB");
MainApp.S.AS_2.ManageData.DisplayData();
我想我明白为什么会输出“默认”(调用是对页面加载时存储的原始变量进行调用)但不知道如何修复它。
对于上下文,我有一个自定义 php 脚本,它连接页面所需的所有 JS 文件,然后将它们作为单个标记添加到页面。我认为这会加快脚本加载速度,特别是因为我的大部分页面都有 50+ JS files/modules。页面的典型名称 space 结构如下所示(但包含更多模块):
MainApp = {
//Page specific or utility modules
ModuleA : [ func / module ],
ModuleB : [ func / module ],
ModuleC : [ func / module ],
ModuleD : [ func / module ],
//Resulable applications consisting of multiple modules
SubAppA : {
ModuleA : [ func / module ],
ModuleB : [ func / module ],
ModuleC : [ func / module ],
ModuleD : [ func / module ],
},
SubAppB : {
ModuleA : [ func / module ],
ModuleB : [ func / module ],
ModuleC : [ func / module ],
ModuleD : [ func / module ],
}
}
我希望我能以某种方式保留这个结构以避免模块名称冲突的风险。我很高兴改变模块本身的结构(例如,从 IIFE 到其他东西)以获得问题的解决方案。 谢谢
创建“SubAppA 的多个独立实例,包括其所有子模块”的一种方法的简单示例,其中模块在多个文件中定义:
// test.js
import { MainApp } from './mainApp.js';
import { SubApp } from './subApp.js';
MainApp.SubAppA = new SubApp();
console.log(MainApp.SubAppA.Config.A()); // 1
MainApp.SubAppB = new SubApp();
console.log(MainApp.SubAppB.Config.B()); // -1
// subApp.js
import { config } from './config.js';
import { getData } from './getData.js';
export function SubApp() {
this.Config = config();
this.GetData = getData();
}
// config.js
export function config() {
let counter = 0;
function A() { return ++counter };
function B() { return --counter };
return {
A: A,
B: B
}
}
作为单个文件:
const MainApp = {};
function SubApp() {
this.Config = config();
this.GetData = getData();
}
function config() {
let counter = 0;
function A() { return ++counter };
function B() { return --counter };
return {
A: A,
B: B
}
}
function getData() {
let counter = 0;
function A() { return ++counter };
function B() { return --counter };
return {
A: A,
B: B
}
}
MainApp.SubAppA = new SubApp();
console.log(MainApp.SubAppA.Config.A()); // 1
console.log(MainApp.SubAppA.GetData.A()); // 1
console.log(MainApp.SubAppA.Config.B()); // 0
MainApp.SubAppB = new SubApp();
console.log(MainApp.SubAppB.Config.B()); // -1
console.log(MainApp.SubAppB.GetData.B()); // -1
console.log(MainApp.SubAppB.Config.A()); // 0
与您自己的代码的重要区别在于,我已将立即调用函数表达式 (IIFE) 替换为每次创建 SubApp 时都会创建闭包的普通函数。
为响应您的编辑而添加:
为了使您在编辑中添加的代码起作用,您需要确保在创建新的 ManageData
对象时传递对父对象的引用:
$S.AS.ManageData = function (owner) {
function StoreData (Data) {
owner.DataStore.SetVar(Data);
};
function DisplayData () {
var SomeVar = owner.DataStore.GetVar();
console.log(SomeVar);
};
return {
StoreData : StoreData,
DisplayData : DisplayData
}
};
// ...
MainApp.S.AS = function () {
this.DataStore = $S.AS.DataStore();
this.ManageData = $S.AS.ManageData(this);
};
我鼓励您使用 ES 模块来构建代码并避免命名冲突。 可以从 roll-up.js 等模块打包器创建单个文件。
感谢 MikeM,现在可以使用了,非常有帮助!是的,我知道我需要更仔细地研究 ES 模块。在一个完全不同的领域工作了 17 年后,我最近学会了编码以测试一个想法,因此必须走一些捷径......
下面是我的实现步骤,以防对其他不太熟悉 Javascript 模块的人有所帮助。
问题的目的是:
- 允许一个应用程序的多个实例存在于一个页面上,每个应用程序由多个模块组成
- 允许模块调用其他模块中的方法
- 使应用程序的实例化(创建新版本)变得容易
与以下解决方案相关的一些注意事项:
- 而不是静态调用其他模块中的方法(例如 ReuseApp.App1.Display.DisplayData(Data),每个模块存储新创建的应用程序实例的顶级对象的内部引用(例如 _App.Display.DisplayData(数据).
- 创建的模块没有立即调用的功能(即没有 IIFE 模式)。
- 需要一个引用所有必需模块的构造函数。此函数会将新创建的对象 (this) 发送到每个模块,例如this.Config = ReuseApps.App1.Config(这个);
- 每个模块将此引用作为参数(App)并将其存储在模块(_App)中。 _App会在调用其他模块方法时使用。
分步指南:
步骤 A:使用以下模式创建模块(如果不需要,请忽略多级命名空间):
//Separate file e.g. Config.js
const ReuseApps = ReuseApps || {};
ReuseApps.App1 = ReuseApps.App1 || {};
ReuseApps.App1.Config = function (App) {
let _App; //Used to call other module methods
let _Settings = {};
function Init (Settings) {
_Settings = Settings;
//Configure app e.g. store element refs, add event handlers etc
var Data = GetDataFromSomeWhere();
//Call another module using the _App reference
_App.Display.DisplayData(Data);
}
_App = App;
Return {
Init : Init
}
}
//Separate file e.g. Display.js
const ReuseApps = ReuseApps || {};
ReuseApps.App1 = ReuseApps.App1 || {};
ReuseApps.App1.Display = function (App) {
let _App; //Used to call other module methods
function DisplayData (Data) {
//Display Data in DOM
}
_App = App;
return {
DisplayData : DisplayData
}
}
步骤 B:创建创建应用程序新实例所需的构造函数
//can be in separate file e.g. app1_create.js
function App1Create () {
this.Config = ReuseApps.App1.Config(this);
this.Display = ReuseApps.App1.Display(this);
//etc more modules …
}
步骤 C:在主代码中创建上述应用程序的单独实例
//Create new instance using constructur function
//(Assumes the MainApp namespace exists already)
MainApp.ViewDataList = new App1Create();
//If application needs to be initiated
var Settings = { some settings };
MainApp.ViewDataList.Config.Init(Settings);