声明为对象文字与函数的淘汰视图模型之间的区别

Posted

技术标签:

【中文标题】声明为对象文字与函数的淘汰视图模型之间的区别【英文标题】:Difference between knockout View Models declared as object literals vs functions 【发布时间】:2012-03-24 06:27:25 【问题描述】:

在淘汰赛 js 中,我看到 View Models 声明为:

var viewModel = 
    firstname: ko.observable("Bob")
;

ko.applyBindings(viewModel );

或:

var viewModel = function() 
    this.firstname= ko.observable("Bob");
;

ko.applyBindings(new viewModel ());

如果有的话,两者有什么区别?

我确实在 knockoutjs google 群组上找到了this discussion,但它并没有真正给我一个满意的答案。

如果我想用一些数据初始化模型,我可以看到一个原因,例如:

var viewModel = function(person) 
    this.firstname= ko.observable(person.firstname);
;

var person = ... ;
ko.applyBindings(new viewModel(person));

但如果我不这样做,我选择哪种风格重要吗?

【问题讨论】:

我认为没有区别。我通常使用构造函数模式,因为我经常有我喜欢在prototype 上声明的方法(例如,经常从服务器获取数据并相应地更新视图模型的方法)。但是,您仍然可以明显地将它们声明为对象文字的属性,所以我看不出有什么区别。 这与淘汰赛无关,一切都与 javascript 中自定义对象的实例化容易程度有关 @Kev 如果 viewModel 是一个构造函数,你可以用大写来写它,就像 var PersonViewModel = function()...; 【参考方案1】:

使用函数定义视图模型有几个优点。

主要优点是您可以立即访问等于正在创建的实例的this 值。这意味着您可以:

var ViewModel = function(first, last) 
  this.first = ko.observable(first);
  this.last = ko.observable(last);
  this.full = ko.computed(function() 
     return this.first() + " " + this.last();
  , this);
;

因此,即使从不同的范围调用,您的计算 observable 也可以绑定到 this 的适当值。

使用对象字面量,您必须这样做:

var viewModel = 
   first: ko.observable("Bob"),
   last: ko.observable("Smith"),
;

viewModel.full = ko.computed(function() 
   return this.first() + " " + this.last();
, viewModel);

在这种情况下,您可以直接在计算的 observable 中使用 viewModel,但它会立即评估(默认情况下),因此您无法在对象字面量中定义它,因为 viewModel 直到之后才定义对象字面量关闭。许多人不喜欢你的视图模型的创建没有被封装到一个调用中。

另一种可以用来确保this 始终合适的模式是在函数中设置一个等于this 的合适值的变量,然后改用它。这就像:

var ViewModel = function() 
    var self = this;
    this.items = ko.observableArray();
    this.removeItem = function(item) 
         self.items.remove(item);
    
;

现在,如果您在单个项目的范围内并调用$root.removeItemthis 的值实际上将是在该级别绑定的数据(这将是项目)。通过在这种情况下使用 self,您可以确保将其从整体视图模型中移除。

另一个选项是使用bind,现代浏览器支持并由KO添加,如果不支持。在这种情况下,它看起来像:

var ViewModel = function() 
    this.items = ko.observableArray();
    this.removeItem = function(item) 
         this.items.remove(item);
    .bind(this);
;

关于这个主题还有更多可以说的内容以及您可以探索的许多模式(例如模块模式和显示模块模式),但基本上使用函数可以让您更灵活地控制对象的创建方式和能够引用实例私有的变量。

【讨论】:

很好的答案。我经常对复杂对象(如视图模型)使用函数(使用显示模块模式)。但是对于简单的模型,我使用了一个函数,这样我就可以在一个地方处理所有事情。 @JohnPapa - 刚刚观看了关于淘汰赛的 PluralSight 视频(刚刚过了一半 - 巧合的是,刚刚观看了关于对象文字与函数的部分)。真的做得很好,帮助一分钱下降。仅此一项就值得一个月的订阅。 @Kev - 谢谢。很高兴你能从中获得价值。有些人不会关心那个模块,因为它不是一个真正的 Knockout 概念,更多的是 JavaScript 模式。但随着我对 Knockout 的深入了解,我发现这些概念确实帮助我创建了更干净更稳定的代码。无论如何,很高兴你喜欢它:) 不应该是 self.items = ko.observableArray();在你的第二个例子中?你用过这个,对吗? @JackNova 在构造函数selfthis 中是相同的,所以两者都是等价的。在 removeItem 函数中,self 确实变得更加有用,因为在子项的上下文中执行时,this 将不再是当前实例。【参考方案2】:

我使用不同的方法,虽然类似:

var viewModel = (function () 
  var obj = ;
  obj.myVariable = ko.observable();
  obj.myComputed = ko.computed(function ()  return "hello" + obj.myVariable() );

  ko.applyBindings(obj);
  return obj;
)();

几个原因:

    不使用this,在ko.computeds 等中使用时会造成混淆 我的 viewModel 是单例的,我不需要创建多个实例(即new viewModel()

【讨论】:

如果没记错的话,这是揭示模块模式。很好的答案,但问题与这种模式无关。 @paul :很抱歉询问旧线程。你说My viewModel is a singleton, I don't need to create multiple instances (i.e. new viewModel()) ,但不清楚你想说什么I don't need to create multiple instances ,你能不能多用一些,这样人们就可以理解你的方法的优势。谢谢 IMO,您将 ViewModel 声明为 function 的原因之一是您将多次执行它。但是,在我的示例中,它是一个立即调用的匿名函数,因此不会被多次创建。它与上面示例中的 Object Literal 非常相似,但提供了更多的隔离

以上是关于声明为对象文字与函数的淘汰视图模型之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

两个视图模型之间的淘汰赛传递值

使用视图控制器淘汰嵌套的可观察对象

在淘汰赛中解除视图模型与视图的绑定

如何处理淘汰视图模型中的多对多关系

mvc mvp mvvm的区别

UML学习-----序列图