Extending prototypes in Javascript - good way? - javascript

I want to validate that the approach I'm using is correct when it comes to extend a prototype - supposing "extend" is the right word.
This topic gets a lot of clones. I'm still trying to properly understand this topic...
The purpose is:
- to write clean and good code.
- to avoid using frameworks, if possible plain Javascript.
- get advice on the clean frameworks that don't twist JS to obtain class-enabled behaviors.
Here is the Parent prototype of my sandbox:
function Parent(){
}
Parent.prototype = {
"init":function(){
this.name = "anon";
},
"initWithParameters":function(parameters){
this.name = parameters.name ? parameters.name : "anon";
},
"talk": function(){
console.log('Parent is: ' + this.name);
}
}
Now the Child prototype - it adds a "position" property and redefines the behaviors:
function Child(){
Parent.call(this);
}
Child.prototype = new Parent;
Child.prototype.constructor = Child;
Child.prototype.init = function(){
Parent.prototype.call(this);
this.setPosition(0, 0);
}
Child.prototype.initWithParameters = function(parameters){
Parent.prototype.initWithParameters.call(this, parameters);
if(!this.position){
this.position = {x:0, y:0};
}
this.setPosition(parameters.pos.x, parameters.pos.y);
}
Child.prototype.setPosition = function(x, y){
this.position.x = x;
this.position.y = y;
}
Child.prototype.talk = function(){
console.log('Child is: ' + this.name + ' and location is: ' + this.position.x + ', ' + this.position.y);
}
Is this a good practice? Is there no shorthand to avoid writing "Child.prototype." when overriding a property (using a litteral maybe, like the Parent prototype is written).
I know of J. Resig's Class/extend approach. But I'd rather use Javascript as the prototypical language it is, not make it work as a "class-like behaving class-less OO language".
Thanks for your help :-)

In general your approach will work but a better approach will be to replace:
Child.prototype = new Parent;
with:
Child.prototype = Object.create(Parent.prototype);
This way you don't need to call new Parent, which is somewhat an anti-pattern. You could also define new properties directly as follows:
Child.prototype = Object.create(Parent.prototype, {
setPosition: {
value: function() {
//... etc
},
writable: true,
enumerable: true,
configurable: true
}
});
Hope this helps.
Object.create() at MDN

Your approach it is a good pure JavaScript approach. The only get away from tipping "Child.prototype" every time is to put it in a reference variable.
Like:
var children = Child.prototype;
children.init = function(){ /*/someoverridecode*/}
But you are still doing the Child.prototype behind this. You can also define a function that does this for you, see bind of underscore, maybe it suits your needs.
Cheers

I may get deep fried for this suggestion as there are several articles that can argue against some practices in my example, but this has worked for me and works well for clean looking code, stays consistent, minifies well, operates in strict mode, and stays compatible with IE8.
I also like utilizing the prototype methodology (rather than all of the 'extend' or 'apply' styles you see everywhere).
I write out my classes like this. Yes, it looks a lot like an OOP language which you didn't want, but it still adheres to the prototypical model while holding similarities to other familiar languages which makes projects easier to navigate.
This is my style I prefer :) I'm not saying it's the best, but it's so easy to read.
(function(ns) {
var Class = ns.ClassName = function() {
};
Class.prototype = new baseClass();
Class.constructor = Class;
var _public = Class.prototype;
var _private = _public._ = {};
Class.aClassProperty = "aValue";
Class.aClassMethod = function(params) {
}
_public.aMethod = function(params) {
_private.myMethod.call(this, "aParam");
Class.aClassMethod("aParam");
}
_private.myMethod = function(params) {
}
})({});
EDIT:
I went ahead and converted your example this style just to show you what it would look like:
var namespace = {};
(function(ns) {
var Class = ns.Parent = function() {
};
var _public = Class.prototype;
var _private = _public._ = {};
_public.init = function() {
this.name = "anon";
}
_public.initWithParameters = function(parameters) {
this.name = parameters.name ? parameters.name : "anon";
}
_public.talk = function() {
console.log('Parent is: ' + this.name);
}
})(namespace);
(function(ns) {
var Class = ns.Child = function() {
this.position = {x:0, y:0};
};
Class.prototype = new ns.Parent();
Class.constructor = Class;
var _public = Class.prototype;
var _private = _public._ = {};
_public.init = function() {
_public.init.call(this);
this.setPosition(0, 0);
}
_public.initWithParameters = function(parameters) {
_public.initWithParameters.call(this, parameters);
this.setPosition(parameters.pos.x, parameters.pos.y);
}
_public.setPosition = function(x, y) {
this.position.x = x;
this.position.y = y;
}
_public.talk = function() {
console.log('Child is: ' + this.name + ' and location is: ' + this.position.x + ', ' + this.position.y);
}
})(namespace);

