So I'm new to Javascript, but I've been working on a programming language with similar semantics to C#, and I want to add a JS trans-compiler. I've been looking through various ways of achieving OOP in Javascript, but I'm not entirely sure if it's going to work further down the line.
Let me start with an example. Here's a very simple bit of code in my language:
Code:
// test.fd
// this code will be put in package 'test'
class Foo
{
float bar;
- test
{
int x = 3;
}
}
It outputs this:
Code:
var test = {
function Foo(){
this.bar = 0.0;
};
foo.prototype.test = function(){
var x = 3;
};
}
Now I have a few of questions. Currently, when it compiles the class it creates the js function 'Foo()', which I see is really behaving as a constructor. I'm thinking I should create this by default if the user doesn't create a default constructor, in which case it is used. But what if I wanted to have multiple constructors for the same class? Will they have to have different names, and will I have to recreate all methods and properties for each constructor?
My next question is, what is the best way to achieve inheritance in JS? My language assumes all methods are virtual so it should hopefully make polymorphism easier.
But what if I wanted to have multiple constructors for the same class? Will they have to have different names, and will I have to recreate all methods and properties for each constructor?
JavaScript doesn't support method overloading. Plus, since it's a loosely typed language, your ability to detect different forms of invocation is limited. The best you could do is something like this:
PHP Code:
function Foo(param) { if (typeof param == 'string') { Foo1.apply(this, arguments); } else if (param instanceof SomeType) { Foo2.apply(this, arguments); } }
function Foo1(param) { // ... }
function Foo2(param) { // ... }
Originally Posted by Zotoaster
My next question is, what is the best way to achieve inheritance in JS?
Unfortunately this isn't a simple answer. Most people are unsatisfied with the native way inheritance is expressed in JavaScript, so there's a plethora of patterns and mini-libraries to do inheritance. The simplest looks like this:
PHP Code:
function Animal(name) { this.name = name; }
// New Animal instances inherit from Animal.prototype, // so any methods and properties we attach here // will be available to all instances. Animal.prototype.sayName = function () { console.log(this.name); };
function Dog(name) { // Manually call super constructor Animal.call(this, name); }
// Replace Dog's prototype with a new Animal instance. // Since new Dog instances inherit from Dog.prototype, // and Dog.prototype is now an Animal instance, // which inherits from Animal.prototype, // Dog instances now also inherit from Animal.prototype. Dog.prototype = new Animal();
Dog.prototype.bark = function () { console.log('Woof!'); };
It's verbose. But the biggest functional problem is that you need to create a nameless, stateless Animal instance just to set up inheritance. This problem has been rectified relatively recently with the Object.create method, which we can simulate for older browsers.
PHP Code:
if (!Object.create) { Object.create = function (o) { function F() {} F.prototype = o; return new F(); }; }
// ...
Dog.prototype = Object.create(Animal.prototype);
That's better, but it's still verbose. People have created functions similar to the one below to make things cleaner.
PHP Code:
function extend(superFn, protoProperties) { // The extend function creates the constructor function for you function SubFn() { // Its only job is to call a specially-named method this.init.apply(this, arguments); }
// Set up inheritance if (superFn) { SubFn.prototype = Object.create(superFn.prototype); }
// Copy protoProperties into the SubFn's prototype for (var propertyName in protoProperties) { if (protoProperties.hasOwnProperty(propertyName)) { SubFn.prototype[propertyName] = protoProperties[propertyName]; } }
// And finally return the new constructor function return SubFn; }
And you'd use it like so:
PHP Code:
var Animal = extend(null, { init: function (name) { this.name = name; },
sayName: function () { console.log(this.name); } });
var Dog = extend(Animal, { bark: function () { console.log('Woof!'); } });
Much cleaner. More intricate variations of the above is the de facto standard pattern in JavaScript right now.
I'll admit, though, that I use a different pattern. My reasons are, first, the above pattern doesn't handle static methods or static properties very well. And second, the only reason a type is defined in two parts -- a constructor function and a prototype object -- is because a prototypal language was trying to disguise itself as a classical language. This has been coined as pseudo-classical, and it was done to appeal to the Java crowd. But as the JavaScript community has become more comfortable with prototypal inheritance, I'm more inclined to use an inheritance mechanism that's truer to the prototypal style... No constructor functions. Just objects inheriting from other objects.
PHP Code:
var Base = { extend: function (typeDefinition) { // Spawn var subtype = Object.create(this);
// Augment if (typeDefinition) { subtype.mixIn(typeDefinition); }
// Reference supertype subtype.$super = this;
return subtype; },
create: function () { var instance = this.extend(); instance.init.apply(instance, arguments);
return instance; },
init: function () { },
mixIn: function (properties) { for (var propertyName in properties) { if (properties.hasOwnProperty(propertyName)) { this[propertyName] = properties[propertyName]; } } } };
And you'd use it like so:
PHP Code:
var Animal = Base.extend({ init: function (name) { this.name = name; },
sayName: function () { console.log(this.name); } });
var Dog = Animal.extend({ bark: function () { console.log('Woof!'); } });
Lots of good knowledge in there. I clearly have a lot to learn, but it seems JS is increasingly becoming a more popular target language due to it's cross-platform capabilities, so I'm eager to learn more about using it. Thanks for all the good tips!
if you ask 10 coders "what's the best ____", you should get 10 different answers. That's great, and it shows the flexibility of JS overcoming the "power" of a classical language. Doug Crockford has a lot to say about this, read his many well-written online writings on the subject.
personally, i think classical is over-rated.
i also don't like using looping to tack-on props that should be inherited in some fashion.
recently, i've become a fan of .apply()-based inheritance. I have no idea what it's called (if anything), but it lets me pass arguments at the time of creating the instance (like Classes do) and i can type out the pattern on-demand in a few seconds:
Code:
function Animal(name){
this.name=name;
}// "parent/super/base" definition:
function Dog(){
Animal.apply(this, arguments);
this.type="dog";
}// "subclass" constructor
Dog.prototype.bark=function(){
alert("bow wow");
};// class-wide method
// Let's try it:
var myDog= new Dog("Spot"); // a new dog, yay!
myDog.bark(); // shows "bow wow"
JSON.stringify(myDog); //== {"name":"Spot","type":"dog"}
though i admit the keyword usage isn't the-most readable, i still think it's a very clean syntax pattern, and it provides "enough" oop-functionality for of my projects.
this doesn't preserve instanceof functionality, but you can use your own conventions if you need that (most don't).
you can also use .bind() instead of .apply() to pre-bake arguments to the constructor (currying), which can be nice.
bottom line: play around with all the inheritance options in JS; it's nice to have several to choose from.
In your example code, you have a method on Dog.prototype, but if you had a method on Animal.prototype, how does that inherit? In the pattern you're describing, I think it only works if the methods are created and attached to each instance within the constructor. The downside of that kind of pattern is that each method is re-created for each instance, rather than one copy of each method that is shared across all instances.
Ok folks I've made some progress on it. I found a simple method that helped to do inheritance.
I've come up with some ideas for inheritance, polymorphism and function overloading. Basically I'm creating a table of all methods in a class. If there is a base class, the table is inherited. If you override a method, you override that table index with the new method. Then you simply call using the method index (which is simple using a compiler, but doesn't help readability, regardless, I've put some auto-generated comments in to help).
Here's a very simple app written in my language demonstrating creating classes, methods, inheritance, polymorphism and method overloading.
PHP Code:
class Foo
{
- new
{
}
- test
{
print "bleh";
}
- test(int x)
{
print "hello " + x;
}
}
class Bar : Foo
{
- new
{
}
- test(int x)
{
print "goodbye " + x;
}
}
class App
{
- new
{
Foo t = new Foo();
}
}
Here's the output in javascript.
PHP Code:
// Extension utility
var __extends = this.__extends || function (d, b) {
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
}
Actually... my first suggestion for improvement is not about the inheritance mechanism, but about code readability. Right from the beginning, in your extends function, variable names such as "b" and "d" are extremely unhelpful. And a lack of comments about what the function does and what parameters it takes doesn't help. I had to rename or rewrite some of your code just to understand what it was doing before I could even begin to analyze it.
On to the inheritance mechanism. By-and-large, this seems to be the "apply" pattern that rndme described earlier, except for two key differences: 1) Rather than attaching methods to each instance, instead you're attaching methods to a vtable array, which in turn is attached to each instance. What benefit do you get from attaching to the vtable rather than just attaching to the instance? And 2) rather than indexing methods by name, instead you're indexing methods by an arbitrary number. This is going to make the JavaScript output virtually unintelligible. What benefit is that supposed to provide?
Hi Jeff. I'm really just experimenting with different methods for OOP in JS. I've scrapped the v-table idea because it's redundant. The numbers are placed by the compiler so I can just put the number at the end of the method name. Now all methods are named '_Method1' etc. It doesn't improve readability, but my goal here is to be able to overload functions (which I have achieved). I am going to be adding more back-ends to my language so I don't want to be limited by Javascript's capabilities, I'd rather get a slightly uglier output in the end of the day.
Anyway, here's the same code sample as before (method calls now added):
PHP Code:
class Foo { - new { }
- test { print "bleh"; }
- test(int x) { print "hello " + x; } }
class Bar : Foo { - new { }
- test(int x) { print "goodbye " + x; } }
class App { - new { Foo t = new Bar(); t.test(42); } }
And here's the output. Just ignore the functions at the top:
PHP Code:
/* Utilities */ var __extends = this.__extends || function (d, b) { function __() { this.constructor = d; } __.prototype = b.prototype; d.prototype = new __(); } var __clone = (function(){ return function (obj) { Clone.prototype=obj; return new Clone() }; function Clone(){} }()); function print(str) { var cons = document.getElementById('GameConsole'); if(cons) cons.value += str + '\n';
Bookmarks