Making objects observable - javascript

I've been looking into JavaScript frameworks such as Angular and Meteor lately, and I was wondering how they know when an object property has changed so that they could update the DOM.
I was a bit surprised that Angular used plain old JS objects rather than requiring you to call some kind of getter/setter so that it could hook in and do the necessary updates. My understanding is that they just poll the objects regularly for changes.
But with the advent of getters and setters in JS 1.8.5, we can do better than that, can't we?
As a little proof-of-concept, I put together this script:
(Edit: updated code to add dependent-property/method support)
function dependentProperty(callback, deps) {
callback.__dependencies__ = deps;
return callback;
}
var person = {
firstName: 'Ryan',
lastName: 'Gosling',
fullName: dependentProperty(function() {
return person.firstName + ' ' + person.lastName;
}, ['firstName','lastName'])
};
function observable(obj) {
if (!obj.__properties__) Object.defineProperty(obj, '__properties__', {
__proto__: null,
configurable: false,
enumerable: false,
value: {},
writable: false
});
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
if(!obj.__properties__[prop]) obj.__properties__[prop] = {
value: null,
dependents: {},
listeners: []
};
if(obj[prop].__dependencies__) {
for(var i=0; i<obj[prop].__dependencies__.length; ++i) {
obj.__properties__[obj[prop].__dependencies__[i]].dependents[prop] = true;
}
delete obj[prop].__dependencies__;
}
obj.__properties__[prop].value = obj[prop];
delete obj[prop];
(function (prop) {
Object.defineProperty(obj, prop, {
get: function () {
return obj.__properties__[prop].value;
},
set: function (newValue) {
var oldValue = obj.__properties__[prop].value;
if(oldValue !== newValue) {
var oldDepValues = {};
for(var dep in obj.__properties__[prop].dependents) {
if(obj.__properties__[prop].dependents.hasOwnProperty(dep)) {
oldDepValues[dep] = obj.__properties__[dep].value();
}
}
obj.__properties__[prop].value = newValue;
for(var i=0; i<obj.__properties__[prop].listeners.length; ++i) {
obj.__properties__[prop].listeners[i](oldValue, newValue);
}
for(dep in obj.__properties__[prop].dependents) {
if(obj.__properties__[prop].dependents.hasOwnProperty(dep)) {
var newDepValue = obj.__properties__[dep].value();
for(i=0; i<obj.__properties__[dep].listeners.length; ++i) {
obj.__properties__[dep].listeners[i](oldDepValues[dep], newDepValue);
}
}
}
}
}
});
})(prop);
}
}
return obj;
}
function listen(obj, prop, callback) {
if(!obj.__properties__) throw 'object is not observable';
obj.__properties__[prop].listeners.push(callback);
}
observable(person);
listen(person, 'fullName', function(oldValue, newValue) {
console.log('Name changed from "'+oldValue+'" to "'+newValue+'"');
});
person.lastName = 'Reynolds';
Which logs:
Name changed from "Ryan Gosling" to "Ryan Reynolds"
The only problem I see is with defining methods such as fullName() on the person object which would depend on the other two properties. This requires a little extra markup on the object to allow developers to specify the dependency.
Other than that, are there any downsides to this approach?
JsFiddle

advent of getters and setters in JS 1.8.5 - are there any downsides to this approach?
You don't capture any property changes apart from the observed ones. Sure, this is enough for modeled entity objects, and for anything else we could use Proxies.
It's limited to browsers that support getters/setters, and maybe even proxies. But hey, who does care about outdated browsers? :-) And in restricted environments (Node.js) this doesn't hold at all.
Accessor properties (with getter and setter) are much slower than real get/set methods. Of course I don't expect them to be used in critical sections, and they can make code looking much fancier. Yet you need to keep that in the back of your mind. Also, the fancy-looking code can lead to misconceptions - normally you would expect property assignment/accessing to be a short (O(1)) operation, while with getters/setters there might be a lot of more happening. You will need to care not forgetting that, and the use of actual methods could help.
So if we know what we are doing, yes, we can do better.
Still, there is one huge point we need to remember: the synchronity/asynchronity (also have a look at this excellent answer). Angular's dirty checking allows you to change a bunch of properties at once, before the event fires in the next event loop turn. This helps to avoid (the propagation of) semantically invalid states.
Yet I see the synchronous getters/setters as a chance as well. They do allow us to declare the dependencies between properties and define the valid states by this. It will automatically ensure the correctness of the model, while we only have to change one property at a time (instead of changing firstName and fullName all the time, firstName is enough). Nevertheless, during dependency resolving that might not hold true so we need to care about it.
So, the listeners that are not related to the dependencies management should be fired asynchronous. Just setImmediate their loop.

Related

How to register Polymer behavior with ES6?

