[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.javascript

Comparing some of the ways to create objects

John Harris

4/3/2016 3:09:00 PM


It's time to compare ways of creating objects now that the full
release version of Firefox implements class declarations. The code
below shows three of the ways. They are all ones where an object is
created by using new and a constructor. These ways are more suited to
cases where several objects of the same kind are to be created, or
where the code might be reused in other projects. Several other ways
exist but they are more suited to the ad hoc creation of only one
object.

The three examples illustrate :
1 Class-style creation : Using an ES6 class declaration;
2 Class-style creation : Using ES3 features;
3 Prototypal-style creation : Using ES3 features.


Remarks

Concerning way 1 and 2
When the constructors in 1 and 2 are called with the same arguments
they construct two objects with the same properties. (For nit-pickers,
some names and constituent objects are different but only because the
two examples here live in the same program). It would be silly to
insist that one of the two objects belongs to a class but the other
object does not.

Concerning way 3
In a true prototypal language new objects are created by cloning a
prototype (hence the name prototypal). In case 3 cloning is emulated
by the created object pointing to the prototype. Each property of the
created object is implemented in the prototype until it is updated; it
then becomes an own-property of the object. This has the unfortunate
consequence that updating the prototype changes the initial values of
the properties that were given to objects created earlier. Any
properties not updated since then will now have a changed initial
value. This effect is not well controlled as it depends on what
accesses have been done to each object. ES6 has a clone function but
it does not act on the copied object's prototype chain. Using it would
make the behaviour even more confusing.

Concerning way 1, 2, and 3
Note that in all three ways the prototype object is not of the same
kind as the objects it helps to create. The prototype object has a
different prototype chain and its properties have no invariants
(constraints on property values).

Concerning way 1, 2, and 3
The three ways create objects that do much the same job. Which way is
best in general is a matter of personal preference. There is no reason
for loud and dogmatic assertions that one way is the Only True Way.

In some programs there can be reasons for not being able to use a
particular way :
if the code must run in pre-ES6 implementations;
if the prototype will be altered after objects have been created;
if it matters which object properties are own-properties.


John


<!- ========================================================== ->

<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd...

<html>
<head>
<title> Some OO styles emulated in ECMAScript </title>
<style>
Body { font-family: Arial, Helvetica, sans-serif; }
P { margin-left: 5%; margin-bottom: 3em; }
</style>
</head>

<body>
<h1> Some OO styles emulated in ECMAScript </h1>
<p> The examples here show some of the ways that objects can be
created. <br>
They concentrate on ways useful for creating several objects of
the same kind.

<h2> 1 Class-style creation : Using an ES6 class declaration </h2>
<p>
<SCRIPT type="text/javascript">

// Declare class Thing1
class Thing1
{
constructor(phrase)
{
this.count = 0; // Counter to be updated; initial value 0
this.text = phrase; // Phrase to be displayed
}
incr() { this.count++; }
toString() { return this.text; }

} // Thing1

// Use Thing1 objects
var a1 = new Thing1("Hello from Thing1 a1");
document.writeln( a1 + " : " + a1.count + "<br>" );
a1.incr();
document.writeln( a1 + " : " + a1.count + "<br>" );

var b1 = new Thing1("Hello from Thing1 b1");
document.writeln( b1 + " : " + b1.count + "<br>" );

</SCRIPT>


<h2> 2 Class-style creation : Using ES3 features </h2>
<p>
<SCRIPT type="text/javascript">

// Set up Thing2 constructor
{
// Constructor
function Thing2(phrase)
{
this.count = 0; // Counter to be updated; initial value 0
this.text = phrase; // Phrase to be displayed
}

// Prototype
Thing2.prototype.incr = function() { this.count++; }
Thing2.prototype.toString = function() { return this.text; }

} // Thing2

// Use Thing2 objects
var a2 = new Thing2("Hello from Thing2 a2");
document.writeln( a2 + " : " + a2.count + "<br>" );
a2.incr();
document.writeln( a2 + " : " + a2.count + "<br>" );

var b2 = new Thing2("Hello from Thing2 b2");
document.writeln( b2 + " : " + b2.count + "<br>" );

</SCRIPT>


<h2> 3 Prototypal-style creation : Using ES3 features </h2>
<p>
<SCRIPT type="text/javascript">

// Set up Thing3 constructor
{
// Constructor
// Only properties that don't have a fixed initial value
// are updated
function Thing3(phrase)
{
this.text = phrase;
}

// Prototype
Thing3.prototype.count = 0; // Fixed initial value
Thing3.prototype.text = ""; // Dummy value. Is documentation
Thing3.prototype.incr = function() { this.count++; }
Thing3.prototype.toString = function() { return this.text; }

} // Thing3

// Use Thing3 objects
var a3 = new Thing3("Hello from Thing3 a3");
document.writeln( a3 + " : " + a3.count + "<br>" );
a3.incr();
document.writeln( a3 + " : " + a3.count + "<br>" );

var b3 = new Thing3("Hello from Thing3 b3");
document.writeln( b3 + " : " + b3.count + "<br>" );

</SCRIPT>



<p>
&copy; Copyright J G Harris, 2016
</body>
</html>

<!- ========================================================== ->

25 Answers

Scott Sauyet

4/4/2016 4:15:00 AM

0

John Harris wrote:

> It's time to compare ways of creating objects now that the full release
> version of Firefox implements class declarations. The code below shows
> three of the ways. They are all ones where an object is created by using
> new and a constructor.

I don't see why that is so special. I will add several more suggestions
below that use three different object creation APIs, including a 'new-and-
constructor' version. Two of them also share the same sort of prototype-
based object mechanism shared by your three samples.

> These ways are more suited to cases where several
> objects of the same kind are to be created, or where the code might be
> reused in other projects. Several other ways exist but they are more
> suited to the ad hoc creation of only one object.

I believe all three techniques I suggest below are similarly suited to
the creation of several objects of the same kind. The second one
(`thing5`) is more memory-intensive, and probably is not suitable for
hundreds of thousands or millions of instances of the type, but other
than that, they have similar characteristics. `thing5` does offer the
ability to have truly private implementation details, which many other
techniques really don't.

> The three examples illustrate :
> 1 Class-style creation : Using an ES6 class declaration;
> 2 Class-style creation : Using ES3 features;
> 3 Prototypal-style creation : Using ES3 features.

These three also illustrate standard prototypal creation, regardless of
whether some syntactic sugar has been added to help make them seem more
similar to C++-style OOP classes.

> [ ... code elided ... ]

My techniques follow. I'm wondering if you see these ones as somehow
lesser techniques than the three you present. And if so, why is that?

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

function Thing4(phrase) {
var count = 0;
this.toString = function() {return phrase;}
this.incr = function() {count++;}
Object.defineProperties(this, {text: {
get: function() {return phrase;}
}, count: {
get: function() {return count;}
}})
} // Thing4

var a4 = new Thing4("Hello from Thing4 a4");
a4 + ': ' + a4.count; //=> "Hello from Thing4 a4: 0"
a4.incr();
a4 + ': ' + a4.count; //=> "Hello from Thing4 a4: 1"
var b4 = new Thing4("Hello from Thing4 b4");
b4 + ': ' + b4.count; //=> "Hello from Thing4 b4: 0"

function thing5(phrase) {
var count = 0;
return {
toString: function() {return phrase;},
incr: function() {count++},
get count() {return count;}
};
} // thing5

var a5 = thing5("Hello from thing5 a5");
a5 + ': ' + a5.count; //=> "Hello from thing5 a5: 0"
a5.incr();
a5 + ': ' + a5.count; //=> "Hello from thing5 a5: 1"
var b5 = thing5("Hello from thing5 b5");
b5 + ': ' + b5.count; //=> "Hello from thing5 b5: 0"


var Thing6 = {
init: function(phrase) {
this.text = phrase;
this.count = 0;
},
incr: function() {
this.count++;
},
toString: function() {
return this.text ;
}
}; // Thing6

var a6 = Object.create(Thing6);
a6.init("Hello from Thing6 a6");
a6 + ': ' + a6.count; //=> "Hello from Thing6 a6: 0"
a6.incr();
a6 + ': ' + a6.count; //=> "Hello from Thing6 a6: 1"
var b6 = Object.create(Thing6);
b6.init("Hello from Thing6 b6");
b6 + ': ' + b6.count; //=> "Hello from Thing6 b6: 0"

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Cheers,

-- Scott

ram

4/4/2016 5:10:00 AM

0

Scott Sauyet <scott@sauyet.com> writes:
>John Harris wrote:
>> The three examples illustrate :
>> 1 Class-style creation : Using an ES6 class declaration;
>> 2 Class-style creation : Using ES3 features;
>> 3 Prototypal-style creation : Using ES3 features.
>These three also illustrate standard prototypal creation

The subject should had better been »ways to create /ways to
create objects/« (repetition intended). /Objects/ of these
ways then can be created via »Object.create« (when there is
a prototype) or »new« (when there is a constructor) or just
a function call (when there is a factory function).

(Of course, prototypes, constructors and factory functions
are objects too, but we see them more as ways to create
other objects.)

And yes, when it comes to the definition of ways to create
objects (like prototypes, constructors or factory functions)
there are very very many ways in JavaScript to choose from,
and I'm not sure whether adding even more ways (»class«) helps.

Stefan Weiss

4/4/2016 1:21:00 PM

0

Scott Sauyet wrote:
> John Harris wrote:
>
>> It's time to compare ways of creating objects now that the full release
>> version of Firefox implements class declarations. The code below shows
>> three of the ways. They are all ones where an object is created by using
>> new and a constructor.
>
> I don't see why that is so special.

The special thing is that it provides inheritance. The new `class` syntax
doesn't add anything that wasn't possible before, the syntactic sugar just
makes it look nicer. Other types of object creation are equally valid, and
often preferable, but like the things 5 and 6 in your examples, they don't
have "is-a" semantics built in. This may or may not be a desirable feature,
depending on the circumstances (Thing6 could be adjusted to make this work).

> I will add several more suggestions
> below that use three different object creation APIs, including a 'new-and-
> constructor' version. Two of them also share the same sort of prototype-
> based object mechanism shared by your three samples.
>
>> These ways are more suited to cases where several
>> objects of the same kind are to be created, or where the code might be
>> reused in other projects. Several other ways exist but they are more
>> suited to the ad hoc creation of only one object.
>
> I believe all three techniques I suggest below are similarly suited to
> the creation of several objects of the same kind. The second one
> (`thing5`) is more memory-intensive, and probably is not suitable for
> hundreds of thousands or millions of instances of the type, but other
> than that, they have similar characteristics. `thing5` does offer the
> ability to have truly private implementation details, which many other
> techniques really don't.

I think it's unfortunate that ES2015 introduced a new incompatible `class`
syntax but missed the chance to address the very common need for private
data properties (or any declared properties, for that matter). Something
like that might be added in a future expansion of the standard, but
currently there are only stage 0 and stage 1 proposals. I found two relevant
ones; neither of them is included in the prospective feature set for ES2017:
https://github.com/jeffmo/es-class-fields-and-static-...
https://github.com/wycats/javascript-pri...

There are workarounds: two well-established patterns (using naming
conventions and adding per-instance methods in the constructor) and two new
methods involving WeakMaps and Symbols. Maybe we should add those to the
list, as long as we're doing comparisons.

Things 4 and 5 in your examples support private data through the use of
per-instance methods, but (as you said) this comes at a price:

rss heapUsed heapTotal (ms)

Thing1 49.76 21.80 47.24 64
Thing2 49.76 21.80 47.24 65
Thing3 49.44 20.00 47.24 67
Thing4 170.85 142.51 166.32 990
thing5 181.99 160.44 178.13 425
Thing6 49.84 25.28 48.22 108

The numbers are memory usage in MB after creating 200k objects (using
Node.js 5.8.0 on my laptop; the usual caveats about platform-dependent
benchmarks and measurements apply).

> John Harris wrote:
>> Concerning way 1, 2, and 3
>> The three ways create objects that do much the same job. Which way is
>> best in general is a matter of personal preference. There is no reason
>> for loud and dogmatic assertions that one way is the Only True Way.

Well said.


- stefan

John Harris

4/4/2016 3:48:00 PM

0

On Mon, 4 Apr 2016 04:15:10 -0000 (UTC), Scott Sauyet
<scott@sauyet.com> wrote:

>John Harris wrote:
<snip>
>> The three examples illustrate :
>> 1 Class-style creation : Using an ES6 class declaration;
>> 2 Class-style creation : Using ES3 features;
>> 3 Prototypal-style creation : Using ES3 features.
>
>These three also illustrate standard prototypal creation, regardless of
>whether some syntactic sugar has been added to help make them seem more
>similar to C++-style OOP classes.
<snip>

I'm afraid I disagree with you there.

First, in a full-on prototypal language a new object is a complete
copy of the prototype. If the creation process adds further data
fields to the object, as in 1 & 2, then it creates a new prototype for
objects that inherit from the first prototype. That's why in example 3
the prototype has a redundant, unused, property.

Second, let's look at a typical C++ implementation. Each object
contains a pointer to a structure shared with all objects belonging to
the same class. The shared structure contains pointers to the class's
method functions. On seeing
a.oink();
the compiler generates code that follows the pointer to the shared
structure, picks out the pointer it knows points to the oink function,
follows that pointer and calls the oink function. One of the call
arguments is a pointer to the object (i.e 'this') so the method knows
what to work on. The 'this' argument is automatic, not defined in the
function definition.

Now look at ECMAScript. Each object contains a pointer to a structure
shared with all objects of the same kind (or null - programmer's
choice). The structure holds pointers to method functions (or not -
programmer's choice). Each method call automatically includes a 'this'
argument, held in a location separate from the user's arguments.

The two languages are remarkably similar. By your definition C++ is a
prototypal language with class declarations bolted on for programmers'
convenience.

ECMAScript is a very flexible language. Most things can be done in
many different ways. My view is that it is flexible enough to emulate
both styles of language, but is neither in full.

As this reply is rather long I'll do any other replies separately.

John

PS Your web site goes bang on the 'photo gallery' link.

Thomas 'PointedEars' Lahn

4/4/2016 8:13:00 PM

0

Scott Sauyet wrote:

> John Harris wrote:
>> It's time to compare ways of creating objects now that the full release
>> version of Firefox implements class declarations. The code below shows
>> three of the ways. They are all ones where an object is created by using
>> new and a constructor.
>
> I don't see why that is so special. I will add several more suggestions
> below that use three different object creation APIs, including a 'new-and-
> constructor' version. Two of them also share the same sort of prototype-
> based object mechanism shared by your three samples.

Just a quick remark on the code of variants 2 and 3: A function declaration
must not occur in a Block statement (â??{â?¦}â?) if the code is to be portable
(which it were in this case if the Block statement delimiters were omitted).

Short version: Do not write code this way. If you need a function within a
block, use a function expression (assigned to a variable) instead.

See <http://www.ecma-international.org/ecma-262/6.0/#sec-block-level-function-declarations-web-legacy-compatibility-sem... for details.

--
PointedEars
FAQ: <http://PointedEars.... | SVN: <http://PointedEars.de...
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-...
Please do not cc me. / Bitte keine Kopien per E-Mail.

Scott Sauyet

4/5/2016 4:04:00 AM

0

John Harris wrote:
> Scott Sauyet wrote:
>>John Harris wrote:
> <snip>
>>> The three examples illustrate :
>>> 1 Class-style creation : Using an ES6 class declaration;
>>> 2 Class-style creation : Using ES3 features;
>>> 3 Prototypal-style creation : Using ES3 features.
>>
>> These three also illustrate standard prototypal creation, regardless of
>> whether some syntactic sugar has been added to help make them seem more
>> similar to C++-style OOP classes.
> <snip>
>
> I'm afraid I disagree with you there.
>
> First, in a full-on prototypal language a new object is a complete copy
> of the prototype. [ ... ]

I don't feel like looking up where, but I believe we've had this
discussion before. I do not believe that concatenative prototyping is
the only legitimate form of prototypal inheritance. Delegation-based
prototyping is reasonable, and has been part of prototypal languages
since the very beginning. Self had a delegation mechanism.


> Second, let's look at a typical C++ implementation. Each object contains
> a pointer to a structure shared with all objects belonging to the same
> class. The shared structure contains pointers to the class's method
> functions. On seeing
> a.oink();
> the compiler generates code that follows the pointer to the shared
> structure, picks out the pointer it knows points to the oink function,
> follows that pointer and calls the oink function. One of the call
> arguments is a pointer to the object (i.e 'this') so the method knows
> what to work on. The 'this' argument is automatic, not defined in the
> function definition.
>
> Now look at ECMAScript. Each object contains a pointer to a structure
> shared with all objects of the same kind (or null - programmer's
> choice). The structure holds pointers to method functions (or not -
> programmer's choice). Each method call automatically includes a 'this'
> argument, held in a location separate from the user's arguments.

You've made this point here a number of times.

I still disagree.

There are certainly significant implementation similarities between
delegation-based prototypes and classes, but there are also significant
differences:

obj.prototype.method(...params);

What's the equivalent of that in a class-based language? In general,
there is none, because _classes are not objects_. Prototypes are
objects. While the above code may fail because the implementation of
`method` might depend upon some properties available only on instances
one level down, it also might work. And more importantly, there is
simply nothing like this in C++ or Java or similar languages, for the
very good reason that the class of an object is a different sort of beast
altogether from the object itself. In Javascript, they are both objects,
and overlap in capabilities as well.

> The two languages are remarkably similar. By your definition C++ is a
> prototypal language with class declarations bolted on for programmers'
> convenience.

Sorry, that's your straw-man, and not my definition.


> ECMAScript is a very flexible language. Most things can be done in many
> different ways. My view is that it is flexible enough to emulate both
> styles of language, but is neither in full.

I'm not sure what you mean by "both styles" here. There are so many
different ways to do OOP that choosing one can become a serious chore.
But I do mostly FP these days, and don't worry about it too much
regardless.


> PS Your web site goes bang on the 'photo gallery' link.

Thanks. Some day, I'll take down that 14 year old page. (Those kids are
now 16 and 19 years old!) But it never seems to be important enough to
put up a new one.

Scott Sauyet

4/5/2016 4:04:00 AM

0

Stefan Weiss wrote:
> Scott Sauyet wrote:
>> John Harris wrote:
>>
>>> It's time to compare ways of creating objects now that the full
>>> release version of Firefox implements class declarations. The code
>>> below shows three of the ways. They are all ones where an object is
>>> created by using new and a constructor.
>>
>> I don't see why that is so special.
>
> The special thing is that it provides inheritance. The new `class`
> syntax doesn't add anything that wasn't possible before, the syntactic
> sugar just makes it look nicer. Other types of object creation are
> equally valid, and often preferable, but like the things 5 and 6 in your
> examples, they don't have "is-a" semantics built in. This may or may not
> be a desirable feature, depending on the circumstances (Thing6 could be
> adjusted to make this work).

Thing6 does have one form of "is-a" already enabled:

Thing6.isPrototypeOf(a6);

And, yes, it would be easy enough to fix up to also support `instanceof`.

Note that a small variant of thing5 could also support this sort of type-
checking as well:

function thing7(phrase) {
var count = 0;
return Object.create(thing7.prototype, {
toString: {value: function() {return phrase;}},
incr: {value: function() {count++}},
count: {get: function() {return count;}}
});
} // thing7

var a7 = thing7("Hello from thing7 a7");
a7 + ': ' + a7.count; //=> "Hello from thing7 a7: 0"
a7.incr();
a7 + ': ' + a7.count; //=> "Hello from thing7 a7: 1"
var b7 = thing7("Hello from thing5 b7");
b7 + ': ' + b7.count; //=> "Hello from thing7 b7: 0"

b7 instanceof thing7; //=> true

The rationale for my response was simply why John focused on these three
forms of OOP at the expense of many others that are equally feasible.

There are certainly trade-offs to be made. Thanks for the chart; it
confirmed what I already knew about memory consumption and taught me
something about speed that I didn't realize.


>> I believe all three techniques I suggest below are similarly suited to
>> the creation of several objects of the same kind. The second one
>> (`thing5`) is more memory-intensive, and probably is not suitable for
>> hundreds of thousands or millions of instances of the type, but other
>> than that, they have similar characteristics. `thing5` does offer the
>> ability to have truly private implementation details, which many other
>> techniques really don't.
>
> I think it's unfortunate that ES2015 introduced a new incompatible
> `class` syntax but missed the chance to address the very common need for
> private data properties (or any declared properties, for that matter).
> Something like that might be added in a future expansion of the
> standard, but currently there are only stage 0 and stage 1 proposals. I
> found two relevant ones; neither of them is included in the prospective
> feature set for ES2017:
> https://github.com/jeffmo/es-class-fields-and-static-...
> https://github.com/wycats/javascript-pri...
>
> There are workarounds: two well-established patterns (using naming
> conventions and adding per-instance methods in the constructor) and two
> new methods involving WeakMaps and Symbols. Maybe we should add those to
> the list, as long as we're doing comparisons.

While it might be instructive to list those as well, I have seen so many
different ways to do OOP in Javascript that I don't feel we could
possibly get an exhaustive list. So I just wanted to add a few more to
John's list to see if there was a particular reason to choose his three
and to exclude other equally valid ones.


>> John Harris wrote:
>>> Concerning way 1, 2, and 3 The three ways create objects that do much
>>> the same job. Which way is best in general is a matter of personal
>>> preference. There is no reason for loud and dogmatic assertions that
>>> one way is the Only True Way.
>
> Well said.

Yes, and I meant to comment on that first time around. There is far too
much "One True Way" nonsense on OOP floating around, including one self-
proclaimed guru trying to sell his mentoring and training, insisting that
anyone who does OOP in a manner different from his own is a moron. While
I prefer the same style he does (`Object.create`-based) on those rare
occasions I use OOP JS, I certainly do not think it's the only
appropriate technique.

-- Scott

John Harris

4/5/2016 8:58:00 AM

0

On Mon, 04 Apr 2016 22:12:45 +0200, Thomas 'PointedEars' Lahn
<PointedEars@web.de> wrote:

<snip>
>Just a quick remark on the code of variants 2 and 3: A function declaration
>must not occur in a Block statement (?{?}?) if the code is to be portable
>(which it were in this case if the Block statement delimiters were omitted).
<snip>

Yes, I'd forgotten about that. Unfortunately Firefox allows it to
work :-( The ES3 standard was a lot clearer on this topic.

John

John Harris

4/6/2016 3:42:00 PM

0

On Mon, 4 Apr 2016 04:15:10 -0000 (UTC), Scott Sauyet
<scott@sauyet.com> wrote:

<snip>
>My techniques follow. I'm wondering if you see these ones as somehow
>lesser techniques than the three you present. And if so, why is that?

In general, any technique for the creation of one object can be
wrapped in a function and used in several places and reused in other
projects. The question is : does the function provided something extra
that constructors can't provide *and* is this needed here? If not then
an ordinary constructor is likely to be simpler and easier to read,
and sometimes faster.


>=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>
> function Thing4(phrase) {
> var count = 0;
> this.toString = function() {return phrase;}
> this.incr = function() {count++;}
> Object.defineProperties(this, {text: {
> get: function() {return phrase;}
> }, count: {
> get: function() {return count;}
> }})
> } // Thing4
>
> var a4 = new Thing4("Hello from Thing4 a4");
> a4 + ': ' + a4.count; //=> "Hello from Thing4 a4: 0"
> a4.incr();
> a4 + ': ' + a4.count; //=> "Hello from Thing4 a4: 1"
> var b4 = new Thing4("Hello from Thing4 b4");
> b4 + ': ' + b4.count; //=> "Hello from Thing4 b4: 0"

This makes count and phrase private variables and defines getters for
them. I'm not a fan of private variables, getters, and setters unless
they are really really needed. If your programmers include someone who
can't stop corrupting objects while the program is running then get
rid of them. On the other hand, in a library if someone corrupts your
object then it's your fault : the customer is always right, i.e shouts
louder :-(


> function thing5(phrase) {
> var count = 0;
> return {
> toString: function() {return phrase;},
> incr: function() {count++},
> get count() {return count;}
> };
> } // thing5
>
> var a5 = thing5("Hello from thing5 a5");
> a5 + ': ' + a5.count; //=> "Hello from thing5 a5: 0"
> a5.incr();
> a5 + ': ' + a5.count; //=> "Hello from thing5 a5: 1"
> var b5 = thing5("Hello from thing5 b5");
> b5 + ': ' + b5.count; //=> "Hello from thing5 b5: 0"

ditto.


> var Thing6 = {
> init: function(phrase) {
> this.text = phrase;
> this.count = 0;
> },
> incr: function() {
> this.count++;
> },
> toString: function() {
> return this.text ;
> }
> }; // Thing6
>
> var a6 = Object.create(Thing6);
> a6.init("Hello from Thing6 a6");
> a6 + ': ' + a6.count; //=> "Hello from Thing6 a6: 0"
> a6.incr();
> a6 + ': ' + a6.count; //=> "Hello from Thing6 a6: 1"
> var b6 = Object.create(Thing6);
> b6.init("Hello from Thing6 b6");
> b6 + ': ' + b6.count; //=> "Hello from Thing6 b6: 0"

I C++ circles, i.e in comp.lang.c++.moderated, an initialise function
provokes cries of Eugh! It's annoying to have to write two things.
More important, it's too easy to forget the init or to miss it out
when copying and pasting elsewhere.

Really, all this example does is to implement a constructor function.
Why bother?

No doubt someone (not me) will complain that "Thing6" is not a
construction function so should not start with a capital letter.

>=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

John

Scott Sauyet

4/6/2016 11:42:00 PM

0

John Harris wrote:
> Scott Sauyet wrote:
>
> <snip>
>> My techniques follow. I'm wondering if you see these ones as somehow
>> lesser techniques than the three you present. And if so, why is that?
>
> In general, any technique for the creation of one object can be wrapped
> in a function and used in several places and reused in other projects.
> The question is : does the function provided something extra that
> constructors can't provide *and* is this needed here? If not then an
> ordinary constructor is likely to be simpler and easier to read, and
> sometimes faster.

Of course, but in Javascript, there are a large number of such techniques,
and they each offer different advantages and disadvantages. As you noted,
#4 and #5 below each offer private properties. They also offer read-only
properties (not getters, BTW.) In addition, #5 can be used directly
anywhere a plain function is required without the overhead of adding
`new`, for instance as a callback to `Array.prototype.map`. #6 builds
the same sort of object prototype structure as your examples with
significantly less cognitive overhead.

I'm not trying to suggest that any of these are the definitive way to
handle OOP in Javascript. I don't think there is such a thing. But
these do offer useful features that differ from the features provided by
the techniques you provided. There are still others we could discuss.

That's what I was trying to get at. Do you think that there is something
special about your original list, or was it simply the first techniques
that came to mind?

>>=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>>
>> function Thing4(phrase) {
>> var count = 0;
>> this.toString = function() {return phrase;}
>> this.incr = function() {count++;} Object.defineProperties(this,
>> {text: {
>> get: function() {return phrase;}
>> }, count: {
>> get: function() {return count;}
>> }})
>> } // Thing4
>>
>> var a4 = new Thing4("Hello from Thing4 a4");
>> [ ... ]
>
> This makes count and phrase private variables and defines getters for
> them. I'm not a fan of private variables, getters, and setters unless
> they are really really needed. If your programmers include someone who
> can't stop corrupting objects while the program is running then get rid
> of them. On the other hand, in a library if someone corrupts your object
> then it's your fault : the customer is always right, i.e shouts louder
> :-(

Are you suggesting that your distaste for these constructs should dictate
how others write their code? If you release version 1 of your API and
some consumer of it starts to depend on what you thought was your
internals, you might start to enjoy to power of encapsulation when you
try to write version 2 and find that you can't modify those things you
always planned to clean up. If nothing else, encapsulation is generally
seen as one of the main tenets of OOP.

By the way, this does not define getter-functions. It only makes the
properties `text` and `count` read-only. I tried to write each version
in a way that might change the construction mechanism but otherwise kept
the public API of your original examples. That included the behavior of
the `incr` and `toString` methods and the existence of a readable `count`
property. (Looking back, I realize that the `text` property was not
necessary here.) Beyond that, I felt free to do as I chose, and here, I
chose not to allow a consumer to modify `count`. It seems likely that
the existence of a method like `incr` implies some invariant that would
be broken by a publicly mutable `count` property.


>> function thing5(phrase) {
>> var count = 0;
>> return {
>> toString: function() {return phrase;},
>> incr: function() {count++},
>> get count() {return count;}
>> };
>> } // thing5
>>
>> var a5 = thing5("Hello from thing5 a5");
>> [ ... ]

But, while this might not be suitable for millions of instances due to
the increased memory footprint, it also has the advantage of being easier
to use in higher-order abstractions:

["Phrase 1", "Phrase 2", "Prase 3"].map(thing5);

This is significantly more ergonomic than

["Phrase 1", "Phrase 2", "Prase 3"].map(function(phrase) {
return new Thing1(phrase);
});

And of course it shares the advantages of Thing4 in terms of greater
encapsulation.

>> var Thing6 = {
>> init: function(phrase) {
>> this.text = phrase;
>> this.count = 0;
>> },
>> incr: function() {
>> this.count++;
>> },
>> toString: function() {
>> return this.text ;
>> }
>> }; // Thing6
>>
>> var a6 = Object.create(Thing6);
>> a6.init("Hello from Thing6 a6");
>> [ ... ]
>
> I C++ circles, i.e in comp.lang.c++.moderated, an initialise function
> provokes cries of Eugh! It's annoying to have to write two things. More
> important, it's too easy to forget the init or to miss it out when
> copying and pasting elsewhere.
>
> Really, all this example does is to implement a constructor function.
> Why bother? [ ... ]

The C++ world has a clear-cut way to do this. In some ways C++ is to
Javascript like Python is to Perl. In Javascript, there simply is no
clear-cut definitive way to do these things, so we get to frequently
discuss varying approaches.

One advantage of this is well described in an article by Kyle Simpson
[1]. The point is that all Javascript's inheritance mechanism is meant
to do is to set up a delegation mechanism between simple objects. The
constructor function grafts an unfortunately messy layers onto this for
little benefit, and changes a very simple conceptual model [2] into an
extremely convoluted one [3].

I agree with Kyle Simpson in many ways. I think he (and Eric Elliott,
who is the loudest proponent of this point of view) oversell the style of
Thing6 as the only reasonable way to do OOP in Javascript. But it is
certainly *one* reasonable way.

Still, my big question is whether you find these techniques somehow
inferior to your original list. Do you believe your list captures the
essence of the set of reasonable object creation techniques in Javascript?


[1]: <https://davidwalsh.name/javascript-objects-deconstr...
[2]: <https://davidwalsh.name/demo/JavaScriptObjects--OnlyObjec...
[3]: <https://davidwalsh.name/demo/JavaScriptObjects--Fu...

-- Scott