Some programmers advise against using pseudo classical inheritance in Javascript, but advise using duck-typing and giving each object a set of capabilities.
Is there a good example of how that is done? I have an example down below, but it only assign function one at a time. Can we assign a whole group of methods to an object, such as can we set a prototype of OceanAnimal
which can "swim", "dive", and "rise", a prototype of LandAnimal
for "run", "walk", and "jump", and let an object inherit from one or both of them? (so a fish object can inherit or get the capabilities of OceanAnimal
, and a turtle can get the capabilities of both OceanAnimal
and LandAnimal
?)
var yoyo = {
name: "Yoyo",
type: "turtle"
}
var simba = {
name: "Simba",
type: "lion"
}
var dolphy = {
name: "Dolphy",
type: "dolphin"
}
function swim(n) {
console.log("My name is", this.name, ", I am a", this.type, "and I just swam", n, "feet")
}
function run(n) {
console.log("My name is", this.name, ", I am a", this.type, "and I just ran", n, "feet")
}
Object.prototype.respondTo = function(method) {
return !!(this[method] && (typeof this[method] === "function"));
}
yoyo.swim = swim;
yoyo.swim(10);
dolphy.swim = swim;
dolphy.swim(80);
simba.run = run;
simba.run(200);
yoyo.run = run;
yoyo.run(2);
yoyo.walk = run;
yoyo.walk(1);
console.log(simba.respondTo("swim"));
console.log(simba.respondTo("run"));
console.log(simba.respondTo("walk"));
console.log(yoyo.respondTo("run"));
console.log(yoyo.respondTo("walk"));
console.log(yoyo.respondTo("fly"));
if(dolphy.respondTo("run")) {
dolphy.run(10);
}
if(dolphy.respondTo("swim")) {
dolphy.swim(10);
}
output:
My name is Yoyo , I am a turtle and I just swam 10 feet
My name is Dolphy , I am a dolphin and I just swam 80 feet
My name is Simba , I am a lion and I just ran 200 feet
My name is Yoyo , I am a turtle and I just ran 2 feet
My name is Yoyo , I am a turtle and I just ran 1 feet
false
true
false
true
true
false
My name is Dolphy , I am a dolphin and I just swam 10 feet
Duck Typing is a way of programming in which an object passed into a function or method supports all method signatures and attributes expected of that object at run time. The object's type itself is not important. Rather, the object should support all methods/attributes called on it.
The duck-typing technique in TypeScript is used to compare two objects by determining if they have the same type matching properties and objects members or not. For example, if we assign an object with two properties and a method and the second object is only assigned with two properties.
Python is a duck typing language. It means the data types of variables can change as long as the syntax is compatible. Python is also a dynamic programming language.
With nominative typing, an object is of a given type if it is declared to be (or if a type's association with the object is inferred through mechanisms such as object inheritance). In duck typing, an object is of a given type if it has all methods and properties required by that type.
Functions in JavaScript are versatile. They can be used as subroutines, methods, constructors, namespaces, modules, and much more.
The reason people advise against using pseudo classical inheritance in JavaScript is because it hides the true power of JavaScript. Functions are just as expressive if not more expressive than objects. This has been proven by Alonzo Church who's work, Lambda Calculus, is Turing Complete.
To answer your question directly I'll use functions to create a Turtle
, a Lion
and a Dolphin
. Then I'll demonstrate how a turtle is an OceanAnimal
and a LandAnimal
, how a lion is only a LandAnimal
, and how a dolphin is only an OceanAnimal
. I'll conclude by explaining what is duck typing.
First let's create the constructor for an OceanAnimal
:
function OceanAnimal() {
this.swim = function (n) {
return "I am " + this.name + ", the " + this.type +
", and I just swam " + n + " meters.";
};
}
Next we'll create the constructor for a LandAnimal
:
function LandAnimal() {
this.walk = function (n) {
return "I am " + this.name + ", the " + this.type +
", and I just walked " + n + " meters.";
};
}
Alright. So now let's create the constructor for a Turtle
:
Turtle.prototype.type = "turtle";
function Turtle(name) {
this.name = name;
LandAnimal.call(this);
OceanAnimal.call(this);
}
What's happening here? Okay, we want Turtle
to inherit from both OceanAnimal
and LandAnimal
. So we're calling LandAnimal.call(this)
and OceanAnimal.call(this)
. In this way we're using the OceanAnimal
and LandAnimal
constructors as mixins. Thus Turtle
inherits from both OceanAnimal
and LandAnimal
without actually becoming of type OceanAnimal
or LandAnimal
.
Another thing to notice is that we're setting the type
property on the prototype
of Turtle
instead of inside it. This is because type
is the same for all turtles. Thus it's shared. The name
of each turtle on the other hand may vary and hence it's set inside the constructor.
Now let's similarly create the constructor for a Lion
:
Lion.prototype.type = "lion";
function Lion(name) {
this.name = name;
LandAnimal.call(this);
}
Since Lion
is a LandAnimal
we only mix in the LandAnimal
constructor.
Similarly for a Dolphin
:
Dolphin.prototype.type = "dolphin";
function Dolphin(name) {
this.name = name;
OceanAnimal.call(this);
}
Now that we have created all the constructors let's create a turtle, a lion and a dolphin:
var yoyo = new Turtle("Yoyo");
var simba = new Lion("Simba");
var dolphy = new Dolphin("Dolphy");
Awww, now let's set them free:
alert(yoyo.walk(10));
alert(yoyo.swim(30)); // turtles are faster in the water
alert(simba.walk(20));
alert(dolphy.swim(20));
Haha. That was fun. Personally I love yoyo the most.
Okay, so what's duck typing? We know that yoyo is an OceanAnimal
and a LandAnimal
. However if we do yoyo instanceof OceanAnimal
or yoyo instanceof LandAnimal
then it returns false
. What?
Turtle
is an OceanAnimal
and a LandAnimal
!Turtle
.OceanAnimal
, and if it walks then it's a LandAnimal
.So since JavaScript is such a party pooper we'll have to create our own test to check if an object is an OceanAnimal
and if it's a LandAnimal
.
Let's start with an OceanAnimal
:
function isOceanAnimal(object) {
if (typeof object !== "object") return false;
if (typeof object.swim !== "function") return false;
return true;
}
Similarly, for a LandAnimal
:
function isLandAnimal(object) {
if (typeof object !== "object") return false;
if (typeof object.walk !== "function") return false;
return true;
}
So now we can use isOceanAnimal(yoyo)
instead of yoyo instanceof OceanAnimal
, and isLandAnimal(yoyo)
instead of yoyo instanceof LandAnimal
; and both these functions will return true
for our beloved yoyo. Yay!
This is a simple example of duck typing in JavaScript. To conclude:
When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.
Similarly:
When I see an animal which swims like an ocean animal, I call that animal an ocean animal; and when I see an animal which walks like a land animal, I call that animal a land animal.
Edit: You can see the above code in action here: http://jsfiddle.net/aaditmshah/X9M4G/
Instead of trying to subclass, why not use the duck-typing initiative to extend with components and dependency-injection?
var Duck = function (flyable, movable, speakable) {
this.speak = speakable.speak;
this.fly = flyable.fly;
this.position = movable.position;
flyable.subscribe("takeoff", movable.leaveGround);
flyable.subscribe("land", movable.hitGround);
}
var duck = new Duck(new BirdFlier(), new BirdWalker(), new Quacker());
var plane = new Duck(new VehicleFlier(), new Drivable(), new Radio());
duck.speak(); // "quack"
plane.speak(); // "Roger"
Notice that plane
is happily using the same constructor as duck
.
They don't even require a constructor in the first place.
A factory would work just as well.
The point of DuckTyping is that object-construction isn't the concern of the program using the object.
As long as it has the same method/property names, the program will use them, regardless of sub/super/static inheritance.
EDIT: added example components
There are a couple of different ideas here that I'm tying together. So one dot at a time:
First, the basic premise of duck-typing:
// the basic nature of duck-typing
var sheriff = {
gun : {
aim : function () { /* point gun somewhere */ },
bullets : 6,
fire : function () {
if (this.bullets === 0) { return; }
this.bullets -= 1;
/* ... et cetera */
}
},
draw : function () {
this.gun.aim();
this.gun.fire();
}
},
cartoonist = {
pen : { scribble : function () { /* ... */ } },
draw : function () { this.pen.scribble(); }
},
graphicsCard = {
pixels : [ /* ... */ ],
screen : { /* ... */ },
draw : function () {
pixels.forEach(function (pixel) { screen.draw(pixel); });
}
};
// duck-typing at its finest:
sheriff.draw();
cartoonist.draw();
graphicsCard.draw();
The goal being to write functions which don't bother to check what kind of object it is:
function duckDraw (array) { array.forEach(function (obj) { obj.draw(); }); }
duckDraw([ sheriff, cartoonist, graphicsCard ]);
So if you've got a sea_turtle, a dolphin, a whale, a submarine and a minnow, your program doesn't have to care about the differences in how they swim. It just cares that they can all .swim();
. Each item can worry about itself, and the special way it does what it needs to do.
Duck-Typing
Next is dependency-injection. In a way, dependency-injection also uses duck-typing, but on the inside of your class, instead of the outside (or if you do it like I did up top, it starts on the inside, and then allows for duck-typing on the outside, as well).
Think of it like this: Instead of a person inheriting something, just hand it to them.
If you have a soldier
, a sniper
a plane
and a tank
, each one needs a gun
.
Instead of trying to subclass so that they can all fire...
...why not make different kinds of guns, to meet your needs, and hand them all what they need?
var Rifle = function () {
this.reload = function () {};
this.fire = function () { /* ... */ };
},
SniperRifle = function () {
this.reload = function () {};
this.fire = function () {};
},
MachineGun = function () {
this.reload = function () {};
this.fire = function () {};
},
Cannon = function () {
this.reload = function () {};
this.fire = function () {};
};
So now we've got different kinds of guns...
You might think that because they've got the same function-names, and they all deal with bullets, so you could try to subclass... ...but you don't need to - none of these guns do the same thing when they fire or reload... ...so you'd end up writing overrides
to an abstract virtual
method/property in some other language, which would be useless.
So now that we've got them, we can see how we can "inject" them, and what that does for us:
var Soldier = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var Sniper = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var Plane = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var Tank = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var soldier = new Soldier( new Rifle() ),
sniper = new Sniper( new SniperRifle() ),
plane = new Plane( new MachineGun() ),
tank = new Tank( new Cannon() );
So now we've got these classes where they call their guns -- they don't care what kind of gun, and it just works, because the gun knows how the gun works and the combatant knows how to fire a gun, and the program knows how to tell a combatant to fire.
But if you look a little closer, the inner code for each combatant is 100% the same for now.
So why not just have a 'Combatant' that you can give specialized components?
var Combatant = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var soldier = new Combatant( new Rifle() );
So the insides of the constructor are duck-typing gun
, and if you had different classes for combatants, and each class had a fire
method, then you can duck-type your units in the game-logic as well.
Ultimately, the constructor would just hold modules: one module to handle shooting, one to handle ground movement, one for drawing, one for player controls, et cetera... The constructor wouldn't have to do anything except put the pieces in touch with one another, and you could make units special by giving them different kinds of guns, or different kinds of movement, or different kinds of health, which operate differently on the inside, but have the same properties and method names for public access.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With