我是javascript原型的新手。
在示例中,原型与主程序定义一致,但这样做会产生启动序列分支。
以下显示了我目前如何将原型应用于一组单身人士。为了清楚起见,最好在后代类中分配原型,或者更明显地“绑定”它的某个地方。 (注意,面板在控制器中实例化以强制分离。)
是否还有其他位置/方法来实现这一点我忽略了?另外,我是否违反了目前采用的任何着名风格?
var controller = new Controller();
function Controller() {
var panels = {
search: SearchPanel,
results: ResultsPanel,
details: DetailsPanel,
action: ActionPanel,
};
$.each(panels, function (i, v) {
// THE QUESTION REFERS TO THIS FOLLOWING STATEMENT:
v.prototype = new PanelCommon();
panels[i] = new v();
});
this.publish = function (site, message) {
$.each(panels, function (i, v) {
if (v[site]) v[site](message);
});
}
/*...*/
}
function PanelCommon() { /*...*/ }
function SearchPanel() { /*...*/ }
function ResultsPanel() { /*...*/ }
function DetailsPanel() { /*...*/ }
function ActionPanel() { /*...*/ }
答案 0 :(得分:2)
另一种适合JavaScript动态特性的是Mixins
或Augmentation
的概念,它有时比原型继承更自然。
一个“mixin”,它接受一个对象,并注入更多功能。基本上,我们的想法是,我们将采用一个对象,并开始向其添加行为。
考虑以下mixinPanelTo()
函数。它将是一个带有构造函数的函数,并为它的原型添加一个通用的render()
函数。
var mixinPanelTo = (function() {
var render = function() {
// a render function that all panels share
console.log("rendering!")
}
// Augment
return function(cls) {
cls.prototype.render = render;
}
})();
现在我们有了这个,我们可以将这个功能混合到我们想要的任何构造函数中:
var SearchPanel = function() {}
SearchPanel.prototype.search = function(query) {
/* search stuff */
this.render();
}
mixinPanelTo(SearchPanel)
然后,我们应该能够
var panel = new SearchPanel()
panel.search("foo"); // "rendering!" on the console
mixins相对于继承的一个优点是对应用功能的更精细控制,以及从多个父项借用功能的能力
var mixinRender = function(cls) { /* inject render */ }
var mixinSearch = function(cls) { /* inject search */ }
var mixinInfiniteScroll = function(cls) { /* inject infinite scroll */ }
var customPanel = function() {}
mixinRender(customPanel);
mixinSearch(customPanel);
mixinInfiniteScroll(customPanel)
使用原型继承很难实现。除了尝试制作奇怪的类层次结构外。
您还可以从目标类中获取mixin的功能/配置。例如,我们可以mixinInfinitScroll
var mixinInfiniteScroll = function(cls, fetch) {
var page = 0;
cls.prototype.more = function() {
var data
// get more results
if(typeof fetch == "function")
data = fetch.call(this, ++page)
else
// assume a key in this
data = this[fetch](++page)
/* do work with data */
}
}
然后在混合使用此功能时,我们可以注入特定的功能:
// by key
var Panel1 = function() { }
Panel1.prototype.fetch = function() { /* go get more results */ }
mixinInifiniteScroll(Panel1, "fetch")
// or even with a direct reference
var Panel1 = function() { }
Panel1.prototype.fetch = function() { /* go get more results */ }
mixinInifiniteScroll(Panel1, Panel1.prototype.fetch)
// or even an anonymous function
var Panel1 = function() { }
mixinInifiniteScroll(Panel1, function() { /* go get more results */ })
你也可以在mixins中覆盖原型方法,这使得它们非常强大
var augmentRender = function(cls, renderFn) {
var oldRender = cls.prototype[renderFn];
cls.prototype[renderFn] = function() {
/* prep */
oldRender.apply(this, arguments);
/* make some more changes */
}
}
然后我们可以说:
var Panel = function() { }
Panel.prototype.render = function() { /* my render */ }
augmentRender(Panel, "render")
无论如何,并不是说原型继承有什么问题,但是这可能会给你一些不同方法的想法,通过以不同的方式解决你的问题。
答案 1 :(得分:1)
通常在声明构造函数之后立即分配原型。另外,不要忘记修改新实例化原型的constructor
属性。
Sean也提出了一个关于使用Object.create的有趣观点,但是否要这样做实际上取决于PanelCommon构造函数的内容。您也可能需要在旧版浏览器中使用Shim.shite。
function PanelCommon() {}
function SearchPanel() {}
SearchPanel.prototype = new PanelCommon();
SearchPanel.prototype.constructor = SearchPanel;
function ResultsPanel() {}
ResultsPanel.prototype = new PanelCommon();
ResultsPanel.prototype.constructor = ResultsPanel;
function DetailsPanel() {}
DetailsPanel.prototype = new PanelCommon();
DetailsPanel.prototype.constructor = DetailsPanel;
function ActionPanel() {}
ActionPanel.prototype = new PanelCommon();
ActionPanel.prototype.constructor = ActionPanel;
答案 2 :(得分:1)
您可以使用Object.create
- 这将避免ES3解决方案的new SuperClass
奇怪:
> SearchPanel.prototype = Object.create(PanelCommon.prototype)
> SearchPanel.prototype.constructor = SearchPanel
> new SearchPanel instanceof PanelCommon
true
这可以提取到一个非常简单的extends
函数中:
function extends(cls, superClass) {
cls.prototype = Object.create(superClass.prototype);
cls.prototype.constructor = cls;
return cls;
}
然后可以这样使用:
var SpecialPanel = extends(function SpecialPanel() {}, PanelCommon);