Javascript 在何处/何时设置功能原型?

Javascript 在何处/何时设置功能原型?,javascript,prototype,Javascript,Prototype,我不熟悉javascript原型 在示例中,原型是按照主程序定义分配的,但这样做会产生启动序列的影响 下面显示了我当前如何将原型应用于一组单例。为了清晰起见,最好将原型分配到后代类中,或者更明显地“绑定”到它的某个地方。(注意,面板在控制器内实例化以强制分离。) 是否有其他的地点/方法来完成这一点,我忽略了?此外,我是否违反了当前方法的任何著名风格 var controller = new Controller(); function Controller() { var panels

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

通常在构造函数声明之后立即分配原型。另外,不要忘记修改新实例化原型的
构造函数
属性

var mixinPanelTo = (function() {

  var render = function() {
    // a render function that all panels share
    console.log("rendering!")
  }

  // Augment
  return function(cls) {
    cls.prototype.render = render;
  }
})();
Sean还对使用Object.create提出了一个有趣的观点,但您是否希望这样做实际上取决于PanelCommon构造函数的内容。您可能还必须在旧浏览器中填充Object.create

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;

通常在构造函数声明之后立即分配原型。另外,不要忘记修改新实例化原型的
构造函数
属性

var mixinPanelTo = (function() {

  var render = function() {
    // a render function that all panels share
    console.log("rendering!")
  }

  // Augment
  return function(cls) {
    cls.prototype.render = render;
  }
})();
Sean还对使用Object.create提出了一个有趣的观点,但您是否希望这样做实际上取决于PanelCommon构造函数的内容。您可能还必须在旧浏览器中填充Object.create

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;
您可以使用-这将避免ES3解决方案的
新超类
怪异之处:

> 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);
您可以使用-这将避免ES3解决方案的
新超类
怪异之处:

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

另一个适合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
多重混合 与继承相比,mixin的一个优点是对应用的功能进行更精细的控制,并且能够从多个父级借用功能

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 */ })
重载方法 您还可以覆盖mixin中的原型方法,这使得它们非常强大

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

无论如何,这并不是说原型继承有什么问题,但这可能会让您对以不同方式解决问题的不同方法有更多的了解。

另一个适合JavaScript动态特性的概念是
混合
增强
,有时比原型遗传更自然

我说的混血是什么意思? 一个“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
多重混合 与继承相比,mixin的一个优点是对应用的功能进行更精细的控制,并且能够从多个父级借用功能

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 */ })
重载方法 您还可以覆盖mixin中的原型方法,这使得它们非常强大

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

无论如何,这并不是说原型继承有什么问题,但这可能会给你更多的想法,让你以不同的方式解决问题。

迈克,众所周知的做法就是这样做,但是,通过将我的依赖实例化移动到代码的末尾来尊重结果序列依赖性?即,可能声明
var控制器在头部,但不考虑位置
控制器=新控制器()在尾部?嗯。是的,Javascript有时依赖顺序,有时不依赖顺序。一般来说,你应该在使用它们之前声明它们,即使这不是严格必要的。函数声明和函数定义之间有区别<代码>函数foo(){}
是一个声明,无论它出现在何处,都将从文件顶部可见
var foo=function(){}
是一个函数表达式,
foo
的值将是