JavaScript:模型视图控制器(MVC)重构现有代码策略
我将重新考虑一个带有Python/Django后端的大型单页web应用程序。我不想使用Angular.js、JavaScriptMVC、BackBone.js等任何现有框架。已经有大量的HTML和JavaScript代码,要遵守它们的方法需要很长时间 我已经使用揭示原型模式开发了一个MVC架构。您可以在此处看到一个简化的工作演示和源代码:。有一些_vars打算成为公共vars,我只是没有时间将它们全部重命名,所以请忽略下划线的含义 我的MVC是:[模型][控制器][视图] 示例中的体系结构实现了我想要的一切:JavaScript:模型视图控制器(MVC)重构现有代码策略,javascript,model-view-controller,revealing-prototype,Javascript,Model View Controller,Revealing Prototype,我将重新考虑一个带有Python/Django后端的大型单页web应用程序。我不想使用Angular.js、JavaScriptMVC、BackBone.js等任何现有框架。已经有大量的HTML和JavaScript代码,要遵守它们的方法需要很长时间 我已经使用揭示原型模式开发了一个MVC架构。您可以在此处看到一个简化的工作演示和源代码:。有一些_vars打算成为公共vars,我只是没有时间将它们全部重命名,所以请忽略下划线的含义 我的MVC是:[模型][控制器][视图] 示例中的体系结构实现了
- 有没有其他/更好的方法来实现这一点?(可能是通过继承)
- 自动实现它的一种方法:ie var FolderManager=Class({})。
- 我尝试采用上面的技术来选择我的公共变量和内部变量,并将它们应用到构造函数中。我无法让它在实例化时构造函数可以使用self的地方工作,以便我可以将它传递给模型、视图和控制器。(这将解决问题,请查找下面的代码)
//创建公共变量(将其移动到类方法) 对于(公共变量中的变量v){this[v]=公共变量[v];} //创建内部变量(将其移动到类方法) var self={}; 对于(公共变量中的v){self[v]=公共变量[v];} 对于(v在内部变量中){self[v]=内部变量[v];}
- 我尝试采用上面的技术来选择我的公共变量和内部变量,并将它们应用到构造函数中。我无法让它在实例化时构造函数可以使用self的地方工作,以便我可以将它传递给模型、视图和控制器。(这将解决问题,请查找下面的代码)
var FolderManager = function(name){
//self instantiate
if (!(this instanceof FolderManager)) return new FolderManager(name);
//private vars
var privateVar = 'private vars are available only to the \"'+name+'\" constructor.';
//internal public vars
var internal_vars={
internalVar:'internal_vars are available only to internal classes.',
sortable:false
};
//public vars
var public_vars = {
publicVar:'public_vars are available to everything.',
toString:name,
name:name
};
//create public vars (move this to a Class Method)
for (var v in public_vars){this[v] = public_vars[v];}
//create internal vars (move this to a Class Method)
var self = {};
for (v in public_vars){self[v] = public_vars[v];}
for (v in internal_vars){self[v] = internal_vars[v];}
//instantiate private classes with internalally public vars
//and the classes they each need to have access to
var model = new FolderManagerModel(self);
var view = new FolderManagerView(self, model);
var cont = new FolderManagerCont(self, model, view);
//provide public access to internal classes as required
this.refresh = model.refresh.bind(model)
this.out = view.out.bind(view)
// init model
this.refresh();
};
var Class = function (className, methods, options) {
options = options || {};
options.ret = options.ret !== false;
options.context = options.context || window
//compile the constructor & internalMembers
var init = function () {
console.log('Class() is building:', this.constructor.name);
//provide inernal object for constructor
this.internal = {}
this.constructor.apply(this, arguments);
//remove internal from public scope
var int = this.internal
delete this.internal
//populate self with this and internal vars
var self = {pub:this, int:{}};
for (var v in int){
self.int[v] = int[v];
}
// Instantiate internalMembers with self
var internalClass = methods.internalClass;
if (internalClass) internalClass.call(this, self);
};
//create constructor function with className (fixes class name in debugger)
//also includes self instantiation
var klass = new Function("init", "return function " + className + "(name){ if (!(this instanceof " + className + ")) return new " + className + "(name);init.apply(this,arguments) };")(init);
//create prototype from Class method
var prototype = methods.prototype;
if (prototype) klass.prototype = new prototype();
//add other Class methods to prototype
var exclude = ['internalClass', 'initialize', 'prototype'];
for (var property in methods) {
if (exclude.indexOf(property) == -1) {
klass.prototype[property] = methods[property];
}
}
if (options.ret) return klass; //return the class
else options.context[className] = klass; //create the class
};
模型构造函数和原型(视图和控制器用户格式相同,完整代码见演示):
更新: 我已经完成了我的类构造函数,以消除一些腿部工作,并修复了我的内部公共变量问题。我剩下的一个问题是:为了让调试器显示对象的类名,我需要为该类创建一个名为的函数。唯一的方法是使用新的function()构造函数。我理解由于性能问题,这不是一个好的做法,因为每次调用函数时都需要对其进行解析。但是,由于我在构造函数中使用它,因此仅在实例化时调用(一次)。这仍然是一个坏主意,还是这是一个合适的情况 类构造函数:
var FolderManager = function(name){
//self instantiate
if (!(this instanceof FolderManager)) return new FolderManager(name);
//private vars
var privateVar = 'private vars are available only to the \"'+name+'\" constructor.';
//internal public vars
var internal_vars={
internalVar:'internal_vars are available only to internal classes.',
sortable:false
};
//public vars
var public_vars = {
publicVar:'public_vars are available to everything.',
toString:name,
name:name
};
//create public vars (move this to a Class Method)
for (var v in public_vars){this[v] = public_vars[v];}
//create internal vars (move this to a Class Method)
var self = {};
for (v in public_vars){self[v] = public_vars[v];}
for (v in internal_vars){self[v] = internal_vars[v];}
//instantiate private classes with internalally public vars
//and the classes they each need to have access to
var model = new FolderManagerModel(self);
var view = new FolderManagerView(self, model);
var cont = new FolderManagerCont(self, model, view);
//provide public access to internal classes as required
this.refresh = model.refresh.bind(model)
this.out = view.out.bind(view)
// init model
this.refresh();
};
var Class = function (className, methods, options) {
options = options || {};
options.ret = options.ret !== false;
options.context = options.context || window
//compile the constructor & internalMembers
var init = function () {
console.log('Class() is building:', this.constructor.name);
//provide inernal object for constructor
this.internal = {}
this.constructor.apply(this, arguments);
//remove internal from public scope
var int = this.internal
delete this.internal
//populate self with this and internal vars
var self = {pub:this, int:{}};
for (var v in int){
self.int[v] = int[v];
}
// Instantiate internalMembers with self
var internalClass = methods.internalClass;
if (internalClass) internalClass.call(this, self);
};
//create constructor function with className (fixes class name in debugger)
//also includes self instantiation
var klass = new Function("init", "return function " + className + "(name){ if (!(this instanceof " + className + ")) return new " + className + "(name);init.apply(this,arguments) };")(init);
//create prototype from Class method
var prototype = methods.prototype;
if (prototype) klass.prototype = new prototype();
//add other Class methods to prototype
var exclude = ['internalClass', 'initialize', 'prototype'];
for (var property in methods) {
if (exclude.indexOf(property) == -1) {
klass.prototype[property] = methods[property];
}
}
if (options.ret) return klass; //return the class
else options.context[className] = klass; //create the class
};
它的用途如下:
Class('FolderManager', {
constructor: function FolderManager(name) {
//private vars
var privateVar = 'this value is only accesable to the \"'+name+'\" constructor.';
//public internal mthods and vars
this.internal.internalVar='this value is only accesable to internal classes.',
this.internal.sortable=false;
//public vars
this.publicVar='this is a public vlue';
this.name = name;
},
internalClass:function(self){
//instantiate private classes
var model = new FolderManagerModel(self);
var view = new FolderManagerView(self, model);
var cont = new FolderManagerCont(self, model, view);
//public access private class methods
this.refresh = model.refresh.bind(model);
this.out = view.out.bind(view);
this.model = model;
// init model
this.refresh();
},
prototype:function(){
// all static vars and methods here
},
},{ret:false,context:this});
完整的工作演示:我相信您对这一切的设计有点过火
不久前,我创建了视图、控制器、路由和作用域。检查一下,它只有75行代码。也是MVC的简单实现 Html: 型号:
var model = {
calculate: function () {
var x = $("input#input").val();
return x * 2;
}
};
控制器:
var controller = {
action: function () {
$("button#showResult").click(function () {
var res = model.calculate();
view.showNumber(res);
});
}
};
请把代码的相关部分(可能带有缩写的值)放在你的问题中。我不明白为什么“Main”应该有任何私有或公共变量。只要把它们放在模型上就可以了…谢谢你的反馈@Bergi。添加main是解决javascripts缺少私有和内部成员的问题的方法。我来自动作脚本/java背景。由于该应用程序将有许多组件,因此该策略将尽可能保持全球范围的未经打磨。我以前从未见过这种情况,我想知道我是否犯了错误。您认为这种策略有什么缺点吗?我认为为应用程序设置
Main
没有什么错,但它不需要有太多的状态和属性。@Bergi,再次感谢。请看上面的更新,让我知道你对类构造函数的看法。SPA看起来很酷。不过,正如我刚才所说,我并没有试图用板条箱包装一个框架,也不能使用任何现有的框架。我正在使用的代码库已经有了自己的路由方法,而且非常复杂。设置所有控制器并定义HTML。我只需要将所有JavaScript插入MVC。这提供了一个空白画布,在这里我可以复制和粘贴所有的方法和变量。我非常感谢您抽出时间来查看。对于小任务,请在不同的文件中使用单独的逻辑。通过ajax在主文件中包含文件
var controller = {
action: function () {
$("button#showResult").click(function () {
var res = model.calculate();
view.showNumber(res);
});
}
};