If I do
Polymer({
is: 'pp-app',
behaviors: [PlayPlan.HelperBehavior],
scrollPageToTop() {
document.getElementById('mainContainer').scrollTop = 0;
},
onDataRouteClick() {
var drawerPanel = document.querySelector('#paperDrawerPanel');
if (drawerPanel.narrow) {
drawerPanel.closeDrawer();
}
}
});
The behavior here will works fine, but in es6
class PlayPlanApp {
beforeRegister() {
this.is = 'pp-app';
this.properties = {};
this.behaviors = [PlayPlan.HelperBehavior];
}
scrollPageToTop() {
document.getElementById('mainContainer').scrollTop = 0;
}
onDataRouteClick() {
var drawerPanel = document.querySelector('#paperDrawerPanel');
if (drawerPanel.narrow) {
drawerPanel.closeDrawer();
}
}
}
Polymer(PlayPlanApp);
The behavior will not works, how to specify behaviors when using es6 ?
This is caused by
registerCallback: function() {
// TODO(sjmiles): perhaps this method should be called from polymer-bootstrap?
this._desugarBehaviors(); // abstract
this._doBehavior('beforeRegister'); // abstract
this._registerFeatures(); // abstract
this._doBehavior('registered'); // abstract
}
_doBehavior('beforeRegister') is after _desugarBehaviors, here is an work around:
get behaviors() {
return [PlayPlan.HelperBehavior];
}
BTW, this will not work, because the constructor never get called(commented in Polymer.Base)
constructor() {
return [PlayPlan.HelperBehavior];
}
Note that the answer given by wener will not work if you use extended behaviors. Extended behaviors in Polymer are arrays of behaviors themselves. I don't know what's going on exactly but apparently Polymer is parsing the protoype of your class and flattening the behavior array, writing the flattened version back to your prototype. The fixed getter function will ignore the flattened version and always return the deep array. That will not do. So you have two options.
You can flatten the array yourself:
get behaviors() {
return [
MyFlatBehavior, MyExtendedBehavior
].reduce(function(a, b) {return a.concat(b);});
}
However, since Polymer may call get behaviors a lot (it does in my case), this will introduce a minor performance penalty. The alternative is letting it set the behaviors:
let SkeletonPolymerBehavior = [MyFlatBehavior, MyExtendedBehavior];
class SkeletonPolymer {
get behaviors( ) {return SkeletonPolymerBehavior;}
set behaviors(b) {SkeletonPolymerBehavior = b;}
...
}
Finally you can flatten the array right away:
let SkeletonPolymerBehavior = [
MyFlatBehavior, MyExtendedBehavior
].reduce(function(a, b) {return a.concat(b);});
class SkeletonPolymer {
get behaviors( ) {return SkeletonPolymerBehavior;}
...
}
Note that my flatten function is not safe! It worked in my case, but it will fail when extending extended behaviors and possibly in other cases. You definitely need something more sophisticated there. If you don't want to bother (and I see no compelling reason to) you should definitely go with the second version, letting Polymer set the behaviors.
Final note: I tried abstracting that away into an es2015 Behaviors mixin. However, Polymer's prototype magic does not catch stuff inherited from base classes and the getters and setters defined there are never called.
However, it is possible to mess with the prototype the es5 way. This utility function will ease the addition of extended behaviors:
function addBehaviors(clas, ...behaviors) {
let bhv = behaviors;
Object.defineProperty(clas.prototype, "behaviors", {
get:function( ) {return bhv;},
set:function(b) {return bhv = b;}
});
}
Usage:
class SkeletonPolymer {...}
addBehaviors(SkeletonPolymer, MyFlatBehavior, MyExtendedBehavior);

My ReactJS flux App has too many 'actiontype' constants, how can I separate and namespace them?

