Herança em Javascript (unidirecional)

Então, recentemente postei uma dica sobre como escrevo aulas. Então, alguém me perguntou como eu uso herança lá. Bem, uma das funções que eu realmente gosto vem da fonte ACE. Você pode olhar aqui . Vou colar um trecho da função também:

/* From the ACE Source code
Distributed under the BSD license:

Copyright (c) 2010, Ajax.org B.V.

All rights reserved.

*/

exports
.inherits = (function() {
var tempCtor = function() {};
return function(ctor, superCtor) {
tempCtor
.prototype = superCtor.prototype;
ctor
.super_ = superCtor.prototype;
ctor
.prototype = new tempCtor();
ctor
.prototype.constructor = ctor;
};
}());

Quando eu uso esse método específico, este é o fluxo:

var Base = function() { 
};

(function() {
this.method = function method() {
return 'BASE';
}
}).call(Base.prototype);

A classe derivada então:

var Derived = function() { 
};

oop
.inherits(Derived, Base);

(function() {
}).call(Derived.prototype);

Agora, tenho que fazer isso entre o construtor e a definição do protótipo, porque se você olhar o inheritscódigo, verá que ele substitui completamente o protótipo. Portanto, eu apenas deixo o inheritancemétodo copiar o protótipo da classe base e, em seguida, vou em frente e modifico por conta própria. Aqui está outro ponto forte em escrever classes da maneira que eu faço, estou apenas modificando o protótipo, não reescrevendo do zero. No entanto, a grande ressalva aqui é que se você quiser usar o método da superclasse, terá que fazer algo assim dentro de um método:

Derived.super_.method.call(this);

Não tenho problemas com isso, mas não gosto da maneira como tenho que mencionar o nome da classe sempre que chamar o método pai. É um pouco mais detalhado do que eu gostaria. Porém, definitivamente funciona. Sem problemas aí.

Está bem. Agora, o outro grande método que eu realmente gosto é escrito aqui por John Resig (o cara que fez o jQuery). É um excelente artigo para ler se você quiser se atualizar / aprender sobre herança em javascript.

/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/

* MIT Licensed.

*/

// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /b_superb/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};

// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;

// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing
= true;
var prototype = new this();
initializing
= false;

// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype
[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;

// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];

// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;

return ret;
};
})(name, prop[name]) :
prop
[name];
}

// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}

// Populate our constructed prototype object
Class.prototype = prototype;

// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;

// And make this class extendable
Class.extend = arguments.callee;

return Class;
};
})();

Leia os comentários e você terá uma boa ideia do que está acontecendo aqui. Embora haja um problema aqui. Ele escreveu o código pensando que todos nós derivaremos de uma classe base e escreveremos código como este:

var Person = Class.extend({
init
: function(isDancing){
this.dancing = isDancing;
}
});
var Ninja = Person.extend({
init
: function(){
this._super( false );
}
});

var p = new Person(true);
p
.dancing; // => true

var n = new Ninja();
n
.dancing; // => false

Não que haja algo de errado com o acima, só não quero ter que fazer isso para que a herança funcione. Então, eu baguncei e mesclei os dois métodos em algo que gosto.

/* JavaScript Inheritance
* Author: Mutahhir Ali Hayat

* Made by joining some parts by John Resig http://ejohn.org/ and some by Ajax.org Code Editor (ACE)

*/


define
(function(require, exports, module) {
var initializing = false,
fnTest
= /xyz/.test(function() {
xyz
;
}) ? /b_superb/: /.*/;


exports
.inherits = (function() {
var tempCtor = function() {};
return function(ctor, superCtor) {
tempCtor
.prototype= superCtor.prototype;
var prop = ctor.prototype,
_super
= superCtor.prototype,
prototype
= new tempCtor();

for(var name in prop) {
prototype
[name] =
typeof prop[name] === "function" &&
typeof _super[name] === "function" &&
fnTest
.test(prop[name])?
(function(name, fn){
return function(){
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
}
})(name, prop[name]) : prop[name];
}

ctor
.prototype = prototype;
ctor
.prototype.constructor = ctor;

return ctor;
}
})();
});

Como você o usa é mencionado no Gist que criei (esqueci que não estava conectado e ele não me perguntou, então postado anonimamente :))

Basicamente … a principal mudança é que em vez de imprensar a chamada de herança entre o construtor e a definição do protótipo, você precisa fazer algo assim:

var Derived = function() {
}

(function() {
}).call(Derived.prototype);

inherits
(Derived, Base);