Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.javascript > #30199 > unrolled thread

Comparing some of the ways to create objects

Started byJohn Harris <niam@jghnorth.org.uk.invalid>
First post2016-04-03 16:09 +0100
Last post2016-04-10 11:52 +0100
Articles 20 on this page of 22 — 5 participants

Back to article view | Back to comp.lang.javascript


Contents

  Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-03 16:09 +0100
    Re: Comparing some of the ways to create objects Scott Sauyet <scott@sauyet.com> - 2016-04-04 04:15 +0000
      Re: Comparing some of the ways to create objects Stefan Weiss <krewecherl@gmail.com> - 2016-04-04 15:20 +0200
        Re: Comparing some of the ways to create objects Scott Sauyet <scott@sauyet.com> - 2016-04-05 04:04 +0000
      Re: Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-04 16:48 +0100
        Re: Comparing some of the ways to create objects Scott Sauyet <scott@sauyet.com> - 2016-04-05 04:04 +0000
          Re: Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-07 15:07 +0100
            Re: Comparing some of the ways to create objects Scott Sauyet <scott@sauyet.com> - 2016-04-08 01:10 +0000
              Re: Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-08 14:55 +0100
      Re: Comparing some of the ways to create objects Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2016-04-04 22:12 +0200
        Re: Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-05 09:57 +0100
      Re: Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-06 16:41 +0100
        Re: Comparing some of the ways to create objects Scott Sauyet <scott@sauyet.com> - 2016-04-06 23:42 +0000
          Re: Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-07 16:09 +0100
            Re: Comparing some of the ways to create objects Scott Sauyet <scott@sauyet.com> - 2016-04-08 02:05 +0000
              Re: Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-08 15:43 +0100
                Re: Comparing some of the ways to create objects Scott Sauyet <scott@sauyet.com> - 2016-04-09 00:01 +0000
                  Re: Comparing some of the ways to create objects Scott Sauyet <scott@sauyet.com> - 2016-04-09 16:46 +0000
                    Re: Comparing some of the ways to create objects Scott Sauyet <scott@sauyet.com> - 2016-04-10 16:51 +0000
                    Re: Comparing some of the ways to create objects Stanimir Stamenkov <s7an10@netscape.net> - 2016-05-08 17:27 +0300
                  Re: Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-10 11:31 +0100
                  Re: Comparing some of the ways to create objects John Harris <niam@jghnorth.org.uk.invalid> - 2016-04-10 11:52 +0100

Page 1 of 2  [1] 2  Next page →


#30199 — Comparing some of the ways to create objects

FromJohn Harris <niam@jghnorth.org.uk.invalid>
Date2016-04-03 16:09 +0100
SubjectComparing some of the ways to create objects
Message-ID<hbc2gbpvk5nk24b0qupps6cjqn8fm3ar5e@4ax.com>
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>

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

[toc] | [next] | [standalone]


#30210

FromScott Sauyet <scott@sauyet.com>
Date2016-04-04 04:15 +0000
Message-ID<ndspod$lhh$1@dont-email.me>
In reply to#30199
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  

[toc] | [prev] | [next] | [standalone]


#30217

FromStefan Weiss <krewecherl@gmail.com>
Date2016-04-04 15:20 +0200
Message-ID<ndtpmv$8al$1@news.albasani.net>
In reply to#30210
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-properties
https://github.com/wycats/javascript-private-state

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

[toc] | [prev] | [next] | [standalone]


#30221

FromScott Sauyet <scott@sauyet.com>
Date2016-04-05 04:04 +0000
Message-ID<ndvdga$o4q$1@dont-email.me>
In reply to#30217
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-properties
> https://github.com/wycats/javascript-private-state
> 
> 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

[toc] | [prev] | [next] | [standalone]


#30218

FromJohn Harris <niam@jghnorth.org.uk.invalid>
Date2016-04-04 16:48 +0100
Message-ID<o435gb9fs37lmdqp7muur2nf9p9mn5nf7j@4ax.com>
In reply to#30210
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.

[toc] | [prev] | [next] | [standalone]


#30220

FromScott Sauyet <scott@sauyet.com>
Date2016-04-05 04:04 +0000
Message-ID<ndvdg9$o4n$1@dont-email.me>
In reply to#30218
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.

[toc] | [prev] | [next] | [standalone]


#30233

