Kyle Simpson's OLOO Pattern vs Prototype Design Pattern
凯尔·辛普森的"对象链接到其他对象"模式与原型设计模式有什么不同吗?除了通过特别指出"链接"(原型的行为)并澄清这里没有"复制"(类的行为)的东西创造它之外,他的模式到底引入了什么?
下面是凯尔在他的书《你不知道JS:这个&对象原型》中的模式示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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." |
what exactly does his pattern introduce?
oloo embraces the prototype is as -链层,没有其他的在线needing(海事组织confusing to get the)语义的连杆。P></
我知道,这些代码片断have the same双精确的结果,但differently get there。P></
形态:构造函数P></
1 2 3 4 5 6 7 8 9 | 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形态:P></
1 2 3 4 5 6 7 | var FooObj = { y: 11 }; var BarObj = Object.create(FooObj); BarObj.z = 31; var x = Object.create(BarObj); x.y + x.z; // 42 |
在两个代码片断,安
茶是identical between the代表关系和代码片断。the memory usage is between the identical代码片断。"to the many children"创造能力(又名多类对象,通过
我argue oloo offers is that这是多简单的内部对象和directly to the‘Express链接链接indirectly them to them,比一
oloo is just切割出中间人。P></
这是一oloo?
凯尔的读书,发现它真的和不对称的,particularly the detail how is about
对我来说,有一定oloo大学院:学院夫妇P></1。简单
在线oloo relies
arguable this is,but is the syntax感觉oloo(many cases)neater简明和更多的方法比标准的JavaScript,它是particularly when to子的多态性(在
我认为有问题的位设计学院there is one(真的那一个点contributes to 2以上),is to do with,shadowing:P></
In behaviour delegation, we avoid if at all possible naming things the same at different levels of the
[[Prototype]] chain.
This is that the背后的想法,他们自己有特异功能的对象,那么更多的internally to which the chain下唐氏函数的代表。for example,You might have a
潜在的问题是:"如果在与人*来决定让对象在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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! |
This is a particularly contrived example is that,but not the other shadowing点专门开发CAN to some properties和重金属铅的尴尬情况使用的词库。P></
也许美好的摇篮》插图of this as an
< > *子其实是不合理的(好的多particularly
在"You don't know JS:This&object prototype"中的讨论和Oloo的演示都是发人深省的,我在这本书中学到了很多东西。其他答案中很好地描述了Oloo模式的优点;但是,我对它有以下宠物投诉(或者缺少一些阻止我有效应用它的东西):
一
当"class"继承经典模式中的"another"class时,可以将这两个函数声明为类似的语法("function declaration"或"function statement"):
1 2 3 4 5 6 7 8 9 10 11 | function Point(x,y) { this.x = x; this.y = y; }; function Point3D(x,y,z) { Point.call(this, x,y); this.z = z; }; Point3D.prototype = Object.create(Point.prototype); |
相反,在oloo模式中,用于定义基对象和派生对象的不同语法形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 | var Point = { init : function(x,y) { this.x = x; this.y = y; } }; var Point3D = Object.create(Point); Point3D.init = function(x,y,z) { Point.init.call(this, x, y); this.z = z; }; |
正如您在上面的示例中看到的,可以使用对象文字表示法定义基对象,而不能对派生对象使用相同的表示法。这种不对称让我心烦。
二
在oloo模式中,创建对象有两个步骤:
调用一些自定义的、非标准的方法来初始化对象(必须记住,因为对象可能因对象而异):
1 2 3 | var p2a = Object.create(Point); p2a.init(1,1); |
相反,在原型模式中,您使用标准运算符
1 | var p2a = new Point(1,1); |
三
在经典模式中,我可以创建不直接应用于"即时"的"静态"实用程序函数,方法是将它们直接分配给"类"函数(而不是它的
1 2 3 4 5 | Point.square = function(x) {return x*x;}; Point.prototype.length = function() { return Math.sqrt(Point.square(this.x)+Point.square(this.y)); }; |
相反,在oloo模式中,任何"静态"功能(通过[[原型]]链)在对象实例上也可用:
1 2 3 4 5 6 7 8 | var Point = { init : function(x,y) { this.x = x; this.y = y; }, square: function(x) {return x*x;}, length: function() {return Math.sqrt(Point.square(this.x)+Point.square(this.y));} }; |
"I figured to do it makes each obj dependent on the other"
当两个物体都是EDOCX1&0时取决于其他人;Instead they are individual object.你是链接一号对方用
1 2 3 4 5 6 7 8 9 10 | var foo= {}, bar= Object.create(foo), baz= Object.create(bar); console.log(Object.getPrototypeOf(foo)) //Object.prototype console.log(Object.getPrototypeOf(bar)) //foo console.log(Object.getPrototypeOf(baz)) //bar |
现在想想第二个你认为依靠别人吗?
现在让我们做同样的
ZZU1
唯一的差别是在一个侧面通过任意对象将BBJOCS连接到每一个对象但在前一个(EDOCX1&15)中,它们是直接连接的。这两种方式,你只是把
1 2 | var anotherObj= {}; Object.setPrototypeOf(foo, anotherObj); |
所以他们都是彼此独立的
BLCK1/
是的,这是不可能的。
作为一个有用的对象
1 2 3 4 5 6 | var Tech= { tag:"technology", setName= function(name) { this.name= name; } } |
创建许多对象,如你希望链接到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | var html= Object.create(Tech), css= Object.create(Tech), js= Object.create(Tech); Some checking (avoiding console.log)- html.isPrototypeOf(css); //false html.isPrototypeOf(js); //false css.isPrototypeOf(html); //false css.isPrototypeOf(js); //false js.isPrototypeOf(html); //false js.isPrototypwOf(css); //false Tech.isPrototypeOf(html); //true Tech.isPrototypeOf(css); //true Tech.isPrototypeOf(js); //true |
你认为这些物体相互连接吗?不,他们没有。现在让我们看看我们是如何做到这一点的。
1 2 3 4 5 6 7 | function Tech() { } Tech.prototype.tag="technology"; Tech.prototype.setName= function(name) { this.name= name; } |
创建许多对象,如你希望链接到
1 2 3 | var html= new Tech(), css= new Tech(), js= new Tech(); |
一些检查(避开控制台.log)--
1 2 3 4 5 6 7 8 9 10 11 12 | html.isPrototypeOf(css); //false html.isPrototypeOf(js); //false css.isPrototypeOf(html); //false css.isPrototypeOf(js); //false js.isPrototypeOf(html); //false js.isPrototypeOf(css); //false Tech.prototype.isPrototypeOf(html); //true Tech.prototype.isPrototypeOf(css); //true Tech.prototype.isPrototypeOf(js); //true |
你觉得这些东西怎么样?不同的对象来自于事实上,他们为同一目的服务。样式一个对象代表
"As is, ObjB has to be created from ObjA.. Object.create(ObjB) etc"
这里不像是一个经典语言的论坛。法国电力公司Object is made delegate to
@Marcus@Bholben
也许我们可以做类似的事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | const Point = { statics(m) { if (this !== Point) { throw Error(m); }}, create (x, y) { this.statics(); var P = Object.create(Point); P.init(x, y); return P; }, init(x=0, y=0) { this.x = x; this.y = y; } }; const Point3D = { __proto__: Point, statics(m) { if (this !== Point3D) { throw Error(m); }}, create (x, y, z) { this.statics(); var P = Object.create(Point3D); P.init(x, y, z); return P; }, init (x=0, y=0, z=0) { super.init(x, y); this.z = z; } }; |
Of course,creating a point3d object that links to the protototype of a point2d object i s kind of silly,but that's beside the point(I wanted to be consistent with your example).无论如何,只要抱怨继续:
该不对称可以与ES6的对象固定。我用的是我们也可以使用超级标准对象,如同在
1 2 3 | const Point3D = Object.assign(Object.create(Point), { ... } |
虽然我不喜欢语法
我们总可以把
1 2 3 4 | var b = Point3D.create(1,2,3); console.log(b); // { x:1, y:2, z:3 } console.log(Point.isPrototypeOf(b)); // true console.log(Point3D.isPrototypeOf(b)) // true |
我只是用这个电池来模拟奥卢的静态方法。我不确定我喜欢还是不喜欢它要求在任何"静态"方法的顶端有一个特殊性能。例如,我制作了
1 2 | var p = Point.create(1,2); var q = p.create(4,1); // Error! |
另一方面,ES6符号可以安全扩展Javascript Base Classes。所以你可以保存自己的一些代码,并确定对象的特殊性能。For example,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | const extendedJS = {}; ( function(extension) { const statics = Symbol('static'); Object.defineProperty(Object.prototype, statics, { writable: true, enumerable: false, configurable: true, value(obj, message) { if (this !== obj) throw Error(message); } }); Object.assign(extension, {statics}); })(extendedJS); const Point = { create (x, y) { this[extendedJS.statics](Point); ... |
@詹姆斯·伊曼农——所以,你指的是多重继承(在第75页的书"你不知道JS:这个&对象原型")。我们可以在下划线的"extend"函数中找到这个机制。您在示例中所述对象的名称有点混合了苹果、桔子和糖果,但我理解其背后的含义。根据我的经验,这将是oolo版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | var ObjA = { setA: function(a) { this.a = a; }, outputA: function() { console.log("Invoking outputA - A:", this.a); } }; // 'ObjB' links/delegates to 'ObjA' var ObjB = Object.create( ObjA ); ObjB.setB = function(b) { this.b = b; } ObjB.setA_B = function(a, b) { this.setA( a ); // This is obvious. 'setA' is not found in 'ObjB' so by prototype chain it's found in 'ObjA' this.setB( b ); console.log("Invoking setA_B - A:", this.a," B:", this.b); }; // 'ObjC' links/delegates to 'ObjB' var ObjC = Object.create( ObjB ); ObjC.setC = function(c) { this.c = c; }; ObjC.setA_C = function(a, c) { this.setA( a ); // Invoking 'setA' that is clearly not in ObjC shows that prototype chaining goes through ObjB all the way to the ObjA this.setC( c ); console.log("Invoking setA_C - A:", this.a," C:", this.c); }; ObjC.setA_B_C = function(a, b, c){ this.setA( a ); // Invoking 'setA' that is clearly not in ObjC nor ObjB shows that prototype chaining got all the way to the ObjA this.setB( b ); this.setC( c ); console.log("Invoking setA_B_C - A:", this.a," B:", this.b," C:", this.c); }; ObjA.setA("A1"); ObjA.outputA(); // Invoking outputA - A: A1 ObjB.setA_B("A2","B1"); // Invoking setA_B - A: A2 B: B1 ObjC.setA_C("A3","C1"); // Invoking setA_C - A: A3 C: C1 ObjC.setA_B_C("A4","B2","C1"); // Invoking setA_B_C - A: A4 B: B2 C: C1 |
这是一个简单的例子,但所显示的点是,我们只是以相当平坦的结构/形式将对象链接在一起,并且仍然有可能使用来自多个对象的方法和属性。我们实现了与使用class/"copying the properties"方法相同的事情。由Kyle总结(第114页,"此对象原型"):
In other words, the actual mechanism, the essence of what’s important
to the functionality we can leverage in JavaScript, is all about objects
being linked to other objects.
我理解您更自然的方法是在一个位置/函数调用中声明所有"parent"(小心:)对象,而不是建模整个链。
它需要的是在我们的应用程序中根据这一点进行思考和建模问题的转换。我也逐渐习惯了。希望这有帮助,凯尔人自己的最终裁决会很好。:)
有没有一种方法可以将两个以上的对象我所用的所有示例都是基于示例的(请参阅op的示例)。假设我们有以下对象,我们如何创建一个"第四"对象,它具有"其他"三个属性?阿拉巴马州
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var Button = { init: function(name, cost) { this.buttonName = name; this.buttonCost = cost; } } var Shoe = { speed: 100 } var Bike = { range: '4 miles' } |
这些对象是任意的,可以包含各种行为。但要点是,我们有n个对象,我们的新对象需要这三个对象中的某个。
而不是给出的例子ala:
1 2 | var newObj = Object.create(oneSingularObject); newObj.whatever.. |
但是,我们的新对象=(按钮,自行车,鞋子)……
在奥洛奥,这是什么样的模式?
@马库斯,和你一样,我对奥洛很感兴趣,也不喜欢你第一点所描述的不对称性。我一直在玩抽象来恢复对称性。您可以创建一个用于替代
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var Point = { init : function(x,y) { this.x = x; this.y = y; } }; var Point3D = link(Point, { init: function(x,y,z) { Point.init.call(this, x, y); this.z = z; } }); |
记住,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function link(delegate, props, propsConfig) { props = props || {}; propsConfig = propsConfig || {}; var obj = {}; Object.keys(props).forEach(function (key) { obj[key] = { value: props[key], enumerable: propsConfig.isEnumerable || true, writable: propsConfig.isWritable || true, configurable: propsConfig.isConfigurable || true }; }); return Object.create(delegate, obj); } |
当然,我认为@kyle不会支持在point3d对象中隐藏