Kyle Simpson OLOO Model vs. Prototype Design Pattern

Kyle Simpson "OLOO (objects associated with other objects)" Pattern "is in any way different from the Prototype design pattern? Besides faking it in that it specifically points to" binding "(prototype behavior) and clarifies what is here there is no “copying” (class behavior), what exactly does its template introduce?

Here's an example of Kyle's template from his book "You Do not Know JS: This and 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." 
+102
javascript design-patterns
Apr 22 '15 at 5:05
source share
8 answers

what exactly does its template introduce?

OLOO covers the prototype chain as it is, without the need to impose semantics on another (IMO confusing) semantics to get the connection.

So, these two fragments have EXACTLY the same result, but act differently.

Constructor form:

 function Foo() {} Foo.prototype.y = 11; function Bar() {} Bar.prototype = Object.create(Foo.prototype); Bar.prototype.z = 31; var x = new Bar(); xy + xz; // 42 

OLOO form:

 var FooObj = { y: 11 }; var BarObj = Object.create(FooObj); BarObj.z = 31; var x = Object.create(BarObj); xy + xz; // 42 

In both fragments, the object x [[Prototype]] attached to the object ( Bar.prototype or BarObj ), which, in turn, is associated with the third object ( Foo.prototype or FooObj ).

Relations and delegation are identical between fragments. Memory usage is the same between fragments. The ability to create many “children” (for example, many objects, such as x1 through x1000 , etc.) is identical between fragments. Delegation performance ( xy and xz ) is identical between fragments. Object creation performance is slower with OLOO, but a health check, which shows that lower performance is not really a problem.

What I affirm in the OLOO sentences is that it is much easier to simply express objects and directly bind them than to indirectly bind them to the / new constructor mechanisms. The latter claims to be classes, but it’s actually just a scary syntax for expressing delegation ( side note: so the syntax is ES6 class !).

OLOO just cuts out the average person.

Here's another comparison of class vs OLOO.

+143
May 26 '15 at 20:48
source share

I read Kyle's book, and I found it very informative, especially in detail about how this attached.

Pros:

For me there are a couple of big pluses of OLOO:

1. Simplicity

OLOO relies on Object.create() to create a new object that [[prototype]] is associated with another object. You do not need to understand that functions have a prototype property or are worried about any possible related errors that result from modifying it.

2. Pure syntax

This can be argued, but I believe that the OLOO syntax (in many cases) is more accurate and more concise than the “standard” JavaScript approach, especially when it comes to polymorphism ( super line calls).

Minuses:

I think there is one dubious bit of design (one that actually contributes to paragraph 2 above), and this is due to tenerization:

In the matter of delegating behavior, we avoid if, for any possible name, things are the same at different levels of the [[prototype]] chain.

The idea is that objects have their own more specific functions, which then internally delegate functions down the chain. For example, you might have a resource object with a save() function on it that sends the JSON server version to the server, but you can also have a clientResource object that has a stripAndSave() function that first removes properties that should not be sent to the server .

The potential problem is this: if someone else comes and decides to make a specialResource object specialResource being fully aware of the whole prototype chain, they can intelligently * decide to save the timestamp for the last save under the save property, which shades the basic save() functionality of the resource object with two ties down the prototype chain:

 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". a.timeStampedSave(); // Error! 

This is a particularly contrived example, but the fact is that deliberately not obscuring other properties can lead to some uncomfortable situations and intensive use of the thesaurus!

Perhaps the best illustration of this might be the init method, which is especially sharp, like functions like the OOLO sidesteps constructor. Since each connected object most likely needs such a function, it can be a tedious exercise to name them appropriately, and uniqueness can make it difficult to remember what to use.

* Actually, this is not particularly reasonable ( lastSaved will be much better, but this is just an example.)

+24
Aug 10 '15 at 11:25
source share

