Where/when to set function prototype? - javascript

I'm new to javascript prototypes.
In examples, prototypes are assigned in-line with the main program definition, but doing so has start-up sequence ramifications.
The following shows how I currently apply a prototype to a group of singletons. It would be nice to instead assign the prototype within the descendant class, or somewhere more visibly 'bound' to it, for clarity. (Note the panels are instantiated within the controller to enforce separation.)
Is there another location/method to accomplish this I'm overlooking? Also, am I violating any well-known styles with the current approach?
var controller = new Controller();
function Controller() {
var panels = {
search: SearchPanel,
results: ResultsPanel,
details: DetailsPanel,
action: ActionPanel,
};
$.each(panels, function (i, v) {
// THE QUESTION REFERS TO THIS FOLLOWING STATEMENT:
v.prototype = new PanelCommon();
panels[i] = new v();
});
this.publish = function (site, message) {
$.each(panels, function (i, v) {
if (v[site]) v[site](message);
});
}
/*...*/
}
function PanelCommon() { /*...*/ }
function SearchPanel() { /*...*/ }
function ResultsPanel() { /*...*/ }
function DetailsPanel() { /*...*/ }
function ActionPanel() { /*...*/ }

Another fit for the dynamic nature of JavaScript is the concept of Mixins or Augmentation, which are sometimes more natural than prototypical inheritance.
What do I mean by a mixin?
A "mixin" that takes an object, and injects more functionality. Basically, the idea is that we are going to take an object, and start adding behavior to it.
Consider the following mixinPanelTo() function. It'll be a function that takes a constructor and adds a common render() function to it's prototype.
var mixinPanelTo = (function() {
var render = function() {
// a render function that all panels share
console.log("rendering!")
}
// Augment
return function(cls) {
cls.prototype.render = render;
}
})();
Now that we have this, we can mix that functionality into any constructor we want:
var SearchPanel = function() {}
SearchPanel.prototype.search = function(query) {
/* search stuff */
this.render();
}
mixinPanelTo(SearchPanel)
Then, we should be able to
var panel = new SearchPanel()
panel.search("foo"); // "rendering!" on the console
Multiple mixins
One advantage of mixins over inheritance is a more granular control over applied functionality, and also the ability to borrow functionality from multiple parents
var mixinRender = function(cls) { /* inject render */ }
var mixinSearch = function(cls) { /* inject search */ }
var mixinInfiniteScroll = function(cls) { /* inject infinite scroll */ }
var customPanel = function() {}
mixinRender(customPanel);
mixinSearch(customPanel);
mixinInfiniteScroll(customPanel)
This would be difficult to accomplish with prototypical inheritance. Other than trying to make a bizarre class hierarchy.
Borrowing functionality
You can also have your mixin's require functionality/configuration from your target class. For instance, lets take mixinInfinitScroll
var mixinInfiniteScroll = function(cls, fetch) {
var page = 0;
cls.prototype.more = function() {
var data
// get more results
if(typeof fetch == "function")
data = fetch.call(this, ++page)
else
// assume a key in this
data = this[fetch](++page)
/* do work with data */
}
}
And then when mixing in this functionality, we can inject specific functionality:
// by key
var Panel1 = function() { }
Panel1.prototype.fetch = function() { /* go get more results */ }
mixinInifiniteScroll(Panel1, "fetch")
// or even with a direct reference
var Panel1 = function() { }
Panel1.prototype.fetch = function() { /* go get more results */ }
mixinInifiniteScroll(Panel1, Panel1.prototype.fetch)
// or even an anonymous function
var Panel1 = function() { }
mixinInifiniteScroll(Panel1, function() { /* go get more results */ })
Overriding methods
You can also override prototype methods in mixins, which makes them quite powerful
var augmentRender = function(cls, renderFn) {
var oldRender = cls.prototype[renderFn];
cls.prototype[renderFn] = function() {
/* prep */
oldRender.apply(this, arguments);
/* make some more changes */
}
}
And then we can say:
var Panel = function() { }
Panel.prototype.render = function() { /* my render */ }
augmentRender(Panel, "render")
Anyway, not that there is anything wrong with prototypical inheritance, but this might give you some more ideas of different ways to solve your problem by approaching it in a different way.

