Crockford's Prototypical Inheritance - Usage - javascript

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.

Related

A native way of adding custom JavaScript functions into a method-calls chain

I would like to known if there is a native way of doing this :
Object.prototype.chain = function(f) { return f.call(this) }
function fun1() {
doSomethingWithObject(this)
return this
}
function fun2() {
doSomethingElse(this)
return this
}
someObject
.method1('something')
.method2()
.chain(checkSomething() ? fun1 : fun2)
.method3()
But I do not feel like changing the prototype of Object. Is there a way to do this without modifying the prototype of Objects or the other constructors that I use (and am not the developer of)
Edits :
I feel I do not explain very well, so let' add some details :
What I would like to do is to use some APIs I do not define. someObject is defined like the following, with chainable methods :
var someObject = {
method1: function(val) {
// do something
return this
},
method2: function() {
// do something
return this
},
method3: function() {
// do something
return this
}
}
Now imagine I cannot change this code, because this object is from a library, and so I don't want to. Then, imagine that I would like to chain methods and some custom functions (see my first snippet) for many more different objects. The simplest thing to do is to attach a chain method to Object.prototype.
But I think that it could result in conflicts in the future. I am looking for a way to do the same thing without touching the prototype.
I'm surprised there are no answers to this to be honest.
There are many ways to natively introduce chaining. I like to use the revealing module pattern.
So I create a basic model (Go ahead and chuck this in your chrome of firefox console)
var Dog = function(name) {
var self = this;
this.name = name;
var core = {
getName:function(){
return self.name;
}
};
this.movement = function(){ //this function will be exposed including its returned functions for chaining
console.log(self.name + " is getting restless... ");
var jump = function(){
console.log(self.name + " jumps around ");
return this //returns the movement scope
};
var run = function(){
console.log(self.name + " has decided to run");
return this //returns the movement scope
};
return {
jump:jump,
run:run
};
}
console.log("A Pup has been born, we shall call him... " + name);
return{
movement:self.movement //only .movement is exposed to the outside world
};
}
Now create a new dog using var p = new Dog("doggyName");
now, you can chain functions. Try:
p.movement().jump().run().jump().run();
You should get the console logged text that corresponds with each function.
By returning the scope of this after executing your movement function you expose the additional functions that are returned in that scope (see the comments in the code). These can then be chained onto the end of your current function provided they are in the same scope. This allows you to scope specific parts of your code. For example with this dog, all movement is scoped to self.movement, you could have all eating scoped to self.eat and so on
Read up on the revealing module pattern. Though this is not the only way to do it.
The wrapper is something that will wrap any object to make it compatible with "chaining" and will add another chain method that will allow you to plug external functions and still get the chaining.
Check this example:
function myObj() {
this.state = {
a: 1
};
this.method1 = function () {
console.log("1");
}
this.method2 = function () {
console.log("2");
}
this.method3 = function () {
console.log("3");
}
this.method4 = function () {
console.log(this.state);
}
}
function objectChainWrapper(obj) {
this.chain = function (fn) {
fn.call(obj);
return this;
}
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop] == 'function') {
this[prop] = (function (methodName) {
return function () {
obj[methodName].call(obj);
return this;
}
}(prop))
}
}
}
var obj = new myObj();
var wrapper = new objectChainWrapper(obj);
var chainMethod = function(){ console.log('chain') };
var chainMethodState = function(){ console.log(this.state) };
wrapper.method1().method2().chain(chainMethodState).method3().chain(chainMethod).method4();
JSFIDDLE.
To "plug" an unbound function into the object's method chain you can assign it to a property and call that:
function fn() {
document.write('hi ');
return this;
}
someObj = {
meth1: function() {
document.write('meth1 ');
return this;
},
meth2: function() {
document.write('meth2 ');
return this;
}
}
someObj
.meth1()
[someObj._=fn, '_']()
.meth2()
This doesn't look very pretty if you ask me. A more readable option is to add the chain method on the fly, like:
function chainable(obj) {
obj.chain = function(fn) {
return fn.call(this);
}
return obj;
}
chainable(someObj).meth1().chain(fn).meth2()

Where/when to set function prototype?

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);

Need help understanding how to define both static and instantiatable classes in javascript