Coming from google in 2019.
From the latest MDN documentation, the way to extend prototype is :
function MyClass() {
SuperClass.call(this);
}
// inherit one class
MyClass.prototype = Object.create(SuperClass.prototype);
// mixin another
Object.assign(MyClass.prototype, {
//... your own prototype ...
});
// re-assign constructor
MyClass.prototype.constructor = MyClass;

These are the ways I usually do it:
Using a helper function:
/**
* A clone of the Node.js util.inherits() function. This will require
* browser support for the ES5 Object.create() method.
*
* #param {Function} ctor
* The child constructor.
* #param {Function} superCtor
* The parent constructor.
*/
function inherits (ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false
}
});
};
Then you might simply do:
function ChildClass() {
inherits(this, ParentClass);
// If you want to call parent's constructor:
this.super_.apply(this, arguments);
}
Extending prototype using Lodash
_.assign(ChildClass.prototype, {
value: key
});
Or just give ES6 a chance!
class ParentClass {
constructor() {
var date = new Date();
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
this.initializeTime = hours + ':' + minutes + ':' + seconds;
}
}
class ChildClass extends ParentsClass {
constructor() {
super();
console.log(this.initializeTime);
}
}

My example shows several things : Private variable, same parsing argument for the parant and child constructor, rewrite .toString() function try : ""+this. :)
full example with doc :
Output :
Constructor arguments
MM = new Parent("val of arg1", "val of arg2");
Child1 = new childInterface("1", "2");
Child2 = new child2Interface("a", "b");
console.log(MM + "args:", MM.arg1, MM.arg2);
// Parentargs: val of arg1 val of arg2
console.log(Child1 + "args:", Child1.arg1, Child1.arg2);
// childInterfaceargs: 1 2
console.log(Child2 + "args:", Child2.arg1, Child2.arg2);
// child2Interfaceargs: a b
Extend function in child class
MM.init();
// Parent: default ouput
Child1.init();
// childInterface: new output
Child2.init();
// child2Interface: default ouput
Increment variable
MM.increment();
// Parent: increment 1
Child1.increment();
// childInterface: increment 1
Child2.increment();
// child2Interface: increment 1
Child2.increment();
// child2Interface: increment 2
MM.increment();
// Parent: increment 2
console.log("p", "c1", "c2");
// p c1 c2
console.log(MM.value, " " + Child1.value, " " + Child2.value);
// 2 1 2
Private variable
MM.getHidden();
// Parent: hidden (private var) is true
MM.setHidden(false);
// Parent: hidden (private var) set to false
Child2.getHidden();
// child2Interface: hidden (private var) is true
MM.setHidden(true);
// Parent: hidden (private var) set to true
Child2.setHidden(false);
// child2Interface: hidden (private var) set to false
MM.getHidden();
// Parent: hidden (private var) is true
Child1.getHidden();
// childInterface: hidden (private var) is true
Child2.getHidden();
// child2Interface: hidden (private var) is false
Protected variable
function Parent() {
//...
Object.defineProperty(this, "_id", { value: 312 });
};
console.log(MM._id); // 312
MM._id = "lol";
console.log(MM._id); // 312
/**
* Class interface for Parent
*
* #class
*/
function Parent() {
this.parseArguments(...arguments);
/**
* hidden variable
*
* #type {Boolean}
* #private
*/
var hidden = true;
/**
* Get hidden
*/
this.getHidden = () => {
console.log(this + ": hidden (private var) is", hidden);
}
/**
* Set hidden
*
* #param {Boolean} state New value of hidden
*/
this.setHidden = (state) => {
console.log(this + ": hidden (private var) set to", !!state);
hidden = state;
}
Object.defineProperty(this, "_id", { value: "312" });
}
Object.defineProperty(Parent.prototype, "nameString", { value: "Parent" });
/**
* Parse arguments
*/
Parent.prototype.parseArguments = function(arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
};
/**
* Get className with `class.toString()`
*/
Parent.prototype.toString = function() {
return this.nameString;
};
/**
* Initialize middleware
*/
Parent.prototype.init = function() {
console.log(this + ": default ouput");
};
/**
* Increment value
*/
Parent.prototype.increment = function() {
this.value = (this.value) ? this.value + 1 : 1;
console.log(this + ": increment", this.value);
};
/**
* Class interface for Child
*
* #class
*/
function childInterface() {
this.parseArguments(...arguments);
}
// extend
childInterface.prototype = new Parent();
Object.defineProperty(childInterface.prototype, "nameString", { value: "childInterface" });
/**
* Initialize middleware (rewrite default)
*/
childInterface.prototype.init = function(chatClient) {
console.log(this + ": new output");
};
/**
* Class interface for Child2
*
* #class
*/
function child2Interface() {
this.parseArguments(...arguments);
}
// extend
child2Interface.prototype = new Parent();
Object.defineProperty(child2Interface.prototype, "nameString", { value: "child2Interface" });
//---------------------------------------------------------
//---------------------------------------------------------
MM = new Parent("val of arg1", "val of arg2");
Child1 = new childInterface("1", "2");
Child2 = new child2Interface("a", "b");
console.log(MM + " args:", MM.arg1, MM.arg2);
console.log(Child1 + " args:", Child1.arg1, Child1.arg2);
console.log(Child2 + " args:", Child2.arg1, Child2.arg2);
console.log(" ");
MM.init();
Child1.init();
Child2.init();
console.log(" ");
MM.increment();
Child1.increment();
Child2.increment();
Child2.increment();
MM.increment();
console.log("p", "c1", "c2");
console.log(MM.value, " " + Child1.value, " " + Child2.value);
console.log(" ");
MM.getHidden();
MM.setHidden(false);
Child2.getHidden();
MM.setHidden(true);
console.log(" ");
Child2.setHidden(false);
MM.getHidden();
Child1.getHidden();
Child2.getHidden();
console.log(MM._id);
MM._id = "lol";
console.log(MM._id);