FromJohn Harris <niam@jghnorth.org.uk.invalid>
Date2016-04-07 15:07 +0100
Message-ID<o9qcgbthcatgarbp46hs2nufsuouqmtn0k@4ax.com>
In reply to#30220
On Tue, 5 Apr 2016 04:04:26 -0000 (UTC), Scott Sauyet
<scott@sauyet.com> wrote:

>John Harris wrote:
  <snip>
>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.

At last I've found out which meaning of delegate you are using. (There
are 3 definitions in Wikipedia.) You mean using a function to do the
work for you, using indirection to reach the function.


  <snip>
>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 C++ you can access an inherited data or function member provided it
hasn't been flagged as private. There is no problem about the 'this'
value. 
  struct A 
    { 
      int a(){return 3;} 
    };
  struct F: public A { };
  void f() 
    {
      F f;        // f is 'obj'
      f.A::a();   // A is 'prototype', a is 'method'
    }


  <snip>
>In general, 
>there is none, because _classes are not objects_.  Prototypes are 
>objects.  

Classes are not objects in any language. Some languages have class
representative objects providing information about objects that belong
to the class and sometimes holding methods, including constructor
methods. That's all.

ECMAScript prototypes are objects, yes, but they aren't classes.


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

Prototype objects really aren't the equivalent of classes or class
definitions. 

Yes, the equivalent of a prototype in C++ is not an object, but you
can access it using different syntax : colon colon instead of dot.

  John

[toc] | [prev] | [next] | [standalone]


#30235

FromScott Sauyet <scott@sauyet.com>
Date2016-04-08 01:10 +0000
Message-ID<ne70et$2i4$1@dont-email.me>
In reply to#30233
John Harris wrote:
> Scott Sauyet wrote:

>> 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.
> 
> At last I've found out which meaning of delegate you are using. (There
> are 3 definitions in Wikipedia.) You mean using a function to do the
> work for you, using indirection to reach the function.

Well, no.

I simply mean that an object that is asked for a property that it doesn't 
have delegates the request to its prototype.  This is different from 
concatenative prototyping in which an object is created by cloning all of 
its prototype's properties and then remains disconnected from it.  The 
first prototypal language, Self, had both: concatenative by default, but 
with opt-in delegation mechanisms.  Most languages since choose one or 
the other.

>> 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 C++ you can access an inherited data or function member provided it
> hasn't been flagged as private. There is no problem about the 'this'
> value. 
>   struct A
>     {
>       int a(){return 3;}
>     };
>   struct F: public A { };
>   void f()
>     {
>       F f;        // f is 'obj'
>       f.A::a();   // A is 'prototype', a is 'method'
>     }

I'm not sure what you mean by "There is no problem about the 'this' 
value."  In an OOP context, and the whole point of my example, is that 
the code is running in the context of a particular object.  If a property 
of `this` is accessed or mutated, it will use the one stored on that 
prototype and not the object itself.  If the prototype is mutated, then 
any objects constructed from it which don't override the property now 
have the mutated value.

As far as I know this is entirely different behavior from any class-based 
languages that exist.


> [ ... ]
> Prototype objects really aren't the equivalent of classes or class
> definitions.

I certainly agree.  It seemed to me that you had been arguing the 
reverse.  I'm sorry if I misunderstood.

  -- Scott

[toc] | [prev] | [next] | [standalone]


#30238

FromJohn Harris <niam@jghnorth.org.uk.invalid>
Date2016-04-08 14:55 +0100
Message-ID<h1efgbtpjtq64cepa3j66096kk74pjlf4g@4ax.com>
In reply to#30235
On Fri, 8 Apr 2016 01:10:54 -0000 (UTC), Scott Sauyet
<scott@sauyet.com> wrote:

  <snip>
>If the prototype is mutated, then 
>any objects constructed from it which don't override the property now 
>have the mutated value.
>
>As far as I know this is entirely different behavior from any class-based 
>languages that exist.
  <snip>

Good for class-based languages! As I pointed out, this feature of
prototypes gives you unforecastable behaviour unless your program
design is both careful and restricted. 

Oh, and C++ can get the same effect by different means if you really
wanted it.

  John

[toc] | [prev] | [next] | [standalone]


#30219

FromThomas 'PointedEars' Lahn <PointedEars@web.de>
Date2016-04-04 22:12 +0200
Message-ID<1773037.o7vNLfRneB@PointedEars.de>
In reply to#30210
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-semantics> for details.
 