current broken code: http://jsfiddle.net/9F52n/2/
What I'm trying to do: Learn how to define an object/function that behaves like a class, and be able to define subclasses, both static , and instantiatable (singleton in my example below).
Currently, my code below doesn't work all that well. but, if the instantiatable class sand the static class were removed, you'll see that I have the very basics of class creation down.
So, I guess my question is: what is the proper / most semantic way to define nested clases (singleton or otherwise) with the way I've defined TBR? (function(){...})(window)
var TBR = (function() {
// define local copy of taco bell run
var TBR = function() {
return new TBR.fn.init();
},
message = "hello world!";
TBR.fn = TBR.prototype = {
constructor: TBR,
init: function() {
console.log("From TBR Constructor: " + message);
}
}
var InstantiatableClass = function() {
return new TBR.InstantiatableClass, fn.init();
}
InstantiatableClass.fn =InstantiatableClass.prototype = {
constructor: TBR.InstantiatableClass,
init: function() {
console.log("from InstantiatableClass: " + message);
}
}
this.staticClass = function() {
var subMessage = "little world";
init = function() {
console.log("from staticClass: " + subMessage);
}
}
// expose TBR to the window object
window.TBR = TBR;
})(window);
var InstantiatableClass = function() {
return new TBR.InstantiatableClass, fn.init();
}
InstantiatableClass.fn =InstantiatableClass.prototype ...
This does not work. Your InstantiatableClass local variable returns objects, the prototype will not get applied to them. Also, TBR.InstantiatableClass is defined nowhere. If that is what you wanted, you'd need to use
function InstantiatableClass() {
// common constructor things
}
TBR.InstantiatableClass = InstantiatableClass; // assign a "static" property
Also, you should not [need to] overwrite the prototypes. Sure, the only difference is that constructor is enumerable now (as far as it is not forgotten), but the following would be much cleaner:
InstantiatableClass.fn = InstantiatableClass.prototype; // shortcut
InstantiatableClass.fn.init = function() { … };
Oh, you want something that works like jQuery. Imho you should not make the constructor (init) a property of the prototype - that is just very odd and I can't see a reason to do so. I'd suggest this code:
window.TBR = (function() {
function TbrConstructor() {
…
}
function InstantiableConstructor() {
…
}
// Now, the creator functions:
function TBR() { return new TbrConstructor; }
function Instantiable() { return new InstantiableConstructor; }
// Now, overwrite the "prototype" properties - this is needed for
// (new TbrConstructor) instanceof TBR === true
// and also create those fn shortcuts
TBR.fn = TBR.prototype = TbrConstructor.prototype;
Instantiable.fn = Instantiable.prototype = InstantiableConstructor.prototype;
// we might overwrite the "constructor" properties like
TBR.fn.constructor = TBR;
// but I don't see much sense in that - they also have no semantic value
// At last, make Instantiable a property on the TBR function object:
TBR.Instantiable = Instantiable;
// and then only
return TBR;
})();
// Usage
TBR(); // returns a TbrConstructor instance
TBR.Instantiable(); // returns a InstantiableConstructor instance

Javascript: Calling object methods within that object