Usually prototypes are assigned immediately following the declaration of the constructor function. Also, don't forget to modify the constructor property of the newly instantiated prototype.
Sean also makes an interesting point about using Object.create, but whether or not you want to do that really depends on the contents of the PanelCommon constructor function. You also may have to shim Object.create in older browsers.
function PanelCommon() {}
function SearchPanel() {}
SearchPanel.prototype = new PanelCommon();
SearchPanel.prototype.constructor = SearchPanel;
function ResultsPanel() {}
ResultsPanel.prototype = new PanelCommon();
ResultsPanel.prototype.constructor = ResultsPanel;
function DetailsPanel() {}
DetailsPanel.prototype = new PanelCommon();
DetailsPanel.prototype.constructor = DetailsPanel;
function ActionPanel() {}
ActionPanel.prototype = new PanelCommon();
ActionPanel.prototype.constructor = ActionPanel;

You can use Object.create - that will avoid the new SuperClass weirdness of the ES3 solution:
> SearchPanel.prototype = Object.create(PanelCommon.prototype)
> SearchPanel.prototype.constructor = SearchPanel
> new SearchPanel instanceof PanelCommon
true
This can be extracted into a very simple extends function:
function extends(cls, superClass) {
cls.prototype = Object.create(superClass.prototype);
cls.prototype.constructor = cls;
return cls;
}
Which can then be used like this:
var SpecialPanel = extends(function SpecialPanel() {}, PanelCommon);

Related

Private prototype methods that can share scope and access the instance