I normally do it like this. I use the class operator now, but there's still a good way to do it in ES3.
Using Node.js utilities
Node.js includes a utility function for this very thing.
const { inherits } = require('util');
function SuperClass () {
this.fromSuperClass = 1;
}
function ExtendingClass () {
this.fromExtendingClass = 1;
}
inherits(ExtendingClass, SuperClass);
const extending = new ExtendingClass();
this.fromSuperClass; // -> 1
this.fromExtendingClass; // -> 1
The above has it's fair share of problems. It doesn't establish the prototypical chain, so it's considered semantically incompatible with the class operator.
Using the Object.create API
Otherwise, you can use Object.create for this.
function Person () {
this.person = true;
}
function CoolPerson () {
Person.call(this);
this.cool = true;
}
CoolPerson.prototype = Object.create(Person);
CoolPerson.prototype.constructor = CoolPerson;
Using a "helper function"
Please note that in the above example that uses the Object.create API, if you do not call Person (the "super class") in the CoolPerson (the extending class), instance properties (and optionally initialization) will not be applied when instantiating Person.
If you want to be more "elegant", you could even create a helper function for this, which might be easier for you.
function extend(BaseClassFactory, SuperClass) {
const BaseClass = BaseClassFactory(SuperClass.prototype, SuperClass);
BaseClass.prototype = Object.assign(BaseClass.prototype, Object.create(SuperClass));
BaseClass.prototype.constructor = BaseClass;
return BaseClass;
}
function SuperClass() {
this.superClass = true;
}
SuperClass.prototype.method = function() {
return 'one';
}
const ExtendingClass = extend((Super, SuperCtor) => {
function ExtendingClass () {
SuperCtor.call(this);
this.extending = true;
}
// Example of calling a super method:
ExtendingClass.prototype.method = function () {
return Super.method.call(this) + ' two'; // one two
}
return ExtendingClass;
}, SuperClass);
const extending = new ExtendingClass();
extending.method(); // => one two
Using ES6's class operator
There's a new class operator in JavaScript that's been released in JavaScript that may make this whole experience more expressive.
class SuperClass {
constructor() {
this.superClass = true;
}
method() {
return 'one';
}
}
class ExtendingClass extends SuperClass {
constructor() {
super();
this.extending = true;
}
method() {
// In this context, `super.method` refers to a bound version of `SuperClass.method`, which can be called like a normal method.
return `${super.method()} two`;
}
}
const extending = new ExtendingClass();
extending.method(); // => one two
Hope this helps.