I'm building a flux app that involves many different types of data, and the CRUD style modification of the resources. This leads to the a large number of ActionTypes. Most of them follow the same pattern, REQUEST_ENTITY, REQUEST_ENTITY_SUCCESS, REQUEST_ENTITY_ERROR, and so on.
How do I separate them into namespaced constants?
Ideally instead of accessing them like:
ActionTypes.REQUEST_ENTITY
I could access them in a more sane way like,
ActionTypes.entity.REQUEST
Why not skip the constants, and just use the string values? Sure, you may mistype one from time to time, but you could just as easily mistype the constant names, right? Your unit tests will fail in the same place, either way, and you'll know what's wrong.
Without compile-time checking, the main value of these kinds of constant lists is that the code is a bit more self-documenting, but if you're that consistent in your naming conventions, it might not be worth the extra effort to write them all out as constants?
(That was kind of a non-answer, I guess, but I've had this same conversation with others, so probably worth adding to the discussion here, too.)
You could simply merge multiple objects (perhaps exported from different files) into ActionTypes.
// entity_actions.js
module.exports = {
entity: {
REQUEST: "entity.REQUEST",
DELETE: "entity.DELETE",
}
};
// user_actions.js
module.exports = {
user: {
REQUEST: "user.REQUEST",
DELETE: "user.DELETE",
}
};
// actions.js
var entityActions = require("./entity_actions");
var userActions = require("./user_actions");
var ActionTypes = Object.assign({}, entityActions, userActions);
You can use something like Underscore#extend or object-assign if Object.assign isn't available in your environment.
I personally use a small module I called nestedKeyMirror that takes a big nested object and automatically generates values based on the nesting:
function nestedKeyMirror(obj, namespace) {
namespace = namespace || [];
for (key in obj) {
if (obj.hasOwnProperty(key) && obj[key] === null) {
obj[key] = namespace.concat([key]).join(":");
} else if (obj.hasOwnProperty(key) && typeof obj[key] === "object") {
obj[key] = nestedKeyMirror(obj[key], namespace.concat([key]));
}
}
return obj;
}
For example, in one app, I have the following action types defined:
var actionTypes = nestedKeyMirror({
LAYOUT: {
RESIZE_PANE: null
},
CANVAS: {
SET_PROPERTY: null
},
SHAPES: {
ADD: null,
SET_PROPERTY: null,
SEND_BACKWARD: null,
SEND_FORWARD: null,
SEND_TO_BACK: null,
SEND_TO_FRONT: null
},
SELECTION: {
SELECT: null,
DESELECT_ALL: null
},
HISTORY: {
ADD: null,
SELECT_INDEX: null
}
});
This would give, e.g., actionTypes.SHAPES.ADD with an automatically-generated string value of "SHAPES:ADD". This technique can be combined with the object-merging strategy, above, to easily create deeply nested action type constants.
[Update: it looks like someone recently released a package that does the nested key mirroring on npm: keymirror-nested]
If the problem is that all your action types look similar, you could easily create a function to generate them (ES6 computed property syntax used here):
function generateActionType(type, base) {
return {
[base]: `${base}_${type}`,
[`${base}_SUCCESS`]: `${base}_${type}_SUCCESS`,
[`${base}_ERROR`]: `${base}_${type}_ERROR`
};
}
ActionTypes.entity = {};
Object.assign(ActionTypes.entity, generateActionType("ENTITY", "REQUEST"));
Object.assign(ActionTypes.entity, generateActionType("ENTITY", "DELETE"));
ActionTypes.entity.REQUEST_SUCCESS === "REQUEST_ENTITY_SUCCESS";

What are the possible ways to create a two-way binding between properties using Object.defineProperty?

Say, I have an object type that implements a 3D-object, which has both quaternion and 3-axis rotation properties:
Object.defineProperty(MyObject3D.prototype, 'rotation', {
...
});
Object.defineProperty(MyObject3D.prototype, 'quaternion', {
...
});
and I want to make sure that each time I modify each of them, another property is recalculated automatically. Obviously, if I make both set methods of those properties, I'll run into infinite loop.
So far, the only way of doing this that I can see is to actually use another set of properties that resemble rotation and quaternion (probably non-enumerable, for a bit cleaner object introspection):
Object.defineProperty(MyObject3D.prototype, '_rotation', {
enumerable: false
});
Object.defineProperty(MyObject3D.prototype, '_quaternion', {
enumerable: false
});
Object.defineProperty(MyObject3D.prototype, 'rotation', {
get: function() {
return this._rotation;
},
set: function(r) {
this._rotation = r;
this._quaternion = rotation2quaternion(r);
}
});
Object.defineProperty(MyObject3D.prototype, 'quaternion', {
get: function() {
return this._rotation;
},
set: function(q) {
this._quaternion = q;
this._rotation= quaternion2rotation(q);
}
});
Any better ways you can come up with? Maybe I'm missing some abilities of Object.defineProperty that could make this even cleaner and shorter?
Instead of making rotation and quaternion accessor properties wouldn't it be simpler to make them data properties and then define a setRotation and a setQuaternion method to mutate them? For example:
function MyObject3D() {
...
}
MyObject3D.prototype.setRotation = function (r) {
this.quaternion = rotation2quaternion(r);
this.rotation = r
return this;
};
MyObject3D.prototype.setQuaternion = function (q) {
this.rotation = quaternion2rotation(q);
this.quaternion = q;
return this;
};
Now you can access .rotation and .quaternion normally. However to set them you use .setRotation and .setQuaternion instead. Yes it is unsafe since the user may manually set .rotation and .quaternion. However it has several advantages:
It will work in browsers which don't support defineProperty.
It allows you to chain operations since it returns this.
It's easy to understand what the functions do.
Another way to solve this problem would be to use immutable objects. This is the way you would do it in Haskell. For example:
function MyObject3D(rotation, quaternion) {
this.quaternion = quaternion;
this.rotation = rotation;
Object.freeze(this);
}
MyObject3D.prototype.putRotation = function (r) {
return new MyObject3D(r, rotation2quaternion(r));
};
MyObject3D.prototype.putQuaternion = function (q) {
return new MyObject3D(quaternion2rotation(q), q);
};
If you have some initialization logic in your constructor then you can move it into a smart constructor:
function createMyObject3D(r, q) {
// some initialization logic
return new MyObject3D(r, q);
}
Hence when you want to create a new object you use createMyObject3D and when you want to mutate the object you use new MyObject3D passing in the new values. The advantage of the following method is:
Your code becomes referentially transparent. Thus it can be subjected to equational reasoning.
It still allows you to chain operations since it returns a brand new object.
Nobody can tamper with the properties of your object.
It's easy to understand what the functions do.
The only disadvantage is that it depends upon Object.freeze which may not be present in old browsers. Nevertheless freezing the object is an optional step and can be omitted.

JavaScript getter support in IE8

Check out this code. This is a very simple JavaScript object which is implemented using Module Pattern (and you can see the live example at this fiddle address)
var human = function() {
var _firstName = '';
var _lastName = ''
return {
get firstName() {
return _firstName;
}, get lastName() {
return _lastName;
}, set firstName(name) {
_firstName = name;
}, set lastName(name) {
_lastName = name;
}, get fullName() {
return _firstName + ' ' + _lastName;
}
}
}();
human.firstName = 'Saeed';
human.lastName = 'Neamati';
alert(human.fullName);
However, IE8 doesn't support JavaScript get and set keywords. You can both test it and see MDN.
What should I do to make this script compatible with IE8 too?
What should I do to make this script compatible with IE8 too?
Change it completely. For example, instead of using accessor properties, use a combination of normal properties and functions:
human.firstName = 'Saeed';
human.lastName = 'Neamati';
alert(human.getFullName());
Somebody else suggested using a DOM object in IE and adding the properties using Object.defineProperty(). While it may work, I'd highly recommend against this approach for several reasons, an example being that the code you write may not be compatible in all browsers:
var human = document.createElement('div');
Object.defineProperty(human, 'firstName', { ... });
Object.defineProperty(human, 'lastName', { ... });
Object.defineProperty(human, 'children', { value: 2 });
alert(human.children);
//-> "[object HTMLCollection]", not 2
This is true of at least Chrome. Either way it's safer and easier to write code that works across all the browsers you want to support. Any convenience you gain from being able to write code to take advantage of getters and setters has been lost on the extra code you wrote specifically targeting Internet Explorer 8.
This is, of course, in addition to the reduction in performance, the fact that you will not be able to use a for...in loop on the object and the potential confusion ensuing when you use a property you thought you defined but was pre-existing on the DOM object.
You cannot (as Andy answered)
The closest alternative would be
var human = function() {
var _firstName = '';
var _lastName = '';
return {
firstName: function() {
if (arguments.length === 1) {
_firstName = arguments[0];
}
else {
return _firstName;
}
},
lastName: function() {
if (arguments.length === 1) {
_lastName = arguments[0];
}
else {
return _lastName;
}
},
fullName: function() {
return _firstName + ' ' + _lastName;
}
};
}();
human.firstName('Saeed');
human.lastName('Neamati');
alert(human.fullName());
Demo at http://jsfiddle.net/gaby/WYjqB/2/
IE8 supports getters and setters on DOM nodes, so if you really want to have getters and setters, you can do this:
var objectForIe8 = $("<div></div>")[0];
Object.defineProperty(objectForIe8, "querySelector", {
get: function() {
return this.name;
},
set: function(val) {
this.name = val+", buddy";
}
});
// notice you can overwrite dom properties when you want to use that property name
objectForIe8.querySelector = "I'm not your guy";
alert(objectForIe8.querySelector);
Note this gives you a somewhat significant performance hit, so I wouldn't use this technique if you need to create thousands of objects like this. But if you're not worried about performance of this particular object, it'll tide you over. And if you couldn't care less about ie8 performance, and just want it to work, use this technique for ie8 only and you're golden : )
Check it on http://robertnyman.com/2009/05/28/getters-and-setters-with-javascript-code-samples-and-demos/
The future, and ECMAScript standardized way, of extending objects in
all sorts of ways is through Object.defineProperty. This is how
Internet Explorer chose to implement getters and setters, but it is
unfortunately so far only available in Internet Explorer 8, and not in
any other web browser. Also, IE 8 only supports it on DOM nodes, but
future versions are planned to support it on JavaScript objects as
well.
You can find the test cases on the same site at http://robertnyman.com/javascript/javascript-getters-setters.html#object-defineproperty
Object.defineProperty(document.body, "description", {
get : function () {
return this.desc;
},
set : function (val) {
this.desc = val;
}
});
document.body.description = "Content container";
Result:
document.body.description = "Content container"

Does JavaScript have the interface type (such as Java's 'interface')?

I'm learning how to make OOP with JavaScript. Does it have the interface concept (such as Java's interface)?
So I would be able to create a listener...
There's no notion of "this class must have these functions" (that is, no interfaces per se), because:
JavaScript inheritance is based on objects, not classes. That's not a big deal until you realize:
JavaScript is an extremely dynamically typed language -- you can create an object with the proper methods, which would make it conform to the interface, and then undefine all the stuff that made it conform. It'd be so easy to subvert the type system -- even accidentally! -- that it wouldn't be worth it to try and make a type system in the first place.
Instead, JavaScript uses what's called duck typing. (If it walks like a duck, and quacks like a duck, as far as JS cares, it's a duck.) If your object has quack(), walk(), and fly() methods, code can use it wherever it expects an object that can walk, quack, and fly, without requiring the implementation of some "Duckable" interface. The interface is exactly the set of functions that the code uses (and the return values from those functions), and with duck typing, you get that for free.
Now, that's not to say your code won't fail halfway through, if you try to call some_dog.quack(); you'll get a TypeError. Frankly, if you're telling dogs to quack, you have slightly bigger problems; duck typing works best when you keep all your ducks in a row, so to speak, and aren't letting dogs and ducks mingle together unless you're treating them as generic animals. In other words, even though the interface is fluid, it's still there; it's often an error to pass a dog to code that expects it to quack and fly in the first place.
But if you're sure you're doing the right thing, you can work around the quacking-dog problem by testing for the existence of a particular method before trying to use it. Something like
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
So you can check for all the methods you can use before you use them. The syntax is kind of ugly, though. There's a slightly prettier way:
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
This is standard JavaScript, so it should work in any JS interpreter worth using. It has the added benefit of reading like English.
For modern browsers (that is, pretty much any browser other than IE 6-8), there's even a way to keep the property from showing up in for...in:
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
The problem is that IE7 objects don't have .defineProperty at all, and in IE8, it allegedly only works on host objects (that is, DOM elements and such). If compatibility is an issue, you can't use .defineProperty. (I won't even mention IE6, because it's rather irrelevant anymore outside of China.)
Another issue is that some coding styles like to assume that everyone writes bad code, and prohibit modifying Object.prototype in case someone wants to blindly use for...in. If you care about that, or are using (IMO broken) code that does, try a slightly different version:
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}
Pick up a copy of 'JavaScript design patterns' by Dustin Diaz. There's a few chapters dedicated to implementing JavaScript interfaces through Duck Typing. It's a nice read as well. But no, there's no language native implementation of an interface, you have to Duck Type.
// example duck typing method
var hasMethods = function(obj /*, method list as strings */){
var i = 1, methodName;
while((methodName = arguments[i++])){
if(typeof obj[methodName] != 'function') {
return false;
}
}
return true;
}
// in your code
if(hasMethods(obj, 'quak', 'flapWings','waggle')) {
// IT'S A DUCK, do your duck thang
}
JavaScript (ECMAScript edition 3) has an implements reserved word saved up for future use. I think this is intended exactly for this purpose, however, in a rush to get the specification out the door they didn't have time to define what to do with it, so, at the present time, browsers don't do anything besides let it sit there and occasionally complain if you try to use it for something.
It is possible and indeed easy enough to create your own Object.implement(Interface) method with logic that baulks whenever a particular set of properties/functions are not implemented in a given object.
I wrote an article on object-orientation where use my own notation as follows:
// Create a 'Dog' class that inherits from 'Animal'
// and implements the 'Mammal' interface
var Dog = Object.extend(Animal, {
constructor: function(name) {
Dog.superClass.call(this, name);
},
bark: function() {
alert('woof');
}
}).implement(Mammal);
There are many ways to skin this particular cat, but this is the logic I used for my own Interface implementation. I find I prefer this approach, and it is easy to read and use (as you can see above). It does mean adding an 'implement' method to Function.prototype which some people may have a problem with, but I find it works beautifully.
Function.prototype.implement = function() {
// Loop through each interface passed in and then check
// that its members are implemented in the context object (this).
for(var i = 0; i < arguments.length; i++) {
// .. Check member's logic ..
}
// Remember to return the class being tested
return this;
}
JavaScript Interfaces:
Though JavaScript does not have the interface type, it is often times needed. For reasons relating to JavaScript's dynamic nature and use of Prototypical-Inheritance, it is difficult to ensure consistent interfaces across classes -- however, it is possible to do so; and frequently emulated.
At this point, there are handfuls of particular ways to emulate Interfaces in JavaScript; variance on approaches usually satisfies some needs, while others are left unaddressed. Often times, the most robust approach is overly cumbersome and stymies the implementor (developer).
Here is an approach to Interfaces / Abstract Classes that is not very cumbersome, is explicative, keeps implementations inside of Abstractions to a minimum, and leaves enough room for dynamic or custom methodologies:
function resolvePrecept(interfaceName) {
var interfaceName = interfaceName;
return function curry(value) {
/* throw new Error(interfaceName + ' requires an implementation for ...'); */
console.warn('%s requires an implementation for ...', interfaceName);
return value;
};
}
var iAbstractClass = function AbstractClass() {
var defaultTo = resolvePrecept('iAbstractClass');
this.datum1 = this.datum1 || defaultTo(new Number());
this.datum2 = this.datum2 || defaultTo(new String());
this.method1 = this.method1 || defaultTo(new Function('return new Boolean();'));
this.method2 = this.method2 || defaultTo(new Function('return new Object();'));
};
var ConcreteImplementation = function ConcreteImplementation() {
this.datum1 = 1;
this.datum2 = 'str';
this.method1 = function method1() {
return true;
};
this.method2 = function method2() {
return {};
};
//Applies Interface (Implement iAbstractClass Interface)
iAbstractClass.apply(this); // .call / .apply after precept definitions
};
Participants
Precept Resolver
The resolvePrecept function is a utility & helper function to use inside of your Abstract Class. Its job is to allow for customized implementation-handling of encapsulated Precepts (data & behavior). It can throw errors or warn -- AND -- assign a default value to the Implementor class.
iAbstractClass
The iAbstractClass defines the interface to be used. Its approach entails a tacit agreement with its Implementor class. This interface assigns each precept to the same exact precept namespace -- OR -- to whatever the Precept Resolver function returns. However, the tacit agreement resolves to a context -- a provision of Implementor.
Implementor
The Implementor simply 'agrees' with an Interface (iAbstractClass in this case) and applies it by the use of Constructor-Hijacking: iAbstractClass.apply(this). By defining the data & behavior above, and then hijacking the Interface's constructor -- passing Implementor's context to the Interface constructor -- we can ensure that Implementor's overrides will be added, and that Interface will explicate warnings and default values.
This is a very non-cumbersome approach which has served my team & I very well for the course of time and different projects. However, it does have some caveats & drawbacks.
Drawbacks
Though this helps implement consistency throughout your software to a significant degree, it does not implement true interfaces -- but emulates them. Though definitions, defaults, and warnings or errors are explicated, the explication of use is enforced & asserted by the developer (as with much of JavaScript development).
This is seemingly the best approach to "Interfaces in JavaScript", however, I would love to see the following resolved:
Assertions of return types
Assertions of signatures
Freeze objects from delete actions
Assertions of anything else prevalent or needed in the specificity of the JavaScript community
That said, I hope this helps you as much as it has my team and I.
Hope, that anyone who's still looking for an answer finds it helpful.
You can try out using a Proxy (It's standard since ECMAScript 2015): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
latLngLiteral = new Proxy({},{
set: function(obj, prop, val) {
//only these two properties can be set
if(['lng','lat'].indexOf(prop) == -1) {
throw new ReferenceError('Key must be "lat" or "lng"!');
}
//the dec format only accepts numbers
if(typeof val !== 'number') {
throw new TypeError('Value must be numeric');
}
//latitude is in range between 0 and 90
if(prop == 'lat' && !(0 < val && val < 90)) {
throw new RangeError('Position is out of range!');
}
//longitude is in range between 0 and 180
else if(prop == 'lng' && !(0 < val && val < 180)) {
throw new RangeError('Position is out of range!');
}
obj[prop] = val;
return true;
}
});
Then you can easily say:
myMap = {}
myMap.position = latLngLiteral;
If you want to check via instanceof (asked by #Kamaffeather), you can wrap it in an object like so:
class LatLngLiteral {
constructor(props)
{
this.proxy = new Proxy(this, {
set: function(obj, prop, val) {
//only these two properties can be set
if(['lng','lat'].indexOf(prop) == -1) {
throw new ReferenceError('Key must be "lat" or "lng"!');
}
//the dec format only accepts numbers
if(typeof val !== 'number') {
throw new TypeError('Value must be numeric');
}
//latitude is in range between 0 and 90
if(prop == 'lat' && !(0 < val && val < 90)) {
throw new RangeError('Position is out of range!');
}
//longitude is in range between 0 and 180
else if(prop == 'lng' && !(0 < val && val < 180)) {
throw new RangeError('Position is out of range!');
}
obj[prop] = val;
return true;
}
})
return this.proxy
}
}
This can be done without using Proxy but instead the classes getters and setters:
class LatLngLiteral {
#latitude;
#longitude;
get lat()
{
return this.#latitude;
}
get lng()
{
return this.#longitude;
}
set lat(val)
{
//the dec format only accepts numbers
if(typeof val !== 'number') {
throw new TypeError('Value must be numeric');
}
//latitude is in range between 0 and 90
if(!(0 < val && val < 90)) {
throw new RangeError('Position is out of range!');
}
this.#latitude = val
}
set lng(val)
{
//the dec format only accepts numbers
if(typeof val !== 'number') {
throw new TypeError('Value must be numeric');
}
//longitude is in range between 0 and 180
if(!(0 < val && val < 180)) {
throw new RangeError('Position is out of range!');
}
this.#longitude = val
}
}
abstract interface like this
const MyInterface = {
serialize: () => {throw "must implement serialize for MyInterface types"},
print: () => console.log(this.serialize())
}
create an instance:
function MyType() {
this.serialize = () => "serialized "
}
MyType.prototype = MyInterface
and use it
let x = new MyType()
x.print()
You need interfaces in Java since it is statically typed and the contract between classes should be known during compilation. In JavaScript it is different. JavaScript is dynamically typed; it means that when you get the object you can just check if it has a specific method and call it.
When you want to use a transcompiler, then you could give TypeScript a try. It supports draft ECMA features (in the proposal, interfaces are called "protocols") similar to what languages like coffeescript or babel do.
In TypeScript your interface can look like:
interface IMyInterface {
id: number; // TypeScript types are lowercase
name: string;
callback: (key: string; value: any; array: string[]) => void;
type: "test" | "notATest"; // so called "union type"
}
What you can't do:
Define RegExp patterns for type value
Define validation like string length
Number ranges
etc.
there is no native interfaces in JavaScript,
there are several ways to simulate an interface. i have written a package that does it
you can see the implantation here
Try this: Describe the interface as a class and use #implements JSDoc to show that a given class implements the interface defined. You'll see red squiggly lines on the class name if its not implementing some properties. I tested with VSCode.
// #ts-check
// describe interface using a class
class PlainInterface {
size = 4;
describe() {}
show(){ }
}
/**
* #implements PlainInterface
*/
class ConcretePlain {
size = 4;
describe() {
console.log('I am described')
}
show(){
console.log('I am shown')
}
}
const conc = new ConcretePlain();
conc.describe();
Javascript does not have interfaces. But it can be duck-typed, an example can be found here:
http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html
This is an old question, nevertheless this topic never ceases to bug me.
As many of the answers here and across the web focus on "enforcing" the interface, I'd like to suggest an alternative view:
I feel the lack of interfaces the most when I'm using multiple classes that behave similarly (i.e. implement an interface).
For example, I have an Email Generator that expects to receive Email Sections Factories, that "know" how to generate the sections' content and HTML. Hence, they all need to have some sort of getContent(id) and getHtml(content) methods.
The closest pattern to interfaces (albeit it's still a workaround) I could think of is using a class that'll get 2 arguments, which will define the 2 interface methods.
The main challenge with this pattern is that the methods either have to be static, or to get as argument the instance itself, in order to access its properties. However there are cases in which I find this trade-off worth the hassle.
class Filterable {
constructor(data, { filter, toString }) {
this.data = data;
this.filter = filter;
this.toString = toString;
// You can also enforce here an Iterable interface, for example,
// which feels much more natural than having an external check
}
}
const evenNumbersList = new Filterable(
[1, 2, 3, 4, 5, 6], {
filter: (lst) => {
const evenElements = lst.data.filter(x => x % 2 === 0);
lst.data = evenElements;
},
toString: lst => `< ${lst.data.toString()} >`,
}
);
console.log('The whole list: ', evenNumbersList.toString(evenNumbersList));
evenNumbersList.filter(evenNumbersList);
console.log('The filtered list: ', evenNumbersList.toString(evenNumbersList));
With an interface you can implement a way of polymorphism. Javascript does NOT need the interface type to handle this and other interface stuff. Why? Javascript is a dynamically typed language. Take as example an array of classes that have the same methods:
Circle()
Square()
Triangle()
If you want to know how polymorphism works the Book MFC of David Kruglinsky is great (written for C++)
Implement in those classes the method draw() push the instances of those classes in the array and call the draw() methods in a loop that iterates the array. That's completely valid. You could say you implemented implicitly an abstract class. Its not there in reality but in your mind you did it and Javascript has no problem with it. The difference with an real interface is that you HAVE to implement all the interface methods and that's in this case not needed.
An interface is a contract. You will have to implement all the methods. Only by making it statically you have to do that.
Its questionable to change a language like Javascript from dynamic to static. Its not mend to be static. Experienced developers have no problems with the dynamic nature of Javascript.
So the reason to use Typescript are not clear to me. If you use NodeJS together with Javascript you can build extremely efficient and cost effective enterprise websites. The Javascript/NodeJS/MongoDB combination are already great winners.
I know this is an old one, but I've recently found myself needing more and more to have a handy API for checking objects against interfaces. So I wrote this: https://github.com/tomhicks/methodical
It's also available via NPM: npm install methodical
It basically does everything suggested above, with some options for being a bit more strict, and all without having to do loads of if (typeof x.method === 'function') boilerplate.
Hopefully someone finds it useful.
This is old but I implemented interfaces to use on ES6 without transpiller.
https://github.com/jkutianski/ES6-Interfaces
It bugged me too to find a solution to mimic interfaces with the lower impacts possible.
One solution could be to make a tool :
/**
#parameter {Array|object} required : method name list or members types by their name
#constructor
*/
let Interface=function(required){
this.obj=0;
if(required instanceof Array){
this.obj={};
required.forEach(r=>this.obj[r]='function');
}else if(typeof(required)==='object'){
this.obj=required;
}else {
throw('Interface invalid parameter required = '+required);
}
};
/** check constructor instance
#parameter {object} scope : instance to check.
#parameter {boolean} [strict] : if true -> throw an error if errors ar found.
#constructor
*/
Interface.prototype.check=function(scope,strict){
let err=[],type,res={};
for(let k in this.obj){
type=typeof(scope[k]);
if(type!==this.obj[k]){
err.push({
key:k,
type:this.obj[k],
inputType:type,
msg:type==='undefined'?'missing element':'bad element type "'+type+'"'
});
}
}
res.success=!err.length;
if(err.length){
res.msg='Class bad structure :';
res.errors=err;
if(strict){
let stk = new Error().stack.split('\n');
stk.shift();
throw(['',res.msg,
res.errors.map(e=>'- {'+e.type+'} '+e.key+' : '+e.msg).join('\n'),
'','at :\n\t'+stk.join('\n\t')
].join('\n'));
}
}
return res;
};
Exemple of use :
// create interface tool
let dataInterface=new Interface(['toData','fromData']);
// abstract constructor
let AbstractData=function(){
dataInterface.check(this,1);// check extended element
};
// extended constructor
let DataXY=function(){
AbstractData.apply(this,[]);
this.xy=[0,0];
};
DataXY.prototype.toData=function(){
return [this.xy[0],this.xy[1]];
};
// should throw an error because 'fromData' is missing
let dx=new DataXY();
With classes
class AbstractData{
constructor(){
dataInterface.check(this,1);
}
}
class DataXY extends AbstractData{
constructor(){
super();
this.xy=[0,0];
}
toData(){
return [this.xy[0],this.xy[1]];
}
}
It's still a bit performance consumming and require dependancy to the Interface class, but can be of use for debug or open api.
Js doesn't have interfaces but typescript does!
While there isn't a interface in javaScript as there is in Java you could mimic the behaviour a bit with the code under this message. because an interface is basicly an enforced contract you could build it yourself.
The code below exists out of 3 classes an interface, parent and child class.
The Interface has the methods to check if the methods and properties exist required exist.
The Parent is used to enforce the required methods and properties in the child using the Interface class.
The Child is the class that the parents rules are enforced on.
After you set it up correctly you will see an error in the console if a method or property is missing in the child and nothing if the child implements the contract correctly.
class Interface {
checkRequiredMethods(methodNames) {
setTimeout( () => {
const loopLength = methodNames.length;
let i = 0
for (i; i<loopLength; i++) {
if (typeof this[methodNames[i]] === "undefined") {
this.throwMissingMethod(methodNames[i]);
}
else if (typeof this[methodNames[i]] !== "function") {
this.throwNotAMethod(methodNames[i]);
}
}
}, 0);
}
checkRequiredProperties(propNames) {
setTimeout( () => {
const loopLength = propNames.length;
let i = 0
for (i; i<loopLength; i++) {
if (typeof this[propNames[i]] === "undefined") {
this.throwMissingProperty(propNames[i]);
}
else if (typeof this[propNames[i]] === "function") {
this.throwPropertyIsMethod(propNames[i]);
}
}
}, 0);
}
throwMissingMethod(methodName) {
throw new Error(`error method ${methodName} is undefined`);
}
throwNotAMethod(methodName) {
throw new Error(`error method ${methodName} is not a method`);
}
throwMissingProperty(propName) {
throw new Error(`error property ${propName} is not defined`);
}
throwPropertyIsMethod(propName) {
throw new Error(`error property ${propName} is a method`);
}
}
class Parent extends Interface {
constructor() {
super()
this.checkRequiredProperties([
"p1",
"p2",
"p3",
"p4",
"p5"
]);
this.checkRequiredMethods([
"m1",
"m2",
"m3",
"m4"
]);
}
}
class Child extends Parent {
p1 = 0;
p2 = "";
p3 = false;
p4 = [];
p5 = {};
constructor() {
super();
}
m1() {}
m2() {}
m3() {}
m4() {}
}
new Child()
No, but it has mixins.
You can use Abstract sub-classss or mixins as an alternative https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#mix-ins

Categories