Closed Bug 83784 Opened 23 years ago Closed 23 years ago

Properties of prototyped-in properties not unique

Categories

(Core :: JavaScript Engine, defect)

x86
Windows 95
defect
Not set
normal

Tracking

()

VERIFIED INVALID

People

(Reporter: WeirdAl, Assigned: rogerl)

Details

From Bugzilla Helper:
User-Agent: Mozilla/5.0 (Windows; U; Win95; en-US; rv:0.9) Gecko/20010505
BuildID:    Mozilla/5.0 (Windows; U; Win95; en-US; rv:0.9) Gecko/20010505

When creating an object using a standard constructor, all properties and methods
prototyped to that object are copied in full.  However, I expect after the copy,
all references to the original prototype's properties to be broken.  This is not
the case, as far as I can tell.

Reproducible: Always
Steps to Reproduce:
1.  Run the following code in your browser.
function test() {
   }
test.prototype.a = new Object()
x = new test()
y = new test()
x.a[0] = 3
alert(y.a[0])

Actual Results:  An alert box containing "3".

Expected Results:  An alert box containing "undefined".

This implies that by setting one instance of test()'s a property to have a
property "0" with a value of 3, all instances of test()'s a property have a
property "0" with a value of 3.

Section 4.3.5 of ECMAScript 3rd Edition states:

-- quote --
A prototype is an object used to implement structure, state, and behaviour
inheritance in ECMAScript. When a constructor creates an object, that object
implicitly references the constructor's associated prototype for the purpose
of resolving property references. The constructor's associated prototype can be
referenced by the program expression constructor.prototype, and properties added
to an object's prototype are shared, through inheritance, by all objects sharing
the prototype.
-- quote --

The key is the phrase "When a constructor creates an object".  Not after.  I see
no justification in the ECMAScript specs for maintaining such a relationship
after the object construction is complete.

This hurts my concept of prototyping a bit.  (I'd welcome someone citing a
reference in the ECMAScript specs showing me to be totally wrong, but I don't
think I am.)
Confirming the described behavior. Note it contrasts with the following 
example, where the property given to the prototype is a number:


function test() {}
test.prototype.a = new Number()
0

x = new test()
y = new test()

js>x.a = 3
3
js> y.a
0


My attempt at an explanation:


In my example, when we do something like x.a = 3, we are actually overriding
the inherited value x.a = 0. In Alex's example, we are NOT overriding a, 
which retains its value as an object. We are simply adding a property to it. 


Note we could also have done something like this: 

function test() {}
x = new test()
y = new test()

test.prototype.a = new Object()

typeof x.a
object
typeof x.b
object


Thus - even though test.prototype.a was created AFTER x and y were
instantiated as new test() objects, both x and y see a. This makes 
Alex's example less surprising. When Alex's script tries to resolve y.a[0], 
it looks up the prototype chain:

1. Is "a" a direct property of y? NO 
   (You can try y.hasOwnProperty('a') to check this)

2. Is "a" a property of y.__proto__? YES
   So when we reference x.a, we are really accessing x.__proto__.a

3. What is the value of y.__proto__.a? The same as x.__proto__.a.
   x and y share the test.prototype object. Neither one has overridden
   a with a direct property of x's or y's own. Therefore x.a === y.a;
   therefore x.a[0] === y.a[0]



Verifying all this in the standalone JS shell: 


function test() {}
test.prototype.a = new Object()
x = new test()
y = new test()
x.a[0] = 3
3

x.hasOwnProperty('a')
false
y.hasOwnProperty('a')
false

x.__proto__.hasOwnProperty('a')
true
y.__proto__.hasOwnProperty('a')
true


x.__proto__ === y.__proto__
true
x.__proto__.a === y.__proto__.a
true
x.__proto__.a[0] === y.__proto__.a[0]
true

x.__proto__.a[0]
3
y.__proto__.a[0]
3
I believe the bug as stated is invalid. I will search for any further
ECMA references I can find to add to this bug. 

I will wait for rogerl to decide if I have explained this properly - 
Yes, this is actually the point of the ECMAscript prototype - namely to provide 
an object that is shared amongst all new instances of the constructing function. 
When test.prototype.a was assigned, it became available to all 'test' objects, 
regardless of time of construction because the association with the prototype is 
by reference, not by copy. 
Status: UNCONFIRMED → RESOLVED
Closed: 23 years ago
Resolution: --- → INVALID
Marking Verified - 

Thanks, Alex, for this report. I learned a lot from it!
Thanks, rogerl -
Status: RESOLVED → VERIFIED
More insight:


ALEX'S EXAMPLE (defining an Object property on the prototype): 

function test() {}
test.prototype.a = new Object()
x = new test()
y = new test()

x.a[0] = 3
3
x.a === x.__proto__.a
true
x.hasOwnProperty('a')
false



MY EXAMPLE (defining a Number() property on the prototype)

function test() {}
test.prototype.a = new Number()
0
x = new test()
y = new test()

x.a
0
x.a === x.__proto__.a
true
x.hasOwnProperty('a')
false


x.a = 3
3
x.a === x.__proto__.a
false
x.hasOwnProperty('a')
true


We can see that in my example, setting x.a = 3 has destroyed the 
identity between x.a and x.__proto__.a, and now 'a' has been added
as a direct property of x with the value 3.
Okay, stupid question:  how can I make sure each instance of the a property is
unique???  (Besides placing a directly in the function -- something I could do,
but I'm not sure I like as much as I like prototyping.)

Based on this report, I think it may be time to add another comment to bug #
76054....
Oops, I mean bug #70879.  Sorry for the spam.
Alex: 

In order for each instance of the Object property 'a' to be unique,
you would have to put it in the constructor function. Any property 
or method held in the prototype will be shared among all instances, 
until it gets explicitly overridden on a given instance.

I am told that in the original versions of JavaScript, properties
of the built-in objects (like String, etc.) were stored in the 
object instances themselves. Imagine, for example, the toUpperCase() 
method of Strings being held separately in each string instance. 
Nowadays, it is shared from String.prototype instead: 

var s = 'hello'
s.hasOwnProperty('toUpperCase')
false
s.__proto__.hasOwnProperty('toUpperCase')
true


In an HTML script, for example, many string instances are defined. 
Instead of each instance carrying its own instance of toUpperCase, 
they all share the same toUpperCase function object held on String.prototype.
This offers a big savings in memory. 

The price is, whenever it comes time to invoke toUpperCase, JavaScript
must start looking for it up the prototype chain. This chain starts 
with the properties directly held on the string instance itself 
(where the programmer may have overridden String.prototype.toUpperCase 
with some toUpperCase function of his own on that particular instance).
The chain continues with the properties held on the prototype, then on up
to the prototype of the prototype, etc. if necessary until 'toUpperCase'
is found - so the memory savings comes at a price of dynamic lookup.

But if you want separate 'a' object property instances for each of your 
test() objects, you'll have to go back to that earlier notion and define 
'a' right inside the test() constructor -
You need to log in before you can comment on or make changes to this bug.