Related

What's the difference between return vs. new keyword Constructor patterns?

Consider the following piece of code:
var Animal = function(name) {
return {
name: name,
speak: function() { return "My name is " + name; }
};
};
var cat = Animal("Kitty");
My question is: what is the difference, in terms of performance, conventions, or best practices, between the code above and the following code:
var Animal = function(name) {
this.name = name,
this.speak = function() { return "My name is " + name; }
};
var cat = new Animal("Another Kitty");
Which one is preferable? What are some use cases for the new keyword?
They are completely different when you consider prototypes (and everything they control, like inheritance). For example:
var SimpleAnimal = function(name) {
return {
name: name,
speak: function() { return "My name is " + name; }
};
};
var RealAnimal = function(name) {
this.name = name,
this.speak = function() { return "My name is " + name; }
};
var simpleCat = SimpleAnimal("Kitty");
var realCat = new RealAnimal("Another Kitty");
console.log(simpleCat instanceof SimpleAnimal, realCat instanceof RealAnimal); // simple is not an instance of the constructor
console.log(simpleCat.constructor, realCat.constructor); // because it uses the object literal constructor
This lack of prototype (and inheritance) means your object won't receive any methods defined on the class prototype:
var SimpleAnimal = function(name) {
return {
name: name,
speak: function() {
return "My name is " + name;
}
};
};
SimpleAnimal.prototype.roar = function() {
console.log("roar!");
};
var RealAnimal = function(name) {
this.name = name,
this.speak = function() {
return "My name is " + name;
}
};
RealAnimal.prototype.roar = function() {
console.log("roar!");
};
var simpleCat = SimpleAnimal("Kitty");
var realCat = new RealAnimal("Another Kitty");
try { simpleCat.roar(); } catch (e) { console.error(e); }
realCat.roar();
The first version returns a simple Object, the second one creates a new Animal.
The first version is good for small data objects. They can store different values, and that's pretty much it. They don't need a lot of memory.
The second one is for bigger objects. You can attach prototypes to them and use the "this" keyword to reach the object's properties. Because every object has the same properties, they take up considerably more space, than the first method.
Let's take the following and create a vector object. With the first method, you would do something like this:
function createVector(x, y) {
return {
x: x,
y: y
};
}
//Add to vectors together
function add(vec1, vec2) {
return {x: vec1.x + vec2.x, y: vec1.y + vec2.y};
}
add(createVector(1, 1), createVector(1, 2)); //return {x: 2, y: 3}
This can be pretty useful, but what if you want to have multiple types of vectors (3 dimensional, 4 dimensional, etc...)? You would need to give a separate name for the adder functions, wich is not very nice. This is where the second method comes in. It can separate functions into different namespaces:
function Vector2(x, y) {
this.x = x;
this.y = y;
}
//Add vectors together:
Vector2.prototype.add = function(vec) {
this.x += vec.x;
this.y += vec.y;
};
new Vector2(1, 1).add(new Vector2(1, 2)); //{x: x, y: y}
This way, you can create multiple vector types and each of them could have a separate add function without interfering with each other.
You should use both of them based on what you want to achieve.