I'm looking for a pattern that both allows me to create a private scope that my function prototype has access to and I need to be able to access the instance from within that scope.
For example, this is how I am currently achieving "private methods" (disregard what the code actually does, just look at the structure.)
function InfoPreview() {
this.element = document.createElement('div');
}
//Private Methods
InfoPreview.prototype.__newLine = function () {
this.element.appendChild(createElement({tagName:'br'}));
};
InfoPreview.prototype.__padLeft = function(level) {
var padding = createElement({tagName: 'span'});
this.element.appendChild(padding);
$(padding).width(level * 10);
};
InfoPreview.prototype.__print = function(string) {
var span = createElement({ tagName: 'span', textContent: string });
this.element.appendChild(span);
this.element.style["margin-right"]='10px';
};
InfoPreview.prototype.__puts = function(string) {
this.__print(string);
this.__newLine();
};
//Public Methods
InfoPreview.prototype.update = function(info) {
$(this.element).empty();
for (var record in info) {
this.__puts(record);
}
};
Notice that I am not creating private methods at all, just utilizing a naming convention. Additionally notice that I have no way to cache chain-lookups, such as this.element.
I would like to create a private scope by utilizing a revealing module pattern, like this:
InfoPreview.prototype = (function() {
var self = this, //<- `this` is actually the global object now.
el = self.element;
var newLine = function () {
el.appendChild(createElement({tagName:'br'}));
};
var padLeft = function(level) {
var padding = createElement({tagName: 'span'});
el.appendChild(padding);
$(padding).width(level * 10);
};
var print = function(string) {
var span = createElement({ tagName: 'span', textContent: string });
el.appendChild(span);
el.style["margin-right"]='10px';
};
var puts = function(string) {
print(string);
newLine();
};
var update = function(info) {
$(el).empty();
for (var record in info) {
puts(record);
}
};
return {
update: update
};
})();
The above approach doesn't work however, because the value of this within the IIFE is the global object, not the instance. I need a way to access the instance.
Is there any downside of using a constructor pattern?
function Foo(constructorArg) {
/* private variables */
var privVar = 'I am private',
cArg = constructorArg;
/* public variables */
this.pubVar = 'I am public';
/* private function */
function privFunc() {
return 'I am a private function';
}
/* public function */
this.publicFunc = function() {
return 'I am a public function and I call privVar->"' + privVar + '" and privFunc->"' + privFunc() + '"';
}
}
var foo = new Foo('something');
console.log('foo.pubVar', foo.pubVar); //ok
console.log('foo.publicFunc()', foo.publicFunc()); // ok
console.log('foo.privVar', foo.privVar); // undefined
console.log('foo.privFunc', foo.privFunc()); //error
Why you should use it (as requested in comments):
Simply put, because it is the only (sane) way of creating a "true private scope", which was your question.
The alternative is using a convention which tell developers what properties and methods are private, usually by prefixing them with an underscore _, which you already implemented but disliked.
Note that constructor and prototype are different things and enable you to do different stuff. Nothing prevents you from mixing both up.
Memory usage
Regarding memory usage, in modern js engines, such as Google's V8 JavaScript Engine, the constructor pattern might actually be faster.
V8 has hidden types created internally for objects at runtime; objects with the same hidden class can then use the same optimized generated code.
For example:
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(11, 22);
var p2 = new Point(33, 44);
// At this point, p1 and p2 have a shared hidden class
p2.z = 55;
// warning! p1 and p2 now have different hidden classes!
Prototype chaining always require two lookups, so it might even be a tiny inny LITTLE bit slower. Note: Can't back up on this, jsperf.com is down!
Constructor pattern is dirty (sic)
Performance was my reason. I hadn't realized that. However it still feels dirty to me
I don't know why you feel the constructor pattern is dirty. Maybe it's because it has some "specifics", limitations and potential pitfalls you should be aware
this can mean different things
It's easy to forget the new keyword causing weird and hard to debug bugs due to shared state
You can't easily split your object across multiple files (without resorting to a build tool or some 3rd party injector)
However, 1 and 2 are also true for prototype declaration style so...
if you feel this is not adequate, you might want to look at the module pattern.
Within each function, you will have access to the this value you want.
var Example = function() {};
Example.prototype = (function() {
var privateUpdate = function() {
document.getElementById('answer').innerHTML = this.foo;
}
return {
update: privateUpdate
}
})();
var e = new Example();
e.foo = 'bar';
e.update();
<div id="answer"></div>
As a variant on what Pointy is suggesting, you can try this pattern;
infoPreview.prototype = (function() {
var self = null;
var update = function(info) {
....
};
var firstUpdate = function(info) {
self = this;
functions.update = update;
update(info);
}
var functions = {
update: firstUpdate
};
return functions;
})();
Maybe something like that, without prototyping :
https://jsfiddle.net/ynwun1xb
var Fn = function(el) {
this.el = el;
var myMethod = function() {
console.log('do something in method with element', this.el);
}.bind(this);
return {
myPublicMethod: function() {
return myMethod();
}
}
}
var instancedFn = new Fn('first instance element')
.myPublicMethod()
;
var instancedFn2 = new Fn('second instance element')
.myPublicMethod()
;

calling parent implementations of privileged functions

I have an inheritance relationship between two JavaScript classes1
RealTimeChart = function(chartAttributes) {
var chart = new FusionCharts(chartAttributes);
this.appendData = function(data) {
chart.feedData(data);
}
};
RealTimeGauge = function(chartAttributes) {
chartAttributes.type = 'AngularGauge';
// call parent constructor
RealTimeChart.call(this, chartAttributes);
};
// inherit from RealTimeChart
RealTimeGauge.prototype = Object.create(RealTimeChart.prototype);
In RealTimeGauge I would like to override appendData(). The implementation of this function in RealTimeGauge needs to call the parent implementation, is this possible?
It's relatively straightforward to do this if I change appendData to a prototype function, e.g.
// parent class
RealTimeChart.prototype.appendData = function(data) {
this.chart.feedData(data);
};
// child class
RealTimeGauge.prototype.appendData = function(data) {
console.log("doing custom stuff...");
// call the parent function to add the data to the chart
RealTimeChart.prototype.appendData.call(this, data);
};
However, if I make appendData a prototype function rather than a privileged function, I also have to make chart a public property, which I'd rather not do. Is it possible to call the parent implementation of appendData form RealTimeGauge if it's a privileged function?
I know they're not really classes, but I don't know of a better name for them
In the child constructor, after the calling the super constructor, this holds the priviledged function as an own property.
You can reassign this to a temporary variable and create the wrapping function that will replace super's implementation:
Parent = function() {
var x = 1;
this.work = function(y) {
console.log(x + y);
}
};
Child = function() {
Parent.call(this);
var super_work = this.work
this.work = function(y) {
super_work(y + 10);
}
};
Child.prototype = Object.create(Parent.prototype);
Let's give it a try:
p = new Parent()
p.work(1) # prints 2
c = new Child()
c.work(1) # prints 12
To avoid this issue altogether, you could drop inheritance, and go for composition.
RealTimeChart = function(chartAttributes) {
var chart = new FusionCharts(chartAttributes);
this.appendData = function(data) {
chart.feedData(data);
};
};
RealTimeGauge = function (realTimeChart) {
this.appendData = function (data) {
console.log("doing custom stuff...");
realTimeChart.appendData(data);
};
};
new RealTimeGauge(new RealTimeChart({}));
Using composition results in simpler, more modular code. As you can see in the above code, RealTimeGauge only depends on the interface of RealTimeChart, not RealTimeChart itself, like in your original code. This means that you can substitute anything that has the same interface. The 2 'classes' are now decoupled. At what price? Less code that's more readable. You could go even further and decouple RealTimeChart from FusionCharts in the same way.