-- 
PointedEars
FAQ: <http://PointedEars.de/faq> | SVN: <http://PointedEars.de/wsvn/>
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-matrix>
Please do not cc me. / Bitte keine Kopien per E-Mail.

[toc] | [prev] | [next] | [standalone]


#30222

FromJohn Harris <niam@jghnorth.org.uk.invalid>
Date2016-04-05 09:57 +0100
Message-ID<mfv6gb9uu9ftdhdjbsr2m8j7dq1lqn82r2@4ax.com>
In reply to#30219
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

[toc] | [prev] | [next] | [standalone]


#30230

FromJohn Harris <niam@jghnorth.org.uk.invalid>
Date2016-04-06 16:41 +0100
Message-ID<0ebagb5j5s77hr64fhi3vlpr64asu3gous@4ax.com>
In reply to#30210
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

[toc] | [prev] | [next] | [standalone]


#30232

FromScott Sauyet <scott@sauyet.com>
Date2016-04-06 23:42 +0000
Message-ID<ne46s8$8td$1@dont-email.me>
In reply to#30230
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-deconstruction>
  [2]: <https://davidwalsh.name/demo/JavaScriptObjects--OnlyObjects.png>
  [3]: <https://davidwalsh.name/demo/JavaScriptObjects--Full.png>

  -- Scott

[toc] | [prev] | [next] | [standalone]


#30234

FromJohn Harris <niam@jghnorth.org.uk.invalid>
Date2016-04-07 16:09 +0100
Message-ID<7vtcgb5hio1hbbtg7cg0ca1dmjkel0hv6b@4ax.com>
In reply to#30232
On Wed, 6 Apr 2016 23:42:01 -0000 (UTC), Scott Sauyet
<scott@sauyet.com> wrote:

>John Harris wrote:
  <snip>
>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?

They are special in the sense that they are simple and
straightforward. Other ways are more complicated or suffer from code
bloat. If the software project is done by a few trustworthy people and
the code is not going to be sold to others, then simplicity and lack
of bloat win the day.

As I said, other ways are sometimes necessary.


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

See above.

Unfortunately, private encapsulation is not an ECMAScript feature
(yet).


>By the way, this does not define getter-functions.  

As usual, there's more than one way to do getting and setting. There's
nothing wrong with calling them getters, especially when including ES3
environments.


  <snip>
>It seems likely that 
>the existence of a method like `incr` implies some invariant that would 
>be broken by a publicly mutable `count` property.

But what does the project specification say?


  <snip>

>Still, my big question is whether you find these techniques somehow 
>inferior to your original list.  

They are inferior when they are not needed.
They are essential when they are needed.


>Do you believe your list captures the 
>essence of the set of reasonable object creation techniques in Javascript?

It captures the essence of the different ways, without complicating
the picture. E.g The per-object data is shown in one list, not
scattered around arguments and local variables.


>  [1]: <https://davidwalsh.name/javascript-objects-deconstruction>
  <snip>

His preferred construction is your Thing6 example. It uses two
statements to create a new object. He forgets that if you are going to
make the same kind of object in several places then you would
encapsulate the code in a makeThing6 function.

  John

[toc] | [prev] | [next] | [standalone]


#30236

FromScott Sauyet <scott@sauyet.com>
Date2016-04-08 02:05 +0000
Message-ID<ne73l3$2i4$2@dont-email.me>
In reply to#30234
John Harris wrote:
> Scott Sauyet wrote:

>> 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?
> 
> They are special in the sense that they are simple and straightforward.
> Other ways are more complicated or suffer from code bloat. If the
> software project is done by a few trustworthy people and the code is not
> going to be sold to others, then simplicity and lack of bloat win the
> day.

I would argue that in fact, a variant of my #5 is substantially simpler:

    function thing8(phrase) {
      return {
        count: 0,
        toString: function() {return phrase;},
        incr: function() {this.count++},
      };
    } // thing8

There are certainly legitimate reasons not to like this.  (See Stefan 
Weiss' response for some of them.)  But if simplicity is you chief 
concern, then this one beats them all.


> Unfortunately, private encapsulation is not an ECMAScript feature (yet).

But it is.  I offered two ways to achieve it.  Stefan also mentioned 
techniques involving Symbols and WeakMaps.  Again, my main point is that, 
if you're going to do OOP, there are many different techniques to achieve 
it.


>> By the way, this does not define getter-functions.
> 
> As usual, there's more than one way to do getting and setting. There's
> nothing wrong with calling them getters, especially when including ES3
> environments.

Just so long as no one confused the syntax with something like Java's 
JavaBeans structure...




> [ ... ]
> 
>> Still, my big question is whether you find these techniques somehow
>> inferior to your original list.
> 
> They are inferior when they are not needed.
> They are essential when they are needed.

Your Honor, permission to treat this man as a hostile witness?  :-)

To me, your list didn't offer ways to solve some of the most essential 
problems in OOP; instead it gave three barely-distinguishable versions of 
the same basic idea and seemed to claim that they covered everything 
important in OOP.


>> Do you believe your list captures the essence of the set of reasonable
>> object creation techniques in Javascript?
> 
> It captures the essence of the different ways, without complicating the
> picture. E.g The per-object data is shown in one list, not scattered
> around arguments and local variables.

Version #8 above does that more succinctly.  Should that be the only one 
we promote?


>>  [1]: <https://davidwalsh.name/javascript-objects-deconstruction>
>   <snip>
> 
> His preferred construction is your Thing6 example. It uses two
> statements to create a new object. He forgets that if you are going to
> make the same kind of object in several places then you would
> encapsulate the code in a makeThing6 function.

I seriously doubt he would object to a function which encapsulated this 
behavior.  His point was to demonstrate the difference in what's 
generated between several different Object-creation schemes.  He was not 
promoting a final API for OOP design.

In any case, I would personally choose something like #5 / #8 for most of 
my work.  I find this clearest and most expressive.  And I suppose that's 
why I challenge the notion that #1 - #3 are paticularly relevent.

  -- Scott

[toc] | [prev] | [next] | [standalone]


#30239

FromJohn Harris <niam@jghnorth.org.uk.invalid>
Date2016-04-08 15:43 +0100
Message-ID<crgfgbd1s7r9irkgg6vbm7qt6e1e8a3ctd@4ax.com>
In reply to#30236
On Fri, 8 Apr 2016 02:05:23 -0000 (UTC), Scott Sauyet
<scott@sauyet.com> wrote:

>John Harris wrote:
>> Scott Sauyet wrote:

  <snip>