Inheriting a class that has the same constructor but different/similar prototypes

Suppose I have a constructor:
function Constructor(input) {
this.input = input
}
Constructor.prototype.method = function() {
console.log('a')
}
But I want to make another class using a copy of the constructor, but changing the prototypes.
function Constructor2(input) {
this.input = input
}
Constructor2.prototype.method = function() {
console.log('b')
}
I don't want to redefine the constructor. How would you do this? Ideally it would be something as simple as:
var Constructor2 = inherits(Constructor)
Constructor2.prototype.method = // overwrite the inherited `method()`
var inherits = function(childCtor, parentCtor) {
/** #constructor */
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.superClass_ = parentCtor.prototype;
childCtor.prototype = new tempCtor();
/** #override */
childCtor.prototype.constructor = childCtor;
};
// How to use it:
var Constructor1 = function() {
//add all your methods, variables etc
};
Constructor1.prototype.myMethod = function() {
};
var Contructor2 = function() {
Contructor1.call(this); // Call the super class constructor
};
inherits(Contstructor2, Constructor1);
// Constructor2 now inherits from Constructor1
// override, add methods variables etc, whatever you need.
// Have fun!
Okay, much easier just to use apply:
function newConstructor(Super) {
function Construct() {
Super.apply(this, arguments)
}
require('util').inherits(Construct, Super)
return Construct
}
Here's a nasty-ish solution:
function Constructor1(input) {
this.input = input;
}
Constructor1.prototype.method = function() {
console.log('a');
}
// be careful here: evals the string value of Constructor1 with references to "Constructor1" changed to "Constructor2"
eval(Constructor1.toString().replace("Constructor1", "Constructor2"));
Constructor2.prototype.method = function() {
console.log('b');
}
var c1 = new Constructor1(1);
var c2 = new Constructor2(2);
console.log(c1.constructor === c2.constructor) // true
c1.method() // a
c2.method() // b

javascript inheritance framework

