interface.js
Runtime Interfaces for JavaScript
Motivation
The Javascript language currently lacks any type of reliable inheritance or interface mechanism, even the class
keyword is
just a syntax sugar over a regular constructor function.
interface.js provides a lightweight and extensible API to create interface objects that have a defined set of methods (aka "interfaces"), to which code can register implementations specific to each class that conforms to that interface.
Examples
With interface.js the JavaScript equivalent of
interface Person {
void talk();
void walk();
String getFullName();
}
is possible:
const Person = new JavascriptInterface(["talk", "walk", "getFullName"]);
Person.getTalkImpl = function(somePerson) {
return this.getImplementation(
somePerson,
"talk",
function(obj) { console.log(`${obj.toString()} is talking!`); },
function(obj) { console.log(`${obj} is talking!`); },
);
};
Person.getWalkImpl = function(somePerson) {
return this.getImplementation(
somePerson,
"walk",
function(obj) { console.log(`${obj.toString()} is walking!`); },
function(obj) { console.log(`${obj} is walking!`); },
);
};
Person.getFullNameImpl = function(somePerson) {
return this.getImplementation(
somePerson,
"getFullName",
function(obj) { return ""; },
function(obj) { return obj; },
);
};
function GoodPerson(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.setImplementation(GoodPerson, "talk", function(goodPerson) {
console.log(`A Good person named ${goodPerson.firstName} ${goodPerson.lastName} is talking!`);
});
Person.setImplementation(GoodPerson, "walk", function(goodPerson) {
console.log(`${goodPerson.firstName} ${goodPerson.lastName} is striding purposefully!`);
});
Person.setImplementation(GoodPerson, "getFullName", function(goodPerson) {
return `${goodPerson.firstName} ${goodPerson.lastName}`;
});
function BadPerson(nickname) {
this.nickname = nickname;
}
Person.setImplementation(BadPerson, "talk", function(badPerson) {
console.log(`${badPerson.nickname} is in the house!`);
});
Person.setImplementation(BadPerson, "walk", function(badPerson) {
console.log(`${badPerson.nickname} is coming to get you!`);
});
Person.setImplementation(BadPerson, "getFullName", function(badPerson) {
return `${badPerson.nickname}`;
});
function UglyPerson(name) {
this.name = name;
}
Person.setImplementation(UglyPerson, "talk", function(uglyPerson) {
console.log(`Thus spoke ${uglyPerson.name}! But did someone listen?`);
});
Person.setImplementation(UglyPerson, "walk", function(uglyPerson) {
console.log(`${uglyPerson.name} walks alone...`);
});
Person.setImplementation(UglyPerson, "getFullName", function(uglyPerson) {
return `${uglyPerson.name}`;
});
let people = [new GoodPerson("Geralt", "of Rivia"), new BadPerson("Lucifer"), new UglyPerson("Zarathustra"), "The Great Merlin"];
for (const person of people) {
console.log(`Full Name: ${Person.getFullNameImpl(person)(person)}`);
Person.getWalkImpl(person)(person);
Person.getTalkImpl(person)(person);
}
Finding the class of an object
Implementing the __isobjectinstance__
interface
Continuing the previous example, the setIsObjectInstance
method allows you to set a method that takes an object and returns a Boolean,
indicating whether that object is an instance of the class:
Person.setIsObjectInstance(GoodPerson, function(obj) {
const keys = Object.keys(obj);
return keys.length == 2 &&
keys.indexOf("firstName") > -1 &&
keys.indexOf("lastName") > -1;
});
Person.setIsObjectInstance(BadPerson, function(obj) {
const keys = Object.keys(obj);
return keys.length == 1 &&
keys.indexOf("nickname") > -1;
});
Person.setIsObjectInstance(UglyPerson, function(obj) {
const keys = Object.keys(obj);
return keys.length == 1 &&
keys.indexOf("name") > -1;
});
Checking the instance of an object
Use the JavascriptInterface.prototype.isObjectInstance
method to determine if an object is instance of a given class:
let zara = new UglyPerson("Zarathustra");
let caveman = {"name": "CAVE MAN"};
let luci = {"nickname": "Lucifer"};
console.log(`${Person.getFullNameImpl(zara)(zara)} ${Person.isObjectInstance(zara, UglyPerson) ? "is ugly..." : "is not ugly!"}`);
console.log(`${caveman.name} ${Person.isObjectInstance(caveman, UglyPerson) ? "is ugly..." : "is not ugly!"}`);
console.log(`${luci.nickname} ${Person.isObjectInstance(luci, UglyPerson) ? "is ugly..." : "is not ugly!"}`);
Use the JavascriptInterface.prototype.classOfObject
method to determine the object's class:
let mrgood = {"firstName": "John", "lastName": "Doe"};
console.log(`${zara.name} is an ${Person.classOfObject(zara).name}`);
console.log(`${caveman.name} is an ${Person.classOfObject(caveman).name}`);
console.log(`${luci.nickname} is a ${Person.classOfObject(luci).name}`);
console.log(`${mrgood.firstName + " " + mrgood.lastName} is a ${Person.classOfObject(mrgood).name}`);
Running on Node JS
First run:
const JavascriptInterface = require("interfaces.js");
Then you can run the examples above.