How to eliminate code repetition between different View Model modules?

My JS is organized into viewmodels and services. The services are mostly concerned with AJAX calls, whereas my viewModels describe the view that they're used in.
I have two view models now - StoreViewModel, and MyStoreViewModel. In each of these, I have the following:
function MyStoreVm(model) {
var self = this;
self.doThis = function(){
// do stuff
self.doThat();
};
}
Then:
function StoreVm(model) {
var self = this;
self.doThis = function(){
// do stuff
self.doThat();
};
}
I come from a C# background - normally I would just use inheritance in this kind of situation. How can I eliminate this code repetition between two distinct modules / viewmodels, by having them inherit from a third, shared module?
More details: These are being used in an MVC view where I have knockout bindings depending on whether or not the store is MyStore:
#if (!Model.IsMyStore) {
<script type="text/javascript">
$(document).ready(ko.applyBindings(new StoreVm(#Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() })))));
</script>
} else if (Model.IsMyStore) {
<script type="text/javascript">
$(document).ready(ko.applyBindings(new MyStoreVm(#Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() }).Sanitize()))));
</script>
}
UPDATE
I looked into some of the suggestions below, but none seemed clean and simple enough for my novice skills. I tried the following which seems to work:
function BaseStore(model){
self.doThis = function(){
// do stuff
self.doThat();
};
// and a whole lot of other shared code
}
function StoreVm(model) {
var storeVm = new BaseStoreVm(model)
var self = storeVm;
self.isolatedFunctionForGenericStores = function(){stuff}
// other stuff for only this type
return storeVm;
}
function MyStoreVm(model) {
var myStoreVm = new BaseStoreVm(model)
var self = myStoreVm;
self.isolatedFunctionForMyStore = function(){stuff}
// other stuff for only this type
return myStoreVm;
}
Is there anything wrong with this approach?
If you have two child types that need to inherit from the same parent, you can do:
function Parent( foo ) {
this.foo = foo;
}
Parent.prototype.method = function() {
console.log( this.foo );
};
function Child1() {
Parent.call( this, "bar" );
}
Child1.prototype = Object.create( Parent.prototype );
Child1.prototype.constructor = Child1;
function Child2() {
Parent.call( this, "qux" );
}
Child2.prototype = Object.create( Parent.prototype );
Child2.prototype.constructor = Child2;
var parent = new Parent("blah");
var child1 = new Child1();
var child2 = new Child2();
parent.method(); // => "blah"
child1.method(); // => "bar"
child2.method(); // => "qux"
First you should understand how JavaScript implement inheritance. JavaScript is a prototype-based language which contains no class statement, such as is found in C#. Instead, it uses functions as classes(no classes, just objects).
So what we have here is objects inherit from other objects (now you might need to get some coffee).
So then JavaScript does not give you the full power of inheritance and polymorphism you get in C#.
If you want to know ways to implement inheritance in JS:
SO: Performing inheritance in JavaScript
My Blog: Javascript Inheritance techniques
Back to your question, i think you may need to implement The Factory Pattern. So your js code could be like that:
function MyStoreVm(model) {
var self = this;
self.doThis = function() {
// do stuff
self.doThat();
};
}
function StoreVm(model) {
var self = this;
self.doThis = function() {
// do stuff
self.doThat();
};
}
// Define factory object that create your proper store object
// StoreFactory takes the model as input.
// You can change it to accept seconf parameter that define class type
function StoreFactory() {
this.classType == "MyStoreVm"; // default value
this.createStore = function(model) {
if (model.IsMyStore === true)
this.classType = MyStoreVm;
else
this.classType = StoreVm;
return new this.classType(model);
}
}
Then in your MVC view:
$(document).ready(function() {
var mystoreFactory = new StoreFactory();
ko.applyBindings(mystoreFactory.createStore((#Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() {
ContractResolver = new CamelCasePropertyNamesContractResolver()
})))));
});
Check out Klass.js. While this is basically the same as creating your own prototypes and inheritance methods, it's nice to use. It's also AMD aware.
// base class
var Store = klass(function() {
var self = this;
// add properties here
}).methods({
doThis: function () {
// do this
},
doThat: function () {
// do that
}
});
return Store;
// create the first constructor
var myStoreVm = Store.extend(function () {
// super class is called
}).methods({
doThis: function(){
this.supr(); // call Store.doThis
// some other code
}
});
return myStoreVm;
// create the second constructor
var storeVm = Store.extend(function () {
// super class is called
}).methods({
doThis: function(){
// override Store.doThis with my own code
}
});
return storeVm;

function object inheritance using jquery's extend

I'm trying to simulate inheritance using jquery extend but as far as I could test, it works only with objects.
What I'm trying to accomplish is:
var baseDefinition = function() {
var self = this;
self.calc1 = function() {
return "something1";
}
self.calc2 = function() {
return "something2";
}
self.myObject = {
propA = 100;
propB = 200;
};
}
var derivedDefinition = function() {
var self = this;
self.calc2 = function() {
return "something different from base";
}
self.myObject = {
propB = 400;
};
}
var instance = $.extend(true, {}, baseDefinition, derivedDefinition);
So I would hope to create a new instance from base definition where the derived definitions would be applied to the base ones but neither definitions would be "touched". Is it possible?
I was hoping to avoid any prototype so basicaly I would like to call instance.calc1 or instance.calc2 without knowing wether it was overriten or not.
Edit:
In my example I didn't include any object properties which was what led me to use jquery's extend function. Although both answers solve inner functions "inheritance", it doesn't (obviously) merge object properties like extend does. As a possible solution I'm thinking after I create my instance to loop through the properties of the instance and apply jquery's extend on them. Although this seems inefficient to me, I don't know if you can advise me on another course of action.
JQuery extend does not create an inheritance hierarchy, so changes you make to base definition AFTER you extend would not be reflected in derived definition. Here's how you can extend the base definition in a way that does reflect later changes down the inheritance hierarchy using Javascript prototypal inheritance:
var baseDefinition = function() {};
baseDefinition.prototype.calc1 = function() {
return "something1";
};
baseDefinition.prototype.calc2 = function() {
return "something2";
};
var derivedDefinition = function() {};
derivedDefinition.prototype = Object.create(baseDefinition.prototype);
derivedDefinition.prototype.calc2 = function() {
return "something different from base";
};
var instance = new derivedDefinition();
instance.calc1(); // something1
instance.calc2(); // something different from base
$.extend only works on already existing objects, not on function which will instantiate objects in the (far?) future:
var instance = $.extend(true, {}, new baseDefinition(), new derivedDefinition());
However, you could of course design an extend function that works for constructors and that returns a function:
function extendConstr() {
var fns = arguments;
return function newConstr(){
var self = {};
for (var i=0; i<fns.length; i++)
fns[i].apply(self, arguments);
return self;
}
}
var extendedFunction = extendConstr(baseDefinition, derivedDefinition);
var instance = extendedFunction();
console.log(instance); // has `calc1` and overwritten `calc2`
Btw, without an extend function you could've done that already manually in the derived constructor:
function derivedDefinition() {
baseDefinition.call(this/*, arguments */);
this.calc2 = function() {
return "something different from base";
}
}
console.log(new derivedDefinition) // has a `calc1` as well

Crockford's Prototypical Inheritance - Usage

I've been building a small JS framework for use at my job, and I'd like to employ Douglas Crockford's prototypical inheritance patterns. I think I get the general idea of how the prototype object works, but what isn't clear is the way in which I would use this pattern beyond the simplest example.
I'll flesh it out to the point that I understand it.
(function () {
'use strict';
var Vehicles = {};
Vehicles.Vehicle = function () {
this.go = function () {
//go forwards
};
this.stop = function () {
//stop
};
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
}());
So now my Vehicles.Airplane object can go() and stop(), but I want more. I want to add takeOff() and land() methods to this object. I could just use ugly dot notation afterwards:
Vehicles.Airplane.takeOff = function () {
//take off stuff
}
But that seems wrong, especially if I were to add many methods or properties. The question asked at here seems to be very similar to mine, but the answer doesn't quite ring true for me. The answer suggests that I should build an object literal before using Object.create, and that I should pass that object literal into the create method. In the example code given, however, it looks like their new object inherits nothing at all now.
What I'm hoping for is some syntax similar to:
Vehicles.Airplane = Object.create(Vehicles.Vehicle({
this.takeOff = function () {
//takeOff stuff
};
this.land = function () {
//land stuff
};
}));
I know this syntax will break terribly with Object.create right now, because of course I'm passing Vehicle.Vehicle a function rather than an object literal. That's beside the point. I'm wondering in what way I should build new properties into an object that inherits from another without having to list them out one at a time with dot notation after the fact.
EDIT:
Bergi, after some anguished thought on the topic, I think I really want to go with what you described as the "Classical Pattern". Here is my first stab at it (now with actual code snippets rather than mocked up hypotheticals - You even get to see my crappy method stubs):
CS.Button = function (o) {
o = o || {};
function init(self) {
self.domNode = dce('a');
self.text = o.text || '';
self.displayType = 'inline-block';
self.disabled = o.disabled || false;
self.domNode.appendChild(ctn(self.text));
if (o.handler) {
self.addListener('click', function () {
o.handler(self);
});
}
}
this.setText = function (newText) {
if (this.domNode.firstChild) {
this.domNode.removeChild(this.domNode.firstChild);
}
this.domNode.appendChild(ctn(newText));
};
init(this);
};
CS.Button.prototype = Object.create(CS.Displayable.prototype, {
constructor: {value: CS.Button, configurable: true}
});
CS.Displayable = function (o) { // o = CS Object
o = o || {};
var f = Object.create(new CS.Element(o));
function init(self) {
if (!self.domAnchor) {
self.domAnchor = self.domNode;
}
if (self.renderTo) {
self.renderTo.appendChild(self.domAnchor);
}
}
//Public Methods
this.addClass = function (newClass) {
if (typeof newClass === 'string') {
this.domNode.className += ' ' + newClass;
}
};
this.addListener = function (event, func, capture) {
if (this.domNode.addEventListener) {
this.domNode.addEventListener(event, func, capture);
} else if (this.domNode.attachEvent) {
this.domNode.attachEvent('on' + event, func);
}
};
this.blur = function () {
this.domNode.blur();
};
this.disable = function () {
this.disabled = true;
};
this.enable = function () {
this.disabled = false;
};
this.focus = function () {
this.domNode.focus();
};
this.getHeight = function () {
return this.domNode.offsetHeight;
};
this.getWidth = function () {
return this.domNode.offsetWidth;
};
this.hide = function () {
this.domNode.style.display = 'none';
};
this.isDisabled = function () {
return this.disabled;
};
this.removeClass = function (classToRemove) {
var classArray = this.domNode.className.split(' ');
classArray.splice(classArray.indexOf(classToRemove), 1);
this.domNode.className = classArray.join(' ');
};
this.removeListener = function () {
//Remove DOM element listener
};
this.show = function () {
this.domNode.style.display = this.displayType;
};
init(this);
};
CS.Displayable.prototype = Object.create(CS.Element.prototype, {
constructor: {value: CS.Displayable, configurable: true}
});
I should be quite clear and say that it's not quite working yet, but mostly I'd like your opinion on whether I'm even on the right track. You mentioned "instance-specific properties and methods" in a comment in your example. Does that mean that my this.setText method and others are wrongly placed, and won't be available to descendant items on the prototype chain?
Also, when used, it seems that the order of declaration now matters (I can't access CS.Displayable.prototype, because (I think) CS.Button is listed first, and CS.Displayable is undefined at the time that I'm trying to reference it). Is that something I'll just have to man up and deal with (put things in order of ancestry in the code rather than my OCD alphabetical order) or is there something I'm overlooking there as well?
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
That line is wrong. You seem to want to use new Vehicles.Vehicle - never call a constructor without new!
Still, I'm not sure which pattern you want to use. Two are coming to my mind:
Classical Pattern
You are using constructor functions just as in standard JS. Inheritance is done by inheriting the prototype objects from each other, and applying the parent constructor on child instances. Your code should then look like this:
Vehicles.Vehicle = function () {
// instance-specific properties and methods,
// initialising
}
Vehicles.Vehicle.prototype.go = function () {
//go forwards
};
Vehicles.Vehicle.prototype.stop = function () {
//stop
};
Vehicles.Airplane = function() {
// Vehicles.Vehicle.apply(this, arguments);
// not needed here as "Vehicle" is empty
// maybe airplane-spefic instance initialisation
}
Vehicles.Airplane.prototype = Object.create(Vehicles.Vehicle.prototype, {
constructor: {value:Vehicles.Airplane, configurable:true}
}); // inheriting from Vehicle prototype, and overwriting constructor property
Vehicles.Airplane.prototype.takeOff = function () {
//take off stuff
};
// usage:
var airplane = new Vehicles.Airplace(params);
Pure Prototypical Pattern
You are using plain objects instead of constructor functions - no initialisation. To create instances, and to set up inheritance, only Object.create is used. It is like having only the prototype objects, and empty constructors. instancof does not work here. The code would look like this:
Vehicles.Vehicle = {
go: function () {
//go forwards
},
stop: function () {
//stop
}
}; // just an object literal
Vehicles.Airplane = Object.create(Vehicles.Vehicle); // a new object inheriting the go & stop methods
Vehicles.Airplane.takeOff = function () {
//take off stuff
};
// usage:
var airplane = Object.create(Vehicles.Airplane);
airplane.prop = params; // maybe also an "init" function, but that seems weird to me
You got Object.create wrong. The first argument should be an object (maybe that's why people suggested you pass a literal).
In your first example, you're actually passing undefined:
Vehicles.Airplane = Object.create(Vehicles.Vehicle()); // the function call will
// return undefined
The following would work, but it's not very Crockford-ish:
Vehicles.Airplane = Object.create(new Vehicles.Vehicle());
The way I believe Crockford would do it (or, at least, wouldn't complain of):
var Vehicles = {};
Vehicles.Vehicle = {
go : function() {
// go stuff
},
stop : function() {
// go stuff
}
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle, {
takeOff : {
value : function() {
// take-off stuff
}
},
land : {
value: function() {
// land stuff
}
}
});
Note that Vehicles.Vehicle is just a literal, which will be used as the prototype for other objects. When we call Object.create, we pass Vehicles.Vehicle as the prototype, and takeOff and land will be own properties of Vehicles.Airplane. You may then call Object.create again, passing Vehicles.Airplane as the prototype, if you want to create e.g. a Boeing.
The own properties passed as the second parameter are packed in an object that contains a representation of their property descriptors. The outer keys are the names of your properties/methods, and each one points to another object containing the actual implementation as the value. You may also include other keys like enumerable; if you don't they'll take the default values. You can read more about descriptors on the MDN page about Object.defineProperty.

Categories

Resources