Closed Bug 83784 Opened 24 years ago Closed 24 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: 24 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.