凯尔·辛普森的OLOO模式与原型设计模式

2022-08-30 05:17:57

Kyle Simpson的“OLOO(对象链接到其他对象)模式”与原型设计模式有何不同?除了通过明确指示“链接”(原型的行为)并澄清这里没有“复制”(类的行为)来创造它之外,他的模式究竟引入了什么?

以下是Kyle在他的书“You Don't Know JS: this & Object Prototypes”中的一个例子

var Foo = {
    init: function(who) {
        this.me = who;
    },
    identify: function() {
        return "I am " + this.me;
    }
};

var Bar = Object.create(Foo);

Bar.speak = function() {
    alert("Hello, " + this.identify() + ".");
};

var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2");

b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."

答案 1

他的模式究竟引入了什么?

OLOO按原样接受原型链,而无需在其他(IMO混淆)语义上进行分层以获得链接。

因此,这两个片段具有完全相同的结果,但到达那里的方式不同。

构造函数形式:

function Foo() {}
Foo.prototype.y = 11;

function Bar() {}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.z = 31;

var x = new Bar();
x.y + x.z;  // 42

OLOO 表格:

var FooObj = { y: 11 };

var BarObj = Object.create(FooObj);
BarObj.z = 31;

var x = Object.create(BarObj);
x.y + x.z;  // 42

在这两个代码段中,一个对象都链接到一个对象 ( or ),而该对象又链接到第三个对象 ( or )。x[[Prototype]]Bar.prototypeBarObjFoo.prototypeFooObj

代码段之间的关系和委派是相同的。代码段之间的内存使用情况相同。在代码段之间创建许多“子”(即,许多对象,如 through 等)的能力是相同的。段间的委托 (和 ) 的性能是相同的。使用 OLOO 时,对象创建性能较慢,但理智检查显示性能较慢实际上不是问题。x1x1000x.yx.z

我认为OLOO提供的是,仅仅表达对象并直接链接它们比通过构造函数/机制间接链接它们要简单得多。后者假装是关于类的,但实际上只是表达授权的可怕语法(旁注:ES6语法也是如此!newclass

OLOO只是在削减中间人。

这是与OLOO的另一个比较class


答案 2

我读了凯尔的书,我发现它非常翔实,特别是关于如何装订的细节。this

优点:

对我来说,OLOO有几个大优点:

1. 简单

OLOO 依赖于创建一个链接到另一个对象的新对象。您不必了解函数具有属性,也不必担心其修改带来的任何潜在相关陷阱。Object.create()[[prototype]]prototype

2. 更清晰的语法

这是有争议的,但我觉得OLOO语法(在许多情况下)比“标准”javascript方法更整洁,更简洁,特别是在多态性(样式调用)方面。super

缺点:

我认为有一个有问题的设计(实际上有助于上面的第2点),这与阴影有关:

在行为委托中,如果可能的话,我们避免在链的不同级别上将事物命名为相同。[[Prototype]]

这背后的想法是,对象有自己更具体的函数,然后在内部委托给链下部的函数。例如,您可能有一个对象,该对象上有一个函数,该函数将对象的 JSON 版本发送到服务器,但您可能还有一个具有函数的对象,该函数首先删除不应发送到服务器的属性。resourcesave()clientResourcestripAndSave()

潜在的问题是:如果其他人出现并决定制作一个对象,而没有完全了解整个原型链,他们可能会合理地*决定在名为 的属性下保存一个时间戳,该属性将对象上的基本功能隐藏在原型链的两个链接上:specialResourcesavesave()resource

var resource = {
  save: function () { 
    console.log('Saving');
  }
};

var clientResource = Object.create(resource);

clientResource.stripAndSave = function () {
  // Do something else, then delegate
  console.log('Stripping unwanted properties');
  this.save();
};

var specialResource = Object.create( clientResource );

specialResource.timeStampedSave = function () {
  // Set the timestamp of the last save
  this.save = Date.now();
  this.stripAndSave();
};

a = Object.create(clientResource);
b = Object.create(specialResource);

a.stripAndSave();    // "Stripping unwanted properties" & "Saving".
b.timeStampedSave(); // Error!

这是一个特别人为的例子,但关键是,特别是跟随其他属性可能会导致一些尴尬的情况和大量使用同义词库!

也许更好的说明是一种方法 - 特别令人心酸,因为OOLO回避构造函数类型函数。由于每个相关对象都可能需要这样的函数,因此适当地命名它们可能是一个繁琐的练习,并且唯一性可能会使人们难以记住要使用哪个。init

*实际上它不是特别合理(lastSaved会好得多,但这只是一个例子。