何时/何时设置功能原型?

时间:2013-11-16 16:43:56

标签: javascript prototype

我是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() { /*...*/ }

3 个答案:

答案 0 :(得分:2)

另一种适合JavaScript动态特性的是MixinsAugmentation的概念,它有时比原型继承更自然。

mixin是什么意思?

一个“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

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);