Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.javascript > #30199 > unrolled thread
| Started by | John Harris <niam@jghnorth.org.uk.invalid> |
|---|---|
| First post | 2016-04-03 16:09 +0100 |
| Last post | 2016-04-10 11:52 +0100 |
| Articles | 20 on this page of 22 — 5 participants |
Back to article view | Back to comp.lang.javascript
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 →
| From | John Harris <niam@jghnorth.org.uk.invalid> |
|---|---|
| Date | 2016-04-03 16:09 +0100 |
| Subject | Comparing 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>
© Copyright J G Harris, 2016
</body>
</html>
<!- ========================================================== ->
[toc] | [next] | [standalone]
| From | Scott Sauyet <scott@sauyet.com> |
|---|---|
| Date | 2016-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]
| From | Stefan Weiss <krewecherl@gmail.com> |
|---|---|
| Date | 2016-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]
| From | Scott Sauyet <scott@sauyet.com> |
|---|---|
| Date | 2016-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]
| From | John Harris <niam@jghnorth.org.uk.invalid> |
|---|---|
| Date | 2016-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]
| From | Scott Sauyet <scott@sauyet.com> |
|---|---|
| Date | 2016-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]
| From | John Harris <niam@jghnorth.org.uk.invalid> |
|---|---|
| Date | 2016-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]
| From | Scott Sauyet <scott@sauyet.com> |
|---|---|
| Date | 2016-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]
| From | John Harris <niam@jghnorth.org.uk.invalid> |
|---|---|
| Date | 2016-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]
| From | Thomas 'PointedEars' Lahn <PointedEars@web.de> |
|---|---|
| Date | 2016-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]
| From | John Harris <niam@jghnorth.org.uk.invalid> |
|---|---|
| Date | 2016-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]
| From | John Harris <niam@jghnorth.org.uk.invalid> |
|---|---|
| Date | 2016-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]
| From | Scott Sauyet <scott@sauyet.com> |
|---|---|
| Date | 2016-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]
| From | John Harris <niam@jghnorth.org.uk.invalid> |
|---|---|
| Date | 2016-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]
| From | Scott Sauyet <scott@sauyet.com> |
|---|---|
| Date | 2016-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]
| From | John Harris <niam@jghnorth.org.uk.invalid> |
|---|---|
| Date | 2016-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]
| From | Scott Sauyet <scott@sauyet.com> |
|---|---|
| Date | 2016-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]
| From | Scott Sauyet <scott@sauyet.com> |
|---|---|
| Date | 2016-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]
| From | Scott Sauyet <scott@sauyet.com> |
|---|---|
| Date | 2016-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]
| From | Stanimir Stamenkov <s7an10@netscape.net> |
|---|---|
| Date | 2016-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