Skip to content

Instantly share code, notes, and snippets.

@indutny
Created July 14, 2010 07:08
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save indutny/475129 to your computer and use it in GitHub Desktop.
Save indutny/475129 to your computer and use it in GitHub Desktop.
/*
* def.js: Простое наследование в стиле Ruby для Javascript
*
* Copyright (c) 2010 Tobias Schneider
* This script is freely distributable under the terms of the MIT license.
*/
(function(global) {
// Используется, чтобы сохранить суперкласс и "плагины" ля
// дальнейшего использования
// used to defer setup of superclass and plugins
var deferred;
// Добавляет родителю "плагины"
function addPlugins(plugins) {
augment(this, this.prototype, plugins);
return this;
}
// Копирует аттрибуты и методы объекта "source" в объект "destination"
// И в его прототип
// TODO: Ensure we fix IE to iterate over shadowed properties
// of those further up the prototype chain. There is also a
// bug in Safari 2 that will match the shadowed property and
// the one further up the prototype chain.
function augment(destination, destinationPrototype, source) {
for (var key in source) {
destinationPrototype[key] = destination[key] = source[key];
}
}
// Пустой класс, необходим для наследования
// dummy subclass
function Subclass() { }
function def(klassName, context) {
context || (context = global);
// Создаем класс в указаном контексте (по умолчанию global)
// create class on given context (defaults to global object)
var Klass =
context[klassName] = function Klass() {
// Если вызывается как конструктор
// called as a constructor
if (this != context) {
// Если в классе есть "init" метод, то он может вернуть
// другой класс/объект
// allow the init method to return a different class/object
return this.init && this.init.apply(this, arguments);
}
// Вызывается как функция
// defer setup of superclass and plugins
// Сохраним в "deferred" класс и "плагины", которые могли быть
// переданы первым аргументом
// called as a method
// defer setup of superclass and plugins
deferred._super = Klass;
deferred._plugins = arguments[0] || { };
};
// Добавляем метод, чтобы запускать его в контексте
// add static helper method
Klass.addPlugins = addPlugins;
// Будет вызываться эта функция,
// если класс создается без наследования
// called as function when not
// inheriting from a superclass
deferred = function(plugins) {
return Klass.addPlugins(plugins);
};
// Благодаря функции valueOf
// будет осуществляться наследование
// valueOf is called to setup
// inheritance from a superclass
deferred.valueOf = function() {
// Возьмем класс, который мы сохранили в "deferred"
var Superclass = deferred._super;
// Если класса нет - значит мы должны
// вернуть сам класс, чтобы вести себя
// как нормальная valueOf функция
if (!Superclass) return Klass.valueOf();;
// Создаем клон супер класса с пустым конструктором
Subclass.prototype = Superclass.prototype;
// Создаем объект
Klass.prototype = new Subclass;
// Добавляем superclass классу
Klass.superclass = Superclass;
Klass.prototype.constructor = Klass;
// Добавляем "плагины", сохраненые в deferred
Klass.addPlugins(deferred._plugins);
// Возвращаем нормальное valueOf
// the result of the inheritance experession
// isn't normally captured so this is really edge
// case stuff here and of little practical use
return Klass.valueOf()
};
// Возвращаем deferred -
// функцию, принимающую атрибуты и методы, а затем создающую класс
return deferred;
}
// Выносим def из замыкания
// expose
global.def = def;
})(this);
// Example
def ('Person') ({
'init': function(name) {
this.name = name;
},
'speak': function(text) {
alert(text || 'Здравствуй, меня зовут ' + this.name);
}
});
def ('Ninja') << Person ({
'ask': function() {
this.speak('Ты веришь, что здесь моя скорость и сила зависят от моих мускулов?');
}
});
var ninjy = new Ninja('Морфеус');
ninjy.speak();
ninjy.ask();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment