250 likes | 468 Views
JavaScript Classes & Inheritance. Marc Heiligers @marcheiligers. Basic JS Classes. Functions act as constructors Functions have a prototype Use new to create an instance of a type Call instance methods with dot notation. function Animal () { this . name = "animal" ; }
E N D
JavaScript Classes & Inheritance • Marc Heiligers • @marcheiligers
Basic JS Classes • Functions act as constructors • Functions have a prototype • Use new to create an instance of a type • Call instance methods with dot notation
functionAnimal() { this.name="animal"; } Animal.prototype.speak=function() { console.log("I'm a "+this.name); }; var animal =newAnimal(); animal.speak(); //logs "I'm a animal"
Object • All JS objects inherit from Object • Object has 2 standard methods: • toString • valueOf • Lot’s of non-standard and newer methods: • https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object
Overriding Methods • Can override super class methods by: • attaching new methods to the prototype property • eg. Animal.prototype.toString = function() { ... }
console.log(animal.toString()); //logs "[object Object]" Animal.prototype.toString=function() { return"["+this.name+" Animal]"; }; console.log(animal.toString()); //logs "[animal Animal]" console.log(animal.valueOf()); //logs Animal { name="animal", ↳speak=function(), toString=function()} console.log(""+ animal.valueOf()); //logs "[animal Animal]" Animal.prototype.valueOf=function() { returnthis.name; }; console.log(animal.valueOf()); //logs "animal"
Add methods to existing Classes • Add methods to the prototype of the existing class • All instances of the class will get those methods • You can extend built in classes too - with a couple of caveats • Generally bad to extend the prototype of Object • Generally accepted as bad to extend the prototype of DOM classes, although MooTools and Prototype do this
String.prototype.articulize=function() { if(["a", "e", "i", "o", "u"].indexOf(this[0].toLowerCase()) ↳>=0) { return"an "+this; } else { return"a "+this; } }; console.log("Pig".articulize()); // logs "a Pig" console.log("Elephant".articulize()); // logs "an Elephant" Animal.prototype.speak=function() { console.log("I'm "+this.name.articulize()); }; animal.speak(); //logs "I'm an animal"
Inheritance • Create a new constructor function • Assign a new instance of the base class to the prototype • function A { ... }function B { ... }B.prototype = new A;
functionPig() { this.name="pig"; } Pig.prototype=newAnimal; var pig =newPig(); pig.speak(); // logs "I'm a pig" console.log(pig.toString()); // logs "[pig Animal]" Animal.prototype.countLegs=function() { returnthis.legs ||0; }; Pig.prototype.legs=4; console.log(animal.valueOf() +" has "+ animal.countLegs() + ↳" legs"); // logs "animal has 0 legs"; console.log(pig.valueOf() +" has "+ pig.countLegs() + ↳" legs"); // logs "pig has 4 legs";
functionEisbein() { this.legs =1; // Instance variable } Eisbein.prototype=newPig; var eisbein =newEisbein(); eisbein.speak(); // logs "I'm a pig" console.log(eisbein.valueOf() +" has "+↳eisbein.countLegs() +" legs"); ↳// logs "pig has 1 legs";
Calling base class methods • Use Function#call to call the base class method in the context of the current class • Base.prototype.method.call(this); • Call is useful in many other places too • Array.prototype.slice.call(arguments); //creates a real array from the arguments oject
Pig.prototype.speak=function() { Animal.prototype.speak.call(this); console.log("Snork"); }; pig.speak(); // logs "I'm a pig\nSnork" Eisbein.prototype.speak=function() { Pig.prototype.speak.call(this); console.log("Sizzle"); }; eisbein.speak(); ↳// logs "I'm a pig\nSnork\nSizzle"
Making it Classical • Create a Class object to create classes • make it look more like classical inheritance • have easier access to super class methods
(function(global) { varisFn=function(fn) { returntypeof fn =="function"; }; global.Class=function() {}; global.Class.create=function(superClass) { varklass=function(magic) { // call init only if there's no magic cookie if(magic != isFn && isFn(this.init)) { this.init.apply(this, arguments); } }; klass.prototype=newthis(isFn); for(key in superClass) { (function(fn, superFn) { // create a closure k.prototype[key] =!isFn(fn) ||!isFn(superFn) ? fn : function() { this.super= sfn; return fn.apply(this, arguments); }; })(superClass[key], k.prototype[key]); } klass.prototype.constructor= klass; klass.extend =this.extend ||this.create; return klass; }; })(this);
(function(global) { varisFn=function(fn) { returntypeof fn =="function"; }; global.Class=function() {}; global.Class.create=function(derived) { varklass=function(magic) { // call init only if there's no magic cookie if(magic != isFn && isFn(this.init)) { this.init.apply(this, arguments); } }; ⇒
⇒ • // use our private method as magic cookie • klass.prototype=newthis(isFn); • for(key in derived) { • (function(fn, superFn) { // create a closure • klass.prototype[key] =!isFn(fn) ||!isFn(superFn) ? • fn : • function() { • this.super= superFn; • return fn.apply(this, arguments); • }; • })(derived[key], klass.prototype[key]); • } • klass.prototype.constructor= klass; • klass.extend =this.extend ||this.create; • return klass; • }; • })(this);
var Animal = Class.create({ init: function() { this.name="animal"; }, speak: function() { console.log("I'm "+this.name.articulize()); }, toString: function() { return"["+this.name+" Animal]"; }, valueOf: function() { returnthis.name; } }); var animal =newAnimal(); animal.speak(); // logs "I'm an animal" console.log(animal.toString()); // logs [animal Animal] console.log(animal.valueOf()); // logs animal
var Pig = Animal.extend({ init: function() { this.name="Pig"; }, speak: function() { this.super(); console.log("Snork"); } }); var Eisbein = Pig.extend({ speak: function() { this.super(); console.log("Sizzle"); } }); var pig =newPig(); pig.speak(); // logs "I'm a pig\nSnork" var eisbein =newEisbein(); eisbein.speak(); // logs "I'm a pig\nSnork\nSizzle"