Tobie Langel
2006-Oct-25 03:43 UTC
Bug ? Different instances of an object created through Class.create() share the same Array Object
Hi all, Noticed the following while working on a script: var Tester = Class.create(); Tester.prototype = { initialize: function(element) { this.array[0] = element; }, array: [] } var t1 = new Tester(''hello''); var t2 = new Tester(''world''); console.log(t1.array.inspect()) // returns [''world''] console.log(t2.array.inspect()) // returns [''world''] For those equipped with Firebug (and therefore Firefox) there''s a live version here: http://tobielangel.com/code/test.html which will print the arays'' values to your console. This issue/bug is due to the fact that both arrays actually reference the same Array object. The following will yield the expected outcome (because the array is actually created during initialization): var Tester = Class.create(); Tester.prototype = { initialize: function(element) { this.array = []; this.array[0] = element; } } var t1 = new Tester(''hello''); var t2 = new Tester(''world''); console.log(t1.array.inspect()) // returns [''hello''] console.log(t2.array.inspect()) // returns [''world''] In my opinion, this is a bug. I''m not too familiar with the way Class functions, but it looks like some Object + array cloning should be applied at some point. Any suggestions ? Regards, Tobie --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs -~----------~----~----~----~------~----~------~--~---
Christophe Porteneuve
2006-Oct-25 06:20 UTC
Re: Bug ? Different instances of an object created through Class.create() share the same Array Object
Tobie Langel a écrit :> var Tester = Class.create(); > Tester.prototype = { > initialize: function(element) { > this.array[0] = element; > }, > array: [] > }This is nominal. Your array field is in the PROTOTYPE. If you want to make an awkward OOP-based comparison, it''s a STATIC CLASS MEMBER. So of course your instances share it. -- Christophe Porteneuve a.k.a. TDD "[They] did not know it was impossible, so they did it." --Mark Twain Email: tdd-x+CfDp/qHev2eFz/2MeuCQ@public.gmane.org --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs -~----------~----~----~----~------~----~------~--~---
Tobie Langel
2006-Oct-25 06:28 UTC
Bug ? Different instances of an object created through Class.create() share the same Array Object
Hi Christophe, What bothers me is the fact that in the following case, two instances of the Tester class would NOT share the same string.>> var Tester = Class.create(); >> Tester.prototype = { >> initialize: function(str) { >> this.string = str; >> }, >> string: '''' >> }In that case, this.string would just be an instance property, wouldn''t it? regards, Tobie On 25 oct. 2006, at 02:20, Christophe Porteneuve wrote:> > Tobie Langel a écrit : >> var Tester = Class.create(); >> Tester.prototype = { >> initialize: function(element) { >> this.array[0] = element; >> }, >> array: [] >> } > > This is nominal. Your array field is in the PROTOTYPE. If you > want to > make an awkward OOP-based comparison, it''s a STATIC CLASS MEMBER. > So of > course your instances share it. > > -- > Christophe Porteneuve a.k.a. TDD > "[They] did not know it was impossible, so they did it." --Mark Twain > Email: tdd-x+CfDp/qHev2eFz/2MeuCQ@public.gmane.org > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs -~----------~----~----~----~------~----~------~--~---
tobie
2006-Oct-25 06:30 UTC
Re: Bug ? Different instances of an object created through Class.create() share the same Array Object
That''s not very consistent really. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs -~----------~----~----~----~------~----~------~--~---
Christophe Porteneuve
2006-Oct-25 07:14 UTC
Re: Bug ? Different instances of an object created through Class.create() share the same Array Object
Hey Tobie, Ah, I should have waited for breakfast caffeine to sink in before answering you, sorry :-) Prototype-defined fields are *not* static fields /per se/. You would have to work on the constructor function''s prototype to obtain something along those lines. The issue you run into here is due to how prototypes are used by the new operator. Basically, every instance refers, by default, to the properties defined in the prototype, dynamically. It''s like they automatically proxy-delegate to the prototype, with the "this" binding going on for methods. Don''t forget that object variables are just references. If you do this in JavaScript: var a = [1, 2, 3]; var b = a; a[1] = ''X''; Then b will become [1, ''X'', 3]. The "b = a" expression just copies a pointer, so to speak. Both variables reference the same object. It''s fortunate that prototypes work this way, otherwise each instance would hold, for instance, a distinct copy of all methods and fields defined in the prototype. Which would be a pretty bad way to go about defining methods. When you want per-instance fields that are still object references, you need to assign them to "this."-prefixed properties in the initialize function (called at construction time, see Class.create). This way, each "new", calling initialize, will use a fresh value (i.e. a fresh object) for its field, therefore a unique reference. Look into Prototype and script.aculo.us code: you''ll see that all prototype-defined fields are basically used as constants (e.g. the NODEMAP and ATTR_MAP fields in Builder). All per-instance fields are assigned, directly or indirectly, from the initialize functions. As for this code: Tobie Langel a écrit :>>> var Tester = Class.create(); >>> Tester.prototype = { >>> initialize: function(str) { >>> this.string = str; >>> }, >>> string: '''' >>> }The last string definition is useless: it''s going to be overridden locally by every instance, because of the initialize function, that guarantees each instance has its own reference. I hope this makes it a bit clearer. -- Christophe Porteneuve aka TDD tdd-x+CfDp/qHev2eFz/2MeuCQ@public.gmane.org --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs -~----------~----~----~----~------~----~------~--~---
Tobie Langel
2006-Oct-25 07:24 UTC
Re: Bug ? Different instances of an object created through Class.create() share the same Array Object
Thanks for clearing it up, it makes more sense like this. So this should be considered as a feature of JavaScript rather than a Prototype bug. I spent about half a day debugging a Class because of that feature... ;-) Regards, Tobie On 25 oct. 2006, at 03:14, Christophe Porteneuve wrote:> > Hey Tobie, > > Ah, I should have waited for breakfast caffeine to sink in before > answering you, sorry :-) > > Prototype-defined fields are *not* static fields /per se/. You would > have to work on the constructor function''s prototype to obtain > something > along those lines. > > The issue you run into here is due to how prototypes are used by > the new > operator. Basically, every instance refers, by default, to the > properties defined in the prototype, dynamically. It''s like they > automatically proxy-delegate to the prototype, with the "this" binding > going on for methods. > > Don''t forget that object variables are just references. If you do > this > in JavaScript: > > var a = [1, 2, 3]; > var b = a; > a[1] = ''X''; > > Then b will become [1, ''X'', 3]. The "b = a" expression just copies a > pointer, so to speak. Both variables reference the same object. > > It''s fortunate that prototypes work this way, otherwise each instance > would hold, for instance, a distinct copy of all methods and fields > defined in the prototype. Which would be a pretty bad way to go about > defining methods. > > When you want per-instance fields that are still object references, > you > need to assign them to "this."-prefixed properties in the initialize > function (called at construction time, see Class.create). This way, > each "new", calling initialize, will use a fresh value (i.e. a fresh > object) for its field, therefore a unique reference. > > Look into Prototype and script.aculo.us code: you''ll see that all > prototype-defined fields are basically used as constants (e.g. the > NODEMAP and ATTR_MAP fields in Builder). All per-instance fields are > assigned, directly or indirectly, from the initialize functions. > > As for this code: > > Tobie Langel a écrit : >>>> var Tester = Class.create(); >>>> Tester.prototype = { >>>> initialize: function(str) { >>>> this.string = str; >>>> }, >>>> string: '''' >>>> } > > The last string definition is useless: it''s going to be overridden > locally by every instance, because of the initialize function, that > guarantees each instance has its own reference. > > I hope this makes it a bit clearer. > > -- > Christophe Porteneuve aka TDD > tdd-x+CfDp/qHev2eFz/2MeuCQ@public.gmane.org > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs -~----------~----~----~----~------~----~------~--~---
Ryan Gahl
2006-Oct-25 14:27 UTC
Re: Bug ? Different instances of an object created through Class.create() share the same Array Object
It''s not just a feature of Javascript... having to initialize instance level members in a class''s construction is a characteristic of not only javascript, but any object oriented language I''ve ever seen. For instance, you can define a publically accessible property on a class in java or c#, but for the property getter method to be able to return a valid object reference at any point in the life of that object, you will have had to have set it to a valid object reference at some point prior to that, and usually you''d do that via initializing it during construction. This also has the added benefit of not allocating that memory until it''s needed. In your js example you are assuming that there is magically going to be a new array allocated in memory, without you having to explicitly create one. The confusion can be expected though, since you can call methods of the prototype using this.someMethod(), and the "this" inside that method will refer to your instance, so it''s natural to equate that to the classical model where each instance has it''s own copy of that method... however you are still calling the prototype''s method (there wasn''t a new method copied into memory for your instance, it''s just that your instance was bound as the callee to the prototype''s method). That''s the main difference between js''s prototypical OO and the classical OO you''re used to. All js object instances are sharing any access to their prototype''s properties/members (be it methods or arrays or whatever). In a language like java or c#, you CAN define a class like the following pseudo-code, and it will work as you were expecting your javascript to work. Just keep in mind though that the compilation step in those other languages handles delegating the initialization of those variables to the object''s internal constructor. <pseudocode> class someJavaOrC#Class { //leaving out actual getter/setter stuff for pseudo-code purposes public Array myArray = new Array(); //constructor public someJavaOrC#Class(string someInitialString) { myArray[0] = someInitialString; } } </pseudocode> On 10/25/06, Tobie Langel <tobie.langel-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > > Thanks for clearing it up, it makes more sense like this. > > So this should be considered as a feature of JavaScript rather than a > Prototype bug. > > I spent about half a day debugging a Class because of that > feature... ;-) > > Regards, > > Tobie > > > On 25 oct. 2006, at 03:14, Christophe Porteneuve wrote: > > > > > Hey Tobie, > > > > Ah, I should have waited for breakfast caffeine to sink in before > > answering you, sorry :-) > > > > Prototype-defined fields are *not* static fields /per se/. You would > > have to work on the constructor function''s prototype to obtain > > something > > along those lines. > > > > The issue you run into here is due to how prototypes are used by > > the new > > operator. Basically, every instance refers, by default, to the > > properties defined in the prototype, dynamically. It''s like they > > automatically proxy-delegate to the prototype, with the "this" binding > > going on for methods. > > > > Don''t forget that object variables are just references. If you do > > this > > in JavaScript: > > > > var a = [1, 2, 3]; > > var b = a; > > a[1] = ''X''; > > > > Then b will become [1, ''X'', 3]. The "b = a" expression just copies a > > pointer, so to speak. Both variables reference the same object. > > > > It''s fortunate that prototypes work this way, otherwise each instance > > would hold, for instance, a distinct copy of all methods and fields > > defined in the prototype. Which would be a pretty bad way to go about > > defining methods. > > > > When you want per-instance fields that are still object references, > > you > > need to assign them to "this."-prefixed properties in the initialize > > function (called at construction time, see Class.create). This way, > > each "new", calling initialize, will use a fresh value (i.e. a fresh > > object) for its field, therefore a unique reference. > > > > Look into Prototype and script.aculo.us code: you''ll see that all > > prototype-defined fields are basically used as constants (e.g. the > > NODEMAP and ATTR_MAP fields in Builder). All per-instance fields are > > assigned, directly or indirectly, from the initialize functions. > > > > As for this code: > > > > Tobie Langel a écrit : > >>>> var Tester = Class.create(); > >>>> Tester.prototype = { > >>>> initialize: function(str) { > >>>> this.string = str; > >>>> }, > >>>> string: '''' > >>>> } > > > > The last string definition is useless: it''s going to be overridden > > locally by every instance, because of the initialize function, that > > guarantees each instance has its own reference. > > > > I hope this makes it a bit clearer. > > > > -- > > Christophe Porteneuve aka TDD > > tdd-x+CfDp/qHev2eFz/2MeuCQ@public.gmane.org > > > > > > > > > >-- Ryan Gahl Application Development Consultant Athena Group, Inc. Inquire: 1-920-955-1457 Blog: http://www.someElement.com --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs -~----------~----~----~----~------~----~------~--~---
tobie
2006-Oct-26 03:40 UTC
Re: Bug ? Different instances of an object created through Class.create() share the same Array Object
Hi Ryan, Thanks for clearing this up. You''ve been really helpful. Regards, Tobie --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs -~----------~----~----~----~------~----~------~--~---