Is there a small, lightweight solution for javascript class inheritance that will work well on both client and server side (node.js)? I'm not wanting a big library, just something that will allow me to declare a constructor and some methods, then have the ability for a class to inherit that.
John Resig outlines a simple inheritance framework in about 25 lines of code here. I have seen it used to good effect.
You can use it like this:
var Vehicle = Class.extend({
init: function(wheels) {
this.wheels = wheels;
}
});
var Truck = Vehicle.extend({
init: function(hp, wheels) {
this.horsepower = hp;
this._super(wheels);
},
printInfo: function() {
console.log('I am a truck and I have ' + this.horsepower + ' hp.');
}
});
var t = new Truck(4, 350);
t.printInfo();
take a look at https://github.com/ded/klass
I created this small library to use an ExtJs Style ClassManager. It's quite simple yet, but very flexible.
Install via node.js
npm install esf-core
Sample
Esf.define('A', {
a: null,
constructor: function (a) {
// Save var
this.a = a;
// Heyho
console.log('A');
},
foo: function (b) {
console.log('foo - ' + b);
}
});
Esf.define('B', {
b: null,
constructor: function (a, b) {
// Call super constructor
this.callParent(a);
// Save var
this.b = b;
// Heyho
console.log('B');
},
foo: function () {
this.callParent('bar');
}
}, {
extend: 'A'
});
// Use
var b = new B(1, 2);
// or
var b = Esf.create('B', 1, 2);
/*
* Output:
* A
* B
* foo - bar
*/
b.foo();
Repository
https://bitbucket.org/tehrengruber/esf-js-core
I've seen the prototype library used successfully.
I think this is much better than the init hax in the simple inheritance fw:
(function() {
var core = {
require : function(source) {
if ( typeof (source) != "object" || !source)
throw new TypeError("Object needed as source.");
for (var property in source)
if (source.hasOwnProperty(property) && !this.prototype.hasOwnProperty(property))
this.prototype[property] = source[property];
},
override : function(source) {
if ( typeof (source) != "object" || !source)
throw new TypeError("Object needed as source.");
for (var property in source)
if (source.hasOwnProperty(property))
this.prototype[property] = source[property];
},
extend : function(source) {
var superClass = this;
var newClass = source.hasOwnProperty("constructor") ? source.constructor : function() {
superClass.apply(this, arguments);
};
newClass.superClass = superClass;
var superClone = function() {
};
superClone.prototype = superClass.prototype;
newClass.prototype = new superClone();
newClass.prototype.constructor = newClass;
if (source)
newClass.override(source);
return newClass;
}
};
core.require.call(Function, core);
Function.create = function (source){
var newClass = source.hasOwnProperty("constructor") ? source.constructor : function() {};
newClass.override(source);
return newClass;
};
})();
The vehicle example with this:
var Vehicle = Function.create({
constructor : function(wheels) {
this.wheels = wheels;
}
});
var Truck = Vehicle.extend({
constructor : function(hp, wheels) {
this.horsepower = hp;
Vehicle.call(this, wheels);
},
printInfo : function() {
console.log('I am a truck and I have ' + this.horsepower + ' hp.');
}
});
var t = new Truck(4, 350);
t.printInfo();
I created a very lightweight library that works in-browser and in node.js. Its a super easy-to-use, bloatless library:
https://github.com/fresheneesz/proto
Example:
var Person = proto(function() { // prototype builder
this.init = function(legs, arms) { // constructor
this.legs = legs
this.arms = arms
}
this.getCaughtInBearTrap = function() { // instance method
this.legs -= 1
}
this.limbs = function() {
return this.arms + this.legs
}
})
var Girl = proto(Person, function() { // inheritance
this.haveBaby = function() {
return Person(2,2)
}
})
var g = Girl(2,2) // instantiation
g.getCaughtInBearTrap()
console.log("Girl has "+g.limbs()+" limbs")
console.log(": (")

How to do inheritance with JavaScript object literals?

