Why does this javascript inheritance cause references to share array? [duplicate] - javascript

This question already has answers here:
Javascript "OOP" and prototypes with multiple-level inheritance
(3 answers)
Closed 9 years ago.
i have a base class
function Base(){
this.children = new Array();
}
Base.prototype.Add = function(child){
this.children.push(child)
}
function Sub1(){...}
Sub1.prototype = new Base();
function Sub2(){...}
Sub2.prototype = new Base();
so how come when i do
var S1_1 = new Sub1();
var S1_2 = new Sub1();
S1_1.Add(new Sub2());
S1_2 for some reason also has 1 child and contains the same information as the child i added to S1_1?

It's because that's how prototypal inheritance works. All your Sub1 objects inherit from a common Base object. Because the Base object holds the Array, all Sub1 instances share that Array.
In other words, when you ask for the .children property of each Sub1 object, they will see that they don't own such a property, and will therefore look for it on the prototype from which they inherit. Since they inherit from the same prototype object, they use the same Array.
For each Sub1 to have its own Array, you should define it in the Sub1 constructor.
function Base(){
this.children = new Array();
}
Base.prototype.Add = function(child){
this.children.push(child); // `this` will be whatever object invoked the method
}
function Sub1(){
this.children = [];
}
Sub1.prototype = new Base();
function Sub2(){
this.children = [];
}
Sub2.prototype = new Base();

You didn't take ownership/copy of the Base variables defined with this when creating a sub, you can do so with:
function Sub1(){
Base.call(this);
}
What that code does is calling Base with the Sub1 instance as the this context.
More on prototype behavior can be found here: Prototypical inheritance - writing up

Related

Prototypal Inheritance: Copying a function prototype to another

What is the harm in copying the prototype of a function to another function like shown below.
function Person(){}
Person.prototype = {};
function Author(){}
Author.prototype = Person.prototype;
Object assignments in JS create a reference.
var o = {};
var c = o;
Now both the objects o and c are referring to the same object. Same rule applies when trying to assign the prototype of one object to another.
Author.prototype = Person.prototype;
Now the prototypes of both Author and Person refers to a single object. If you put some data to a prototype property of the Author, the same data will be there for the Person too. This is least expected for distinct objects.
One of the proper ways of doing this is
Author.prototype = Object.create(Person.prototype);
Here you create a brand new object for Author.prototype - but inheriting from Person object.
Because you're passing the prototype by reference, which means both are affected by all changes. Consider:
function Person(){}
Person.prototype = {};
function Author(){}
Author.prototype = Person.prototype;
Author.prototype.books = [];
var bob = new Person();
console.log(bob.books); // this will be an empty array, not undefined as one would expect.
The Ideal way of attaching methods to prototype is by creating an object which is created by instance.
function Person(){
this.test = "1";
}
function Author(){}
Author.prototype = new Person();
this way you create a new instance of person and returned object is fed into Author.
What if you simply copy it ?
If you simply copy the same instance is shared across prototypes , change in one prototype will be reflected in all.
function b(){
this.test = 'a';
this.test1 = function(){
console.log('test1');
}
this.test2 = function(){
console.log('test2');
}
}
function a(){
}
function c(){
}
a.prototype = new b();
var a1 = new a();
c.prototype = a.prototype;
a.test = 'asdf';
console.log(c.test);
Uniqueness of data to an instance will be missing.

Inherited child objects share the same array property in JavaScript?

> function Parent() {this.arr = [];}
undefined
> function Child() {}
undefined
> Child.prototype = new Parent();
{ arr: [] }
> child1 = new Child();
{}
> child2 = new Child();
{}
> child1.arr.push('From child1');
1
> child2.arr
[ 'From child1' ]
>
Given the above, I would expect child2.arr to be empty as it is its own object. How can I have child1 and child2 contain their own arr? Thanks!
You'll have to make the assignment in your constructor:
function Child() {
this.arr = this.arr ? this.arr.slice(0) : [];
}
That gives each child a copy of the prototype's .arr array, if it exists. (It's just a shallow copy, and that's just a possible example.) Assignments to object properties always involve local ("own") properties of the target object, but references involve the prototype chain.
Also not that for fairly arcane reasons it's not a good idea to initialize a prototype like that. Better to use Object.create:
Child.prototype = Object.create(Parent.prototype);
That gives you a new object that uses the Parent prototype object as its prototype.
Here is a somewhat old but still interesting question on the topic.
Look into inheritance in javascript
Basically if the property is not on the object you are looking into javascript looks into it's prototype and tries to find it there until it reaches the Object.
Your Child does not have property arr but it's prototype new Parent() does and that one is referred from Child1 and Child2.
function Parent() { this.arr = []; }
function Child() { this.parent = new Parent(); }
Child.prototype = new Parent();
var child1 = new Child();
var child2 = new Child();
child1.parent.arr.push('From child1');
alert(child2.parent.arr);

Is it better to declare variables inside or outside the prototype property? [duplicate]

