Javascript inheritence .Why does my class share nested member data? - javascript

I am having difficulty with the following inheritance code that I read in Crockfords book.
When I create two instances of a Column object the "this.name" member data is different in each instance, as I would have expected.
However, when I use nested member data "this.model.name" it is always shared among the instances.
Can anyone suggest a way to fix this?
var BaseColumn = function() {
this.model = {};
this.model.name = "";
this.name="";
};
var Column = function (name) {
this.model.name = name;
this.name = name;
};
Column.prototype = new BaseColumn();
var col1 = new Column("Column1");
var col2 = new Column("Column2");
alert(col1.name); //Returns "Column1"
alert(col2.name); //Returns "Column2"
alert(col1.model.name); //Returns "Column2"
alert(col2.model.name); //Returns "Column2"

This is because every instance of Column will have a reference to the SAME model object in the prototype. Creating the second Column overwrites the previous value of model.name.
Let's look at your code more closely to see why you are getting what you are:
var BaseColumn = function() {
this.model = {};
this.model.name = "";
this.name="";
};
This creates a type BaseColumn constructor that will create properties model and name on the new object when invoked using new.
var Column = function (name) {
this.model.name = name;
this.name = name;
};
This creates a type Column constructor that will fail. If you called new Column() now, it would fail on this.model (at this moment).
Column.prototype = new BaseColumn();
This REPLACES the existing prototype with a NEW prototype created from a new instance of BaseColumn. This object contains the properties I mentioned above for this type, and the property values will be SHARED across ALL Column objects. Why? Because if the local Column instance (col1 or col2) doesn't contain a property, the prototype chain is searched for one. Since all Column instances shared the same prototype, they all end up referencing the same values.
var col1 = new Column("Column1");
Did you know that at this point, Column.prototype.model.name == "Column1"?
var col2 = new Column("Column2");
Column.prototype.model.name is now "Column2"`. You see, the prototype is NOT duplicated. It is SHARED.
If the desire is to have one model per instance, you will have to construct a new model object for each new Column.
For a better way to approach inheritance, take a look at the answer by Juan Mendes. In this pattern, you are calling the base constructor explicitly from the constructor of your sub type. This will add/construct the base properties, and should be placed at the top of the sub type's constructor (usually).

The answer provided by James Wilkins is correct. There is only one instance of BaseColumn being created and it's shared by all instances of Columns through Column.prototype
However, it's worth noting that your way of establishing inheritance is incorrect. There's no need to instantiate a parent just to set up inheritance, and you need to call the parent's constructor from your constructor.
See my posts at http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html and http://js-bits.blogspot.com/2014/10/understanding-prototypical-inheritance.html for further details
The following example works fine.
var BaseColumn = function() {
this.model = {};
this.model.name = "";
this.name = "";
};
var Column = function(name) {
// Call the parent's constructor, a new model object will be created
// instead of using a shared one from the prototype
BaseColumn.apply(this);
this.model.name = name;
this.name = name;
};
Column.prototype = Object.create(BaseColumn.prototype);
var col1 = new Column("Column1");
var col2 = new Column("Column2");
console.log(col1.name); //Returns "Column1"
console.log(col2.name); //Returns "Column2"
console.log(col1.model.name); //Returns "Column1"
console.log(col2.model.name); //Returns "Column2"

This is because the value of model is inherited from BaseColumn for all instances of Column. You need to explicitly assign a value to the model property to make it unique. The way to can tell is by evaluating col.__proto__.model and see that model is indeed part of the prototype.

The answer is very simple: the {} is parsed once by JS and refers to the identical object in each instance you create. To create a new, different object for each instance, just do something like
this.model = Object.create();
or
this.model = new Object();
or
this.model = function() { return {}; }();
However, there are other issues with how you've got inheritance set up, that the other answers may help you with.

Related

Javascript inheritance pattern

I am writing domain objects in Javascript which gets populated with the database fields. Suppose I have two objects dog and cat and I have following constructor function definition:
function Dog(opt_data) {
var data = opt_data || {};
this.createdAt = data['created_at'];
this.updatedAt = data['updated_at'];
this.name = data['name'];
this.breed = data['breed'];
}
function Cat(opt_data) {
var data = opt_data || {};
this.createdAt = data['created_at'];
this.updatedAt = data['updated_at'];
this.name = data['name'];
this.fur = data['fur'];
}
Now, both of the above objects have craetedAt and updatedAt properties. So, should I create a new class BaseModel which has there properties and let all the objects inherit that or is there any better alternative in javascript for this pattern?
Update 1:
My understanding from comments and answer.
function Cat(opt_data) {
var data = opt_data || {};
this.name = data['name'];
this.fur = data['fur'];
this.updateTimestamp(data);
}
Cat.prototype = Object.create({
updateTimestamp: function(data) {
this.createdAt = data['created_at'] || new Date();
this.updatedAt = data['updated_at'] || new Date();
}
});
Unless the createdAt and updatedAt values have some common supporting methods or accessors that you need to define on both Dog and Cat objects, just set the attributes to whatever value you need them to be.
Since you don't declare object members in JavaScript (the way you would in C++, C#, Java, etc.), there's nothing to be gained by inheriting from a BaseModel prototype in the case you have proposed. That is to say, since you don't have to do anything in JavaScript to create the createdAt and updatedAt attributes other than to simply assign to them, a base type does not really provide anything useful because you would just have to assign those attributes in the base type constructor anyway.
Where you may need a base type is if both objects need to have similar methods to save and load data (presumably automatically updating the updatedAt attribute when saving). In this case, giving both Dog and Cat a prototype with save and load methods would be a useful application of the prototypical inheritance pattern.

create subclass object with reference to existing object

In node.js (Javascript) I have two classes, class mainclass and subclass, where subclass inherites from mainclass.
In an other module (other class, other .js file) i have an array of objects from class mainclass:
myArray[0] = new mainclass();
myArray[1] = new mainclass();
//etc..
On runtime, i want to create a new subclass object, and set its reference to the one in myArray[0], so that myArrayis not changed, but myArray[0] then returns the new subclass object.
And i want to do this in the mainclass, so that the array is not changed, but the reference in the array points now to an other object (the new subclass object). In fact i want to do something like
this = new subclass();
in a method in mainClass
mainClass.prototype.changeType = function(){
this = new subclass();
}
which of course doesnt work because you cant assign value to this.
You could "simulate" pointers if you are ready to access your objects through indexes. As you can see below, whatever object reference is at index 0, it remains available :
function Person (name) { this.name = name; };
Person.prototype.whoami = function () { return this.name };
memory = [];
memory.push(new Person("Hillary Clinton"));
memory[0].whoami(); // "Hillary Clinton"
memory[0] = new Person("Donald Trump");
memory[0].whoami(); // "Donald Trump"
Good luck though... x-D

Value of constructor and prototype gets changed after over writing the prototype object. Why?

I have the Director() function. I have created 2 instances AlfredH and JohnD out of Director() constructor. I did not write the prototype object.
function Director(){
this.genre = "Thriller";
}
var AlfredH = new Director();
var JohnD = new Director();
If I check the values of JohnD.constructor; and JohnD.constructor.prototype; I get Director() and Object() respectively.
But, if I add properties to prototype object of Director() like the below:
function Director(){
this.genre = "Thriller";
}
Director.prototype = {
noir: true
};
var AlfredH = new Director();
var JohnD = new Director();
and if I check the values of JohnD.constructor; and JohnD.constructor.prototype; I get Object() and Object() respectively. Can anyone explain this behavior? and the same can be extended to the value of JohnD.constructor.prototype.constructor;
var a = {
value:22;
}
then
var a = {
somethingelse:0
}
Can you guess what a.value is?
You are overwriting the prototype with another object.
Then add to that that
console.log({}.constructor)===Object;//=true
Maybe try adding it like this:
Director.prototype.noir = true;
Note that anything on the prototype is shared among instances, this is a good thing because it saves memory and instantiate the object quicker with less cpu.
When assigning a new value the value is assigned to the instance but when manipulating the value through functions it affects all instances
Director.prototype.someArray=[];
var d1=new Director();
var d2=new Director();
d1.someArray.push(22);
console.log(d2.someArray);//=[22]
More info on prototype here: https://stackoverflow.com/a/16063711/1641941

use of prototype in javascript

I am learning prototype in JavaScript and this is the code I am trying -
<script>
function employee(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var trialcoder = new employee('trialcoder', 26, 'M');
//employee.prototype.salary = null;
trialcoder.salary = 19000;
document.write("salary is "+ trialcoder.salary);
</script>
My thoughts- To add another property we need to use prototype like - employee.prototype.salary = null; so on un commenting this line, I was expecting an error but it was not..let me know where I am wrong in the prototype concept.
Code Source - http://www.w3schools.com/jsref/jsref_prototype_math.asp
Your code is correct, because when you called
var trialcoder = new employee('trialcoder', 26, 'M');
You got an object instance of employee and just like any other object you can add properties to your trialcoder object like
trialcoder.salary = 19000;
In this case, the salary property is only available to your trialcoder object and if you make another instance of employee like var another = new employee() you have no salary property in another object, but, if you do something like
function employee(name, age, sex) { //... }
employee.prototype.salary = 19000;
and then make instances like
var anEmp = new employee();
console.log(anEmp.salary); // 19000
Make another instance
var newEmp = new employee();
console.log(newEmp.salary); // 19000
if you want, you can
newEmp.salary = 10000;
console.log(anEmp.salary); // 10000
Which means, when you add a property in the prototype of a constructor (employee) then every object instance can share the same property and after making an instance from the constructor, you can change the property of an instance but this won't effect other instances. Hope it's clear enough now.
Your code is right and you will not receive error because using prototype your setting property salary of class employee and after creating an object of your class ur are setting the property for that specific object,if you create another object you can set its property salary too
If you set property using prototype then all objects of that class will share that (salary) property .

JavaScript creating new instance of objects

So I am designing a grade book interface and I have a course defined as:
<script>
course = new Object();
var name;
var gradingareas;
var finalgrade;
</script>
then later I want to create a new instance:
var gradingareas = new Array("Homework", "Classwork", "Exams");
course1 = new course("CS1500", gradingareas, 85);
I have also tried without the var in front to no avail. I get an "Uncaught TypeError: Object is not a function" I am very new to javascript so I don't even know if Im going about this the correct way. Any help is appreciated Thanks.
Your existing code:
// Creates a new, empty object, as a global
course = new Object();
// Creates three new variables in the global scope.
var name;
var gradingareas;
var finalgrade;
There is no connection between the variables and the object.
It looks like you want something more like:
function Course(name, gradingareas, finalgrade) {
this.name = name;
this.gradingareas = gradingareas;
this.finalgrade = finalgrade;
}
Then:
var course1 = new Course("CS1500", gradingareas, 85);
Note the use of a capital letter for naming the constructor function. This is a convention in the JS community.
JS is prototypical, rather than class based and if you are new to it there are advantages to learning this immediately rather than trying to mush classical inheritance models from it, however, classical inheritance is alive and well in JS.
Anyhow, to answer how you would access your variables:
course1.name works fine with the example above.
If you wanted to privatise your data you could take this approach using closure:
var Course = function(name, grade) {
// Private data
var private = {
name: name,
grade: grade
}
// Expose public API
return {
get: function( prop ) {
if ( private.hasOwnProperty( prop ) ) {
return private[ prop ];
}
}
}
};
Then instantiate a new object:
var course = new Course('Programming with JavaScript', 'A');
and start using all that private data:
course.get('name');
Of course, you'd probably want setters to manipulate that data too ;)
The code that you described does the following:
// Declares a memory variable called course and stores and object in it
var course = new Object();
// Declares three variables
var name;
var gradingareas;
var finalgrade;
These declared variables aren't automatically connected to the object. If you want these properties declared on the object you have 2 options:
Declare them as properties of the object
Declare them on the prototype of of the object
Example1: declare them as properties of the object:
// Declares a memory variable called course and stores and object in it
var course = new Object();
// Access or create new properties with . or [] operator
course.name = 'math';
course.gradingareas = 'muliple';
course['finalgrade'] = 'A'
console.log(course);
Example2: Declare them on the prototype:
// Create a constructor function
function Course (name, grade) {
this.name = name;
this.grade = grade;
}
// course is added on the prototype
Course.prototype.gradingareas = 'some gradingareas';
// the name and the grade are added on the object itself
var course = new Course ('willem', 10);
console.log(course);
To create a very simple object with constructor and default values, you can do :
//My object with constructor
var myObjectWithConstrutorFunction = {
//construtor function with default values in constructor
myConstrutor: function(Name = 'bob', Age = 18){
this.Name = name;
this.Age = age;
}
};
// instance
var myInstance = new myObjectWithConstrutorFunction.myConstrutor();
// show on console
console.log('object with constructor function: ', myInstance);
// show properties
console.log(myInstace.Name, myInstance.Age);
PS : It's a good practice create a constructor's name with the same name of the class, if you are creating a external class.

Categories

Resources