The discussion in “You Do Not Know JS: This and Object Prototypes” and the OLOO presentation are thought provoking, and I recognized a ton passing through the book. The benefits of the OLOO template are well described in other answers; however, I have the following food complaints with him (or I missed something that prevents me from applying it effectively):

one

When a "class" inherits a "different" class "in a classic template, two functions can be declared with the same syntax ( " function declaration "or" function instruction " ):

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

In contrast, the OLOO template has different syntactic forms used to define the base and derived objects:

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

As you can see in the above example, the base object can be defined using object notation, while the same notation cannot be used for a derived object. This asymmetry bothers me.

2

In the OLOO template, object creation is performed in two stages:

  • call Object.create
  • call some customizable, non-standard method to initialize the object (which you must remember, since it can vary from one object to another):

      var p2a = Object.create(Point); p2a.init(1,1); 

In contrast, in the Prototype template, you use the standard new operator:

 var p2a = new Point(1,1); 

3

In the classic template, I can create “static” utility functions that do not apply directly to the “instantaneous” function, assigning them directly to the “class” function (as opposed to its .prototype ). For example. like the square function in the code below:

 Point.square = function(x) {return x*x;}; Point.prototype.length = function() { return Math.sqrt(Point.square(this.x)+Point.square(this.y)); }; 

In contrast, any “static” functions are available in the OLOO template (via the [[prototype]] chain):

 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));} }; 
+13
Nov 05 '15 at 18:32
source share

"I decided to do this so that each object depended on another"

As Kyle explains, when two objects are connected [[Prototype]] , they are not really dependent on each other; instead, they are an individual object. You link one object to another using the [[Prototype]] link, which you can change at any time. If you take two [[Prototype]] related objects created in OLOO style as dependent on each other, you should also think of those created through constructor calls.

 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 

Now think, do you think foo bar and baz depend on each other?

Now let's do the same constructor style code -

 function Foo() {} function Bar() {} function Baz() {} Bar.prototype= Object.create(Foo); Baz.prototype= Object.create(Bar); var foo= new Foo(), bar= new Bar(). baz= new Baz(); console.log(Object.getPrototypeOf(foo)) //Foo.prototype console.log(Object.getPrototypeOf(Foo.prototype)) //Object.prototype console.log(Object.getPrototypeOf(bar)) //Bar.prototype console.log(Object.getPrototypeOf(Bar.prototype)) //Foo.prototype console.log(Object.getPrototypeOf(baz)) //Baz.prototype console.log(Object.getPrototypeOf(Baz.prototype)) //Bar.prototype 

The only difference b / w of the last and previous code is that in the last foo , bar , baz bbjects are connected to each other through arbitrary objects of their constructor function ( Foo.prototype , Bar.prototype , Baz.prototype ), but in the first ( OLOO style) they are directly related. In both ways, you simply bind foo , bar , baz each other, directly in the first and indirectly in the last. But in both cases, the objects are independent of each other, because it does not look like an instance of any class that, after creating the instance, cannot be inherited from any other class. You can always change which object should delegate the object.

 var anotherObj= {}; Object.setPrototypeOf(foo, anotherObj); 

Therefore, they are all independent of each other.

"I was hoping OLOO would solve a problem in which every object knows nothing about the other."

Yes, what is really possible -

Let me use Tech as a utility object -

  var Tech= { tag: "technology", setName= function(name) { this.name= name; } } 

create as many objects that you want to associate with Tech -

 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 

Do you think that html , css , js objects are related to each other? No, it is not. Now let's see how we could do this using the constructor function -

 function Tech() { } Tech.prototype.tag= "technology"; Tech.prototype.setName= function(name) { this.name= name; } 

create as many objects you want to associate with Tech.proptotype -

 var html= new Tech(), css= new Tech(), js= new Tech(); 

Some check (excluding console.log) -

 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 