This question already has answers here:
Use of 'prototype' vs. 'this' in JavaScript?
(15 answers)
Closed 7 years ago.
Which of the following cases is better for performance or proper techniques?
CASE #1
function SomeClass ()
{
this.someVar = null;
this.someFunc = function () {}
}
CASE #2
function SomeClass () {}
SomeClass.prototype.someVar = null;
SomeClass.prototype.someFunc = function () {}
It depends entirely on whether you want them shared between instances created via that constructor function. If you do, put them on the prototype. If you don't, set them up in the constructor.
Beware that putting references to objects/arrays on the prototype is likely to trip you up, as (again) all instances will share those references.
Putting methods (references to functions) on the prototype is fairly standard practice.
Here's an example of getting tripped up by putting an array on the prototype:
function MyConstructor() {
};
MyConstructor.prototype.theArray = []; // Don't do this unless you're really sure
var a = new MyConstructor();
a.theArray.push("foo");
snippet.log(a.theArray.length); // 1 -- so far, everything seems fine
var b = new MyConstructor();
b.theArray.push("bar");
snippet.log(b.theArray.length); // 2 -- huh?!?!
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
The reason, of course, is that both a and b are using the same array: The one on the prototype.
if they are shared among all of the instances of SomeClass, It's better to declare it in the prototype because they will share the same reference which reduces the amount of memory.
but if these properties varies from instance to another, you must declare it in the constructor.
Examples:
function SomeClass () {}
SomeClass.prototype.someVar = null;
SomeClass.prototype.someFunc = function () {}
var a = new SomeClass();
var b = new SomeClass();
a.someFunc === b.someFunc //true because they share the same reference
within constructor:
function SomeClass ()
{
this.someVar = null;
this.someFunc = function () {}
}
var a = new SomeClass();
var b = new SomeClass();
a.someFunc === b.someFunc //false because the now have difference reference
I think the first one will be faster
function SomeClass ()
{
this.someVar = null;
this.someFunc = function () {}
}
because in this case while accessing someClassInstace.someFunc javascript will not look up in prototype for someFunc function.

Javascript: How to create a new instance of the prototype object? [duplicate]

This question already has answers here:
B extends A, but B.add populates A.prototype.property
(2 answers)
Closed 9 years ago.
The prototype is an object that is inherited? by all instances of the object, like child in my following example.
It has to be an instance of the parent, else parent's prototype will not get inherited?
In this case, the goal is to create a separate array, which is inherited from parent, for each instance of child.
I am unsure how to achieve that exactly. I know of extend.
Is the extend method simply copying prototypes over to a new object and applying the new methods onto it as well?
My code example + jsfiddle:
function parent(){
}
parent.prototype.arr = [];
parent.prototype.add = function(item){
this.arr.push(item);
}
parent.prototype.show = function(){
for(var i = 0; i < this.arr.length; ++i){
$('body').append(this.arr[i]);
}
}
function child(childname){
this.add(childname);
}
child.prototype = new parent();
var child1 = new child('child1');
var child2 = new child('child2');
child2.show();//this should only show child2?
To give each instance its own array, don't use the prototype to hold the array because it is shared among all instances. You can initialize a new array in the constructor of parent and then in the child class make sure you call the parent's constructor:
function parent(){
this.arr = [];
}
function child() {
parent.call(this); // call parent constructor
}
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
Now every parent object will have its own copy of the array including the ones that are part of the child descendants.
See this MDN article for a good explanation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

Trying to understand this code in Javascript

// Sim.App - application class (singleton)
Sim.App = function()
{
this.renderer = null;
this.scene = null;
this.camera = null;
this.objects = [];
}
// Constructor
EarthApp = function()
{
Sim.App.call(this);
}
// Subclass Sim.App
EarthApp.prototype = new Sim.App();
=============================================
In the above, I don't understand why the author used this statement
EarthApp.prototype = new Sim.App();
He could have used
EarthApp = new Sim.App();
Please help me understand the use of 'prototype' within that statement .
Prototypes are a fundamental piece in Javascript's inheritance model. I recommend you read about it, because without understading it you'll not "get" JS fully.
Having that said, assinging an object as a function's prototype makes this object be in a prototype chain of every instance of this function created afterwards.
The way prototype chain works is (more or less) (see example below):
You try to access "foo" variable in an object
If this object have "foo", return it's value.
If this object does not have "foo", look at it's prototype ('Sim.App' instance in your case) - does it have "foo"? If so, return it's value.
If object's prototype does not have a "foo", look at prototype's prototype - and so on, up through the chain.
If you want to read more on that, have a look at this article.
Example - consider:
var Sim = {};
Sim.App = function () {
this.foo = 'sim';
}
EarthApp = function () {
}
EarthApp.prototype = new Sim.App();
var earth = new EarthApp();
// I can access earth.foo even though 'earth' is an instance of 'EarthApp'
// and not 'Sim.App'. That's because instance of 'Sim.App' is in
// earth's prototype chain.
console.log(earth.foo);
The line
EarthApp.prototype = new Sim.App();
...creates a Sim.App object and assigns it to the prototype property of the EarthApp function. That means that when we do this:
var e = new EarthApp();
...the e object will get the object from EarthApp.prototype as its prototype, giving it access to the properties and methods of that object.
FWIW, the inheritance implemented by that code isn't really ideal, because it's calling the Sim.App constructor to create the EarthApp.prototype object, then calling it again to initialize instances. If you want to chain constructors together in that way, here's the more correct way to do it:
// Reusable `derive` function
// Set up `child`'s `prototype` property to be based on `parent`'s `prototype` property
function derive(child, parent)
{
function ctor()
{
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.prototype.constructor = parent;
}
// Sim.App - application class (singleton)
Sim.App = function()
{
this.renderer = null;
this.scene = null;
this.camera = null;
this.objects = [];
};
// Constructor
EarthApp = function()
{
Sim.App.call(this);
};
// Subclass Sim.App
derive(EarthApp, Sim.App);
You might want to check out my Lineage helper script, which does the above and handles all the plumbing required to handle "supercalls" correctly and such.
It's also worth noting that this is just one way to use JavaScript's prototypical inheritance, which is tremendously flexible.

Categories

Resources