【问题标题】:Are computed observables on prototypes feasible in KnockoutJS?在 KnockoutJS 中计算原型上的 observables 是否可行?
【发布时间】:2012-05-05 04:59:55
【问题描述】:

我有一系列从服务返回的项目。我试图为每个 Item 实例定义一个计算的 observable,所以我的直觉告诉我把它放在原型上。

计算 observable 的一种情况:系统计算点,但用户可以选择覆盖计算值。我需要保持计算值可用,以防用户删除覆盖。我还需要合并用户分配和计算的点,然后将总数相加。

我正在使用映射来执行以下操作:

var itemsViewModel;
var items = [
    { 'PointsCalculated' : 5.1 },
    { 'PointsCalculated' : 2.37, 'PointsFromUser' : 3 }
];

var mapping = {
    'Items' : {
        create : function(options) {
            return new Item(options.data);
        }
    }
};

var Item = function(data) {
    var item = this;
    ko.mapping.fromJS(data, mapping, item);
};

Item.prototype.Points = function () {
    var item = this;
    return ko.computed(function () {
        // PointsFromUser may be 0, so only ignore it if the value is undefined/null
        return (item.PointsFromUser != null) ? item.PointsFromUser : item.PointsCalculated;
    });
};

ko.mapping.fromJS(items, mapping, itemsViewModel);

它现在的工作方式,我必须调用匿名函数来返回计算出的 observable。这似乎为每个绑定创建了一个计算的 observable 的新实例,这违背了将它放在原型上的大部分要点。每次访问 observable 时都必须破译使用多少个括号,这有点烦人。

它也有些脆弱。如果我尝试在代码中访问 Points(),我做不到

var points = 0;
var p = item.Points;
if (p && typeof p === 'function') {
    points += p();
}

因为将 Points() 的上下文更改为 DOMWindow,而不是 item。

如果我将 create() 中的计算结果放入映射中,我可以捕获上下文,但是每个对象实例上都有该方法的副本。

我找到了 Michael Best 的 Google 网上论坛帖子 (http://groups.google.com/group/knockoutjs/browse_thread/thread/8de9013fb7635b13)。原型在“激活”时返回一个新的计算 observable。我还没有弄清楚什么叫做“激活”(也许是 Objs?),但我猜它仍然会每个对象发生一次,而且我不知道“this”会得到什么范围。

在这一点上,我相信我已经超越了已发布文档中可用的内容,但我仍在努力从源头上破译发生了什么。

【问题讨论】:

    标签: knockout.js knockout-mapping-plugin


    【解决方案1】:

    您提到您不想在您的 javascript 类的每个实例上都有一个 ko.computed 函数的实例,但是,这与 ko 的功能是如何构建的并不真正有效。当您使用 ko.computedko.observable 时,它们会创建指向内部私有变量的特定内存指针,您通常不希望在类实例之间共享(尽管在极少数情况下您可能会这样做)。

    我会这样做:

    var Base = function(){
        var extenders = [];
    
        this.extend = function(extender){
            extenders.push(extender);
        };
    
        this.init = function(){
            var self = this; // capture the class that inherits off of the 'Base' class
    
            ko.utils.arrayForEach(extenders, function(extender){
    
                 // call each extender with the correct context to ensure all
                 // inheriting classes have the same functionality added by the extender
                 extender.call( self );
            });
        };
    };
    
    var MyInheritedClass = function(){
        // whatever functionality you need
    
       this.init(); // make sure this gets called
    };
    
    // add the custom base class
    MyInheritedClass.prototype = new Base();
    

    然后对于计算的 observables(必须是 MyInheritedClass 的每个实例上的实例函数),我只需在 extender 中声明它们,如下所示:

    MyInheritedClass.prototype.extend(function(){
    
         // custom functionality that i want for each class 
         this.something = ko.computed(function() {
             return 'test';
         });
    });
    

    鉴于您的示例和上面定义的 Base 类,您可以轻松做到:

    var Item = function(data) {
        var item = this;
    
        ko.mapping.fromJS(data, mapping, item);
    
        this.init(); // make sure this gets called
    };
    Item.prototype = new Base();
    
    Item.prototype.extend(function () {
        var self = this;
    
        this.Points = ko.computed(function () {
    
            // PointsFromUser may be 0, so only ignore it if the value is undefined/null
            return (self.PointsFromUser != null) ? 
                   self.PointsFromUser : self.PointsCalculated;
        });
    };
    

    那么您的Item 类的所有实例都将具有Points 属性,并且它将正确处理每个实例的ko.computed 逻辑。

    【讨论】:

    • 谢谢。我认为 .extend() 的目的刚刚被点击。这很好地解决了我的作用域/双重函数调用问题,我相信 Knockout 不会让我在原型上保留实际计算的 observable。
    • eric,你能提供一个更“糖化”的版本吗?类似 childClass = BaseClass.extend(function(){/*...*/}) 的东西已经完成了原型链设置?必须为每个类实例手动执行它有点奇怪......
    • 我看不出有什么理由不能在继承的构造函数上放置一个“扩展”静态函数,只要它基本上是这样:SubClass.extend = function(extender){ SubClass.prototype.extend(extender); };
    【解决方案2】:
    Item.prototype.Points = function () {
    var item = this;
    return ko.computed(function () {
        // PointsFromUser may be 0, so only ignore it if the value is undefined/null
        return (item.PointsFromUser != null) ? item.PointsFromUser : item.PointsCalculated;
    });
    

    };

    【讨论】:

      【解决方案3】:
      Item.prototype.extend(function () {
      var scope = this
      this.Points = ko.computed(function () {
          return (this.PointsFromUser != null) ? 
                 this.PointsFromUser : this.PointsCalculated;
      }, scope);
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-23
        • 2014-08-28
        • 2012-04-26
        • 2017-09-10
        • 2014-10-23
        • 1970-01-01
        • 1970-01-01
        • 2013-07-19
        相关资源
        最近更新 更多