Do you think these constructor style objects ( html , css , js ) Objects are different from OLOO style code? In fact, they serve the same purpose. In OLOO build one object to delegate to Tech (delegation was specified explicitly), and in constructor - create one object to delegate to Tech.prototype (delegation was implicitly specified). In the end, you have a relationship between three objects that are not related to each other, with one object, directly using the OLOO style, indirectly using the constructor style.

"As is, ObjB must be created from ObjA .. Object.create (ObjB), etc.

No, ObjB is not like an instance (in classical languages) of any ObjA class. We can say that how an ObjB object is passed to the ObjA object when it is created time . " If you used the constructor, you would have made the same“ link ”, albeit indirectly, using .prototype s.

+5
Mar 16 '16 at 20:20
source share

@Marcus @bholben

Perhaps we can do something like this.

  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 associated with a prototype Point2D object seems silly, but it is not (I wanted to be in line with your example). In any case, if complaints go:

  • Asymmetry can be fixed with ES6 Object.setPrototypeOf or the more I am on __proto__ = ... We can also use super for regular objects, as shown in Point3D.init() . Another way is to do something like

     const Point3D = Object.assign(Object.create(Point), { ... } 

    although I don’t particularly like the syntax.




  1. We can always just p = Object.create(Point) and then p.init() in the constructor. e.g. Point.create(x,y) . Using the code above, we can create an instance of Point3D as follows.

     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. I just came up with this hack for emulating static methods in OLOO. I'm not sure if I like it or not. This requires calling a special property at the top of any "static" methods. For example, I made a static Point.create() method.

        var p = Point.create(1,2); var q = p.create(4,1); // Error! 

Alternatively, with ES6 Symbols, you can safely extend Javascript base classes. Thus, you can save code and define a special property Object.prototype. For example,

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



+3
Nov 12 '16 at 6:13
source share

@james emanon - So you mean multiple inheritance (see page 75 in the book “You Do not Know JS: This and Object Prototypes”). And we can find this mechanism, for example, in the underline function "stretch". The names of the object that you specified in your example are a bit of mixing apples, oranges and sweets, but I understand the essence of this. In my experience, this would be the OOLO version:

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

This is a simple example, but the point indicated is that we simply combine the object together into a fairly flat structure / formation and still have the ability to use methods and properties from several objects. We achieve the same as with the class / "copy properties". Summarized by Kyle (p. 114, "This and Object Prototypes"):

In other words, the real mechanism, the essence of the important functionality that we can use in JavaScript, is all about objects associated with other objects .

I understand that a more natural way for you would be to specify all the "parent" (careful :)) objects in one place / function, but rather to simulate the whole chain.

This requires a shift in thinking and modeling problems in our applications in accordance with this. I'm also used to it. Hope this helps, and the final verdict from Kyle himself will be great. :)

+2
Nov 06 '15 at 9:03
source share

@Marcus, like you, I was fond of OLOO, and also did not like asymmetry, as described in your first paragraph. I played with abstraction to restore symmetry. You can create a link() function that is used instead of Object.create() . When used, your code might look something like this:

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

Remember that Object.create() has a second parameter that can be passed. Here is a link function that uses the second parameter. It also allows you to customize a bit ...

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

Of course, I think @Kyle does not approve of the shading of the init() function in a Point3D object .; -)

-one
Nov 06 '15 at 3:29
source share

Is there an OLO way more than two objects. all examples I consisted of a based example (see the OP example). Suppose we had the following objects, how can we create a "fourth" object with the attributes of the "other" three? ala ...

 var Button = { init: function(name, cost) { this.buttonName = name; this.buttonCost = cost; } } var Shoe = { speed: 100 } var Bike = { range: '4 miles' } 

these objects are arbitrary and can cover all kinds of behavior. But the bottom line is that we have an "n" number of objects, and our new object needs one of all three.

instead of the above ala examples:

 var newObj = Object.create(oneSingularObject); newObj.whatever.. 

BUT, our new Object = (button, bike, shoe) ......

What is the template to get this in OLOO?

-one
Nov 06 '15 at 6:59
source share



All Articles