>I would argue that in fact, a variant of my #5 is substantially simpler:
>
>    function thing8(phrase) {
>      return {
>        count: 0,
>        toString: function() {return phrase;},
>        incr: function() {this.count++},
>      };
>    } // thing8
>
>There are certainly legitimate reasons not to like this.  (See Stefan 
>Weiss' response for some of them.)  But if simplicity is you chief 
>concern, then this one beats them all.

See later.


>> Unfortunately, private encapsulation is not an ECMAScript feature (yet).
>
>But it is.  I offered two ways to achieve it.  Stefan also mentioned 
>techniques involving Symbols and WeakMaps.  Again, my main point is that, 
>if you're going to do OOP, there are many different techniques to achieve 
>it.
  <snip>

Then there's also get and set methods. But there isn't a 'private'
keyword yet.


>To me, your list didn't offer ways to solve some of the most essential 
>problems in OOP; 

The title says 'comparing' and 'ways', not problems that some people
need to solve.


>instead it gave three barely-distinguishable versions of 
>the same basic idea 

It's true, they are barely distinguishable, but some people are
absolutely adamant that ECMAScript is one of these ways and not the
other.


>and seemed to claim that they covered everything 
>important in OOP.

That would be a misunderstanding of the title.


>>> Do you believe your list captures the essence of the set of reasonable
>>> object creation techniques in Javascript?
>> 
>> It captures the essence of the different ways, without complicating the
>> picture. E.g The per-object data is shown in one list, not scattered
>> around arguments and local variables.
>
>Version #8 above does that more succinctly.  Should that be the only one 
>we promote?

Using function arguments as pseudo-properties is not really succinct.



>>>  [1]: <https://davidwalsh.name/javascript-objects-deconstruction>
  <snip>
>In any case, I would personally choose something like #5 / #8 for most of 
>my work.  I find this clearest and most expressive.  

Have you wondered what his so simple final diagram would look like if
you add all the lines and boxes for accessing function arguments and
local variables ?

An aside : There's a video talk by the inventor of Smalltalk where he
makes it very clear that if a language hasn't got *every* feature of
Smalltalk then it isn't OO. I wonder if Dave Walsh has seen it.


>And I suppose that's 
>why I challenge the notion that #1 - #3 are paticularly relevent.

I think we have to agree to disagree on the subject of preferred
creation methods.

  John

[toc] | [prev] | [next] | [standalone]


#30240

FromScott Sauyet <scott@sauyet.com>
Date2016-04-09 00:01 +0000
Message-ID<ne9go3$ehl$1@dont-email.me>
In reply to#30239
John Harris wrote:
> Scott Sauyet wrote:
>> John Harris wrote:
>>> Scott Sauyet wrote:

>> I would argue that in fact, a variant of my #5 is substantially
>> simpler:
>>
>>    function thing8(phrase) {
>>      return {
>>        count: 0,
>>        toString: function() {return phrase;},
>>        incr: function() {this.count++},
>>      };
>>    } // thing8
>>
>> There are certainly legitimate reasons not to like this.  (See Stefan
>> Weiss' response for some of them.)  But if simplicity is you chief
>> concern, then this one beats them all.
 
>>> Unfortunately, private encapsulation is not an ECMAScript feature
>>> (yet).
>>
>> But it is.  I offered two ways to achieve it.  Stefan also mentioned
>> techniques involving Symbols and WeakMaps.  Again, my main point is
>> that, if you're going to do OOP, there are many different techniques 
>> to achieve it.
> 
> Then there's also get and set methods. But there isn't a 'private'
> keyword yet.


Yes, JS is not an OOP language.  It's not an imperative language.  It's 
not a functional language.  It's a multi-paradigm language with a 
reasonable set of prototypal OOP features and a basic set of functional 
features that is used by many beginners as a simple imperative language.

But the privacy offered by these techniques, even without a keyword is 
actually much stronger than that offered by Java or C# (I don't know 
about C++.)  In those languages, you can use reflection to to still get 
at the field.  In Javascript closure-based privacy, it's really hidden.
[1]

>> To me, your list didn't offer ways to solve some of the most essential
>> problems in OOP;
> 
> The title says 'comparing' and 'ways', not problems that some people
> need to solve.

Ok, I've offered now five additional ways, as I found none of the 
originals compelling.  But perhaps I should simply shut up because I 
mostly don't find OOP particularly compelling.

 
>> instead it gave three barely-distinguishable versions of the same basic
>> idea
> 
> It's true, they are barely distinguishable, but some people are
> absolutely adamant that ECMAScript is one of these ways and not the
> other.

Sure.  Were you mostly trying to make the point that these are equally 
legitimate?  If so, I missed it, perhaps because that's blindingly 
obvious to me.  Clearly I believe there are many other ways that might be 
still more reasonable.

>> and seemed to claim that they covered everything important in OOP.
> 
> That would be a misunderstanding of the title.

I don't put much stock in USENET titles.  But you're right, I didn't 
think much about the title in my responses.


>>>> Do you believe your list captures the essence of the set of
>>>> reasonable object creation techniques in Javascript?
>>> 
>>> It captures the essence of the different ways, without complicating
>>> the picture. E.g The per-object data is shown in one list, not
>>> scattered around arguments and local variables.
>>
>> Version #8 above does that more succinctly.  Should that be the only 
>> one we promote?
> 
> Using function arguments as pseudo-properties is not really succinct.

I'm afraid I don't follow this.

I was discussing the version quoted above, in which a function returns 
object described by an object literal containing the properties `count`, 
`toString`, and `incr`, the first initially hardcoded to a number, the 
second to a function which references the `phrase` parameter of the 
function, and the third a function which updates the local reference to 
the `count` property.

Are you saying that the fact that `phrase` is stored in the closure makes 
this less succinct?  That seems backwards to me.  But obviously we could 
change it trivially so that the property is stored in the returned 
object.  But that once again makes mutable something that arguably should 
remain immutable.

I think working in the functional programming world long enough may have 
colored how I see OOP as well.  I prefer immutable objects, and even when 
they are mutable, I prefer to contain the mutation as much as possible.

For this reason, I still prefer #5 to #8.

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

In this version, the user cannot break the object as we might with #8, or 
with any of the versions in the OP:

    var a8 = thing8("Hello from thing8 a8");
    a8 + ': ' + a8.count; //=> "Hello from thing8 a8: 0"
    a8.incr();
    a8 + ': ' + a8.count; //=> "Hello from thing5 a8: 1"
    a8.count = "hey look, a unicorn"
    a8 + ': ' + a8.count; //=> "Hello from thing5 a8: hey look, a unicorn"
    a8.incr();
    a8 + ': ' + a8.count; //=> "Hello from thing5 a8: NaN"

>>>>  [1]: <https://davidwalsh.name/javascript-objects-deconstruction>
>> In any case, I would personally choose something like #5 / #8 for most
>> of my work.  I find this clearest and most expressive.
> 
> Have you wondered what his so simple final diagram would look like if
> you add all the lines and boxes for accessing function arguments and
> local variables ?

The technique Kyle Simpson suggests does not use local closures for any 
such thing.  That was a different example, #6 in my list.

> An aside : There's a video talk by the inventor of Smalltalk where he
> makes it very clear that if a language hasn't got *every* feature of
> Smalltalk then it isn't OO. I wonder if Dave Walsh has seen it.

I'd be interested in seeing a reference if you have one, because that 
does not sound like his ideas at all.

I think this bears repeating:

  (from my <news:655ac788-5a00-447c-a148-08287f3c87bd@googlegroups.com>)

| In a response to (our own?) Stefan Ram, Alan Kay, who coined the
| term "object-oriented", said, "OOP to me means only messaging, local
| retention and protection and hiding of state-process, and extreme
| late-binding of all things."
| 
| The entire exchange [*] is well worth a read.  Dr. Kay also partially
| disavows at least the word "polymorphism" as not flexible enough to
| capture what he was looking for, and more firmly disavows existing
| (circa 2003) strong type systems.
| 
| While I don't believe that Dr. Kay has the right to dictate the ongoing
| discussion of the term, he is also in a quite privileged place with
| respect to it.  These ideas are worth serious consideration.  And to
| me, they are quite convincing. 
| 
| [*]: <http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/
doc_kay_oop_en> 


> 
>>And I suppose that's why I challenge the notion that #1 - #3 are
>>paticularly relevent.
> 
> I think we have to agree to disagree on the subject of preferred
> creation methods.

Yes, clearly we have very different ideas of what's important.   And 
we're not likely to convince one another.  If you would, though, I'd 
appreciate it if you could clarify what you mean by "Using function 
arguments as pseudo-properties is not really succinct."



[1]: I believe I've recently seen a technique that might allow for the 
breaking of this privacy.  I thought it was on Gleb Bahmutov's blog 
(https://glebbahmutov.com/blog/), but I don't spot it when I go back to 
look.  It was quite obscure, and Node.js only, if I recall correctly.

  -- Scott

[toc] | [prev] | [next] | [standalone]


#30242

FromScott Sauyet <scott@sauyet.com>
Date2016-04-09 16:46 +0000
Message-ID<nebblk$6dj$1@dont-email.me>
In reply to#30240
Stefan Ram wrote:
> Scott Sauyet wrote:

>> But the privacy offered by these techniques, even without a keyword is
>> actually much stronger than that offered by Java or C# (I don't know
>> about C++.)  In those languages, you can use reflection to to still get
>> at the field.  In Javascript closure-based privacy, it's really hidden.
> 
> Java programs can be run with a security manager, and IIRC there is a
> »suppressAccessChecks« permission that can be denied to deny access to
> private fields via reflection.
> (I am not sure about this, I haven't tested it today.)

I'm more than six years out from regularly working with Java, so I may be 
a little rusty.  But I believe that field is precisely the reverse: a 
tool to _allow_ otherwise restricted code from viewing private or 
protected members.  That's certainly how the Javadocs seem to read.  [1]


> But the privacy of OOP is not meant to be a shield against malicious
> attacs, it is meant to be a means to help programmers not to access a
> private field inadvertently.

I've always thought of it a bit differently.  I picture it as a way to 
encapsulate internal state so that one can freely change implementation 
without the threat of breaking dependent code.  The shenanigans allowed 
by the combination of Reflection and ClassLoaders means there is little 
protection from malicious code running in the same VM, as far as I can 
see.  But the protection offered by encapsulation is real and very 
important.

 
> And one /can/ generate closures in Java too. In the Java program
> below, each of »e« and »e1« have their /own/ »closure variable«
> »a[ 0 ]« (which is not a field of a class).
> 
> [ ... details and code samples elided ... ]

Yes, I was gone from Java before version 8 was released, so I haven't 
really played around with this all that much.  But it's interesting 
because you answered a question a colleague and I were speculating about 
a few days ago, but weren't quite motivated enough to actually research.  
( :-> )  We knew that Java closures used static references; so you 
couldn't update a counter variable stored in a closure.  As I pointed 
that out as a somewhat-crippling handicap to one colleague, another 
speculated, "I wonder if you could use a container like an array and 
simply mutate the value stored inside."  Thank you for clearing that up 
for us!  :-)

So are those closure-based values as hidden as ones in Javascript?  That 
is, are they much more private than class members with the `private` 
keyword, which can still be accessed through reflection?  That would be 
interesting to know.  It doesn't change how the `private` and `protected` 
keywords work, but it would be interesting to know.


  [1]: <http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/
ReflectPermission.html>

  -- Scott

[toc] | [prev] | [next] | [standalone]


#30253

FromScott Sauyet <scott@sauyet.com>
Date2016-04-10 16:51 +0000
Message-ID<nee0b2$p9m$1@dont-email.me>
In reply to#30242
Stefan Ram wrote:
> Scott Sauyet writes:

>> Yes, I was gone from Java before version 8 was released, so I haven't
>> really played around with this all that much.  But it's interesting
>> because you answered a question a colleague and I were speculating 
>> about a few days ago, but weren't quite motivated enough to actually 
>> research.
> 
> The Java example I gave should run with every Java version since
> version 1.1 (1997).

Ah, you tricked me.  :-)  I saw unfamiliar-looking code, and assumed it 
was something new.  And since reading Java hurts my head (or my pride, 
sometimes I can't tell!), I didn't look any further.  But it was only 
code laid out differently than I'm used to.  Sorry.

The relevant part of the Java sample, when formatted so I can read it 
looks something like this:

|   static final Example closureGenerator(final java.lang.Integer i) {
|     final int[] a = {i};
|     return new Example() {
|       public void reset() {a[0] = 0;}
|       public void next() {
|         java.lang.System.out.println(a[0]++);
|       }
|     };
|   }

Interesting.  When I was using Java, I mostly stayed away from static 
code, and don't think I'd ever noticed that this was possible.  Very 
interesting.  It's too bad about that `final` declaration.  (And thanks 
for the history on that, too.)

 -- Scott