What is the best design pattern for achieving the following (which doesn't work)?
var obj = (function() {
// code defining private variables and methods
var _obj = {
property: value,
method1: function() {
// do stuff
},
method2: function() {
// use property
var prop = _obj.property; // obviously doesn't work
// call method1
obj.method1(); // "obj" not finished being defined yet!
}
};
// obviously now I could do...
var prop = _obj.property;
return _obj;
})();
// and I could now do...
obj.method1();
A variation which I think should work is
var obj = (function() {
var property = value,
method1 = function() {
// do stuff
},
method2 = function() {
// use property
var prop = property;
// call method1
method1();
},
_obj = {
property: property,
method1: method1,
method2: method2
};
return _obj;
})();
Similarly, how does it work for objects meant to be created with the new operator? Within the constructor function itself you can write this.method(). But what if you want to keep the constructor small, only defining those things which will likely be customized upon creation, and then defining the rest in the prototype? (This seems to be the common pattern.) Can the properties / methods within the prototype interact in any way?
var MyObj = function(name) {
this.name = name;
};
var obj = new MyObj('Bob');
MyObj.prototype = {
called_often: function() {
// lots more code than just the following
return document.getElementById('someID').value;
},
global_default: 'value', // can be changed, so need to pull value when run
does_stuff: function(value) {
var str = global_default + value, // can't access global_default on its own
input = MyObj.called_often(), // doesn't work; MyObj.prototype.called_often() DOES
name = this.name; // 'this' used in the prototype doesn't work
// even within a created object
return name + input + str;
}
};
I'm sure there's better ways to achieve my result whenever I run into this problem. This code isn't situation specific and just illustrates the general problem. So you won't be able to give me an alternative for those specific situations I run into. But maybe you can help my overall thinking.
Well, from your first example:
var _obj = {
property: value,
method1: function() {
// do stuff
},
method2: function() {
// use property
var prop = this.property;
// call method1
this.method1();
}
};
That's what the this value is for.
Now, what you cannot do is refer to a property of an "under construction" object from elsewhere in the object literal syntax. (It's hard to give an example because it's just not syntactically possible.) In cases where you want to do that, you do need one or more separate assignment statements.
Guess what? You are making simple stuff complex. Pointy's answer is good, but the prototype way is better for several reasons. That's why I am describing (rather, making corrections in) the last method. Check this fiddle.
var MyObj = function(name) {
this.name = name;
};
MyObj.prototype = {
called_often: function() {
// lots more code than just the following
return 'VALUE'; //document.getElementById('someID').value;
},
global_default: 'value', // can be changed, so need to pull value when run
does_stuff: function(value) {
var str = this.global_default + value, // can't access global_default on its own
input = this.called_often(), // doesn't work; MyObj.prototype.called_often() DOES
name = this.name; // 'this' used in the prototype doesn't work
// even within a created object
return name + input + str;
}
};
var obj = new MyObj('Bob');

javascript class inherit from Function class

I like that in javascript, I can create a function, and then add further methods and attributes to that function
myInstance = function() {return 5}
myInstance.attr = 10
I would like to create a class to generate these objects. I assume I have to inherit from the Function base class.
In other words, I would like to:
var myInstance = new myFunctionClass()
var x = myInstance()
// x == 5
But I don't know how to create the myFunctionClass. I have tried the following, but it does not work:
var myFunctionClass = function() {Function.call(this, "return 5")}
myFunctionClass.prototype = new Function()
myInstance = new myFunctionClass()
myInstance()
// I would hope this would return 5, but instead I get
// TypeError: Property 'myInstance' of object #<Object> is not a function
I also tried the more complicated (and more proper?) inheritance method found here: How to "properly" create a custom object in JavaScript?, with no more luck. I have also tried using the util.inherits(myFunctionClass, Function) found in node.js. Still no luck
I have exhausted Google, and therefore feel that I must be missing something fundamental or obvious. Help would be greatly appreciated.
Your trying to inherit from Function. This is a right pain to do. I suggest you do the following instead
Live Example
var Proto = Object.create(Function.prototype);
Object.extend(Proto, {
constructor: function (d) {
console.log("construct, argument : ", d);
this.d = d;
// this is your constructor logic
},
call: function () {
console.log("call", this.d);
// this get's called when you invoke the "function" that is the instance
return "from call";
},
method: function () {
console.log("method");
// some method
return "return from method";
},
// some attr
attr: 42
});
You want to create a prototype object that forms the basis of your "class". It has your generic methods/attributes. It also has a constructor that gets invoked on object construction and a call method that gets invoked when you call the function
var functionFactory = function (proto) {
return function () {
var f = function () {
return f.call.apply(f, arguments);
};
Object.keys(proto).forEach(function (key) {
f[key] = proto[key];
});
f.constructor.apply(f, arguments);
return f;
}
}
A function factory takes a prototype object and returns a factory for it. The returned function when called will give you a new function object that "inherits" from your prototype object.
var protoFactory = functionFactory(proto);
var instance = protoFactory();
Here you create your factory and then create your instance.
However this isn't proper prototypical OO. we are just shallow copying properties of a prototype into a new object. So changes to the prototype will not reflect back to the original object.
If you want real prototypical OO then you need to use a hack.
var f = function () {
// your logic here
};
f.__proto__ = Proto;
Notice how we use the non-standard deprecated .__proto__ and we are mutating the value of [[Prototype]] at run-time which is considered evil.
JS does not allow a constructor to return a function, even though functions are objects. So you cant have an instantiation of a prototype that is itself executable. (Am I right in this? please correct if I'm not, it's an interesting question).
Though you could do a factory function:
var makeCoolFunc = function() {
var f = function() { return 5 };
f.a = 123;
f.b = 'hell yes!'
return f;
};
var func = makeCoolFunc();
var x = func();
You can extend Function and pass the wanted function body as String to the super constructor. The context of the function can be accessed with arguments.callee.
Example for an observable Attribute class:
export default class Attribute extends Function {
constructor(defaultValue){
super("value", "return arguments.callee.apply(arguments);");
this.value = defaultValue;
this.defaultValue = defaultValue;
this.changeListeners = [];
}
apply([value]){
if(value!==undefined){
if(value!==this.value){
var oldValue = this.value;
this.value=value;
this.changeListeners.every((changeListener)=>changeListener(oldValue, value));
}
}
return this.value;
}
clear(){
this.value=undefined;
}
reset(){
this.value=this.defaultValue;
}
addChangeListener(listener){
this.changeListeners.push(listener);
}
removeChangeListener(listener){
this.changeListeners.remove(listener);
}
clearChangeListeners(){
this.changeListeners = [];
}
}
Example usage:
import Attribute from './attribute.js';
var name= new Attribute();
name('foo'); //set value of name to 'foo'
name.addChangeListener((oldValue, newValue)=>{
alert('value changed from ' +oldValue+ ' to ' +newValue);
});
alert(name()); //show value of name: 'foo'
name('baa'); //set value of name to new value 'baa' and trigger change listener

Categories

Resources