Hello I have a problem with how to do inheritance while declaring object prototypes with object literal syntax.
I have made two Fiddles to help you help me.
Fiddle1, This one works
Fiddle2, This one doesn't work
This is my base class, almost all objects is defined in this way in my application:
Base = function(param){
this.init(param);
}
Base.prototype = {
init: function(param){
this.param = param;
},
calc: function(){
var result = this.param * 10;
document.write("Result from calc in Base: " + result + "<br/>");
},
calcB: function(){
var result = this.param * 20;
document.write("Result from calcB in Base: " + result+ "<br/>");
}
}
This is how I succeed extending and overriding methods in Base:
Extend = function(param){
this.init(param);
}
Extend.prototype = new Base();
Extend.prototype.calc = function(){
var result = this.param * 50;
document.write("Result from calc in Extend: " + result+ "<br/>");
}
But I wanted to use the same style as the rest of the application so I started playing around with object literals but it is driving me nuts eagerly cheered on by eclipse and firebug with its nonsense response to my syntax.
Now on to the question of how do I convert my succeeded extension code to object literal style?
Here is one of many attempts (it don't compile but will give you an rough idea how I want the code to look like.)
Extend = function(param){
this.init(param);
}
Extend.prototype = {
: new Base(),
calc: function(){
var result = this.param * 50;
document.write("Result from calc in Extend: " + result+ "<br/>");
}
}
You want Object.make. Live Example
Extend = function(param){
this.init(param);
}
Extend.prototype = Object.make(Base.prototype, {
constructor: Extend,
calc: function(){
var result = this.param * 50;
document.write("Result from calc in Extend: " + result+ "<br/>");
}
});
If you want an ES5 compliant implementation of Object.make to just plug into your code then use
Object.make = function make (proto) {
var o = Object.create(proto);
var args = [].slice.call(arguments, 1);
args.forEach(function (obj) {
Object.getOwnPropertyNames(obj).forEach(function (key) {
o[key] = obj[key];
});
});
return o;
}
You need to extend the original prototype rather than assigning a new object as the prototype.
function extend(original, extension) {
for (var key in extension) {
if (extension.hasOwnProperty(key)) {
original[key] = extension[key];
}
}
};
var A = function () {};
var B = function () {};
B.prototype = new A();
extend(B.prototype, {
methodName: function () {}
});

Javascript prototype and issues accessing class

Family = function(name) {
this._Name = name;
}
Family.prototype = {
getName: function() {
return this._Name;
},
People: function(num) {
this._Number = num;
}
}
Family.People.prototype = {
clearNumber: function() {
this._Number = 0;
}
}
People is a nested class. Its parent class is Family.
I get the error that Family.People is undefined. Could someone correct the code above?
Working code
// function doesn't need "new" operator
var Family = function(name) { this._Name = name; };
Family.prototype = {
getName: function() { return this._Name; }, // missing comma
People: function(num) {
this._Number = num;
}
};
// work with prototypes
Family.prototype.People.prototype = {
clearNumber: function() { this._Number = 0; }
};
This will work. But you have to be aware, that when you call:
var f = new Family("Doe");
f.People is just an object constructor, and not an instance of some other object. You will have to instantiate it as well like:
f.members = new f.People(3);
Sou you have a constructor within your instance which is rather confusing.
A better approach
So it would probably be better if you'd write your prototypes this way:
var Family = function(name) {
this._Name = name;
this.getName = function() { return this._Name; };
};
Family.People = function(num) {
this._Number = num;
this.clearNumber = function() { this._Number = 0; };
};
This actually makes a class within a class (and not within instances). So upper lines would be called this way:
var f = new Family("Doe");
f.members = new Family.People(3);
Drill down of f instance would look like:
f
_Name
getName()
members
_Number
clearNumber()
Private variables
var Family = function(name) {
var _name = name;
this.getName = function() { return _name; };
};
Family.People = function(num) {
var _num = num;
this.getNumber = function() { return _num; }
this.clearNumber = function() { _num = 0; };
};
This way we make variables private and only accessible within so they can't be manipulated outside. You must always use functions to manipulate them. This makes it more robust especially when there are certain business rules related to variable values.
var f = new Family("Doe");
f._name; // this is undefined because "_name" is private closure variable
Drill down of f instance would now look more like a class object instance:
f
getName()
members
getNumber()
clearNumber()
Notice that you are assigning to Family.prototype.People then trying to access Family.People.
Family is not an instance of Family thus it does not have the properties of that class - Family is an instance of Function thus you are trying to access Function.prototype.People in that 3rd statement. (this is a bit of a simplification)
i.e. what you want to be doing is
Family.prototype.People.prototype = {
clearNumber:function(){this._Number = 0;}
}
You are also missing a comma before people, but I assume this is a typo...
You should declare the People constructor as a key in the Family object:
Family.People = function(num) {
this._Number = num;
}
The Family prototype will be in the prototype chain for new objects of type Family; not a part of Family itself.

Categories

Resources