[toc] | [prev] | [next] | [standalone]


#30411

FromStanimir Stamenkov <s7an10@netscape.net>
Date2016-05-08 17:27 +0300
Message-ID<ngni5e$ns6$1@dont-email.me>
In reply to#30242
Sat, 9 Apr 2016 16:46:45 -0000 (UTC), /Scott Sauyet/:
> Stefan Ram wrote:
>> Scott Sauyet wrote:
>>
>>> But the privacy offered by these techniques, even without a keyword is
>>> actually much stronger than that offered by Java or C# (I don't know
>>> about C++.)  In those languages, you can use reflection to to still get
>>> at the field.  In Javascript closure-based privacy, it's really hidden.
>>
>> Java programs can be run with a security manager, and IIRC there is a
>> »suppressAccessChecks« permission that can be denied to deny access to
>> private fields via reflection.
>> (I am not sure about this, I haven't tested it today.)
>
> I'm more than six years out from regularly working with Java, so I may be
> a little rusty.  But I believe that field is precisely the reverse: a
> tool to _allow_ otherwise restricted code from viewing private or
> protected members.  That's certainly how the Javadocs seem to read.  [1]
>
>    [1]: <http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/ReflectPermission.html>

No contradiction here.  While the default security manager, or may 
be better stated having no security manager when you run a program 
from the command line may allow you to suppress access checks for 
private fields of any class via refection, running a code inside an 
application or applet container may have a security manager 
installed which prohibits the user code from doing it – at least for 
framework/container (provided/built-in) classes (vs. user code 
classes).  The security manager is standard feature of the Java 
sandboxing model.  See that trying to override access checks may 
still fail:

http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible%28boolean%29

> First, if there is a security manager, its checkPermission method
> is called with a ReflectPermission("suppressAccessChecks")
> permission.
>
> Throws:
>     SecurityException - if the request is denied.

java.lang.reflect.Field is a subclass of AccessibleObject.

-- 
Stanimir

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.javascript


csiph-web