06.03.2014, 20:17
|
|
junior
|
|
Регистрация: 29.11.2011
Сообщений: 3,924
|
|
Многоуровневое наследование, супер методы, наследование дескрипторов
Всем привет
Этот пост отвечает на следующие вопросы:
1. Как написать наследование на чистом javascript
2. Как написать многоуровневое наследование
3. Как написать наследование дескрипторов
4. Как написать многоуровневое наследование с наследованием дескрипторов
5. Как вызывать супер методы
Никаких библиотек не подключено, весь используемый код расположен ниже.
В качестве "многоуровневое наследования" выступают три класса:
Animal -> Mammal - Cat.
В качестве супер-метода выступает метод destroy().
Данный пост отражает точку зрения автора и носит рекомендательный характер.
/**********************
* HELPERS
* *********************/
/**
* @borrows toString as toString
*/
Object.toString = Object.prototype.toString;
/**
* Returns "true" if a value is date
* @param {*} v A value
* @return {Boolean}
*/
Date.isDate = function(v) {
return Object.toString.call(v) === '[object Date]';
};
/**
* Returns "true" if a value is regular expression
* @param {*} v A value
* @return {Boolean}
*/
RegExp.isRegExp = function(v) {
return Object.toString.call(v) === '[object RegExp]';
};
/**
* Returns "true" if a value is array
* @param {*} v A value
* @return {Boolean}
*/
Array.isArray = Array.isArray || function(v) {
return Object.toString.call(v) === '[object Array]';
};
/**
* Creates clone of object
* Not working with DOM elements
* [url]http://stackoverflow.com/a/728694[/url]
* [url]https://github.com/andrewplummer/Sugar/blob/master/lib/object.js#L328[/url]
* @param {Object} obj
* @return {Object}
*/
Object.clone = function(obj) {
// Number, String, Boolean, Function, null, undefined
if (null === obj || 'object' !== typeof obj) {
return obj;
}
// Date and RegExp
if (Date.isDate(obj) || RegExp.isRegExp(obj)) {
return new obj.constructor(obj);
// Array and Object
} else {
var copy = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = this.clone(obj[key]);
}
}
return copy;
}
};
/**
* Inherits a target (Class_1) by a source (Class_2)
* @param {Object} target
* @param {Object} source
*/
Object.inherit = function(target, source) {
target._super = source;
target.prototype = Object.create(target._super.prototype, target._super.descriptor);
target.prototype.constructor = target;
};
/**********************
* CLASSES
* *********************/
/**
* Class Animal
* @param {Number} legs
* @constructor
*/
function Animal(legs) {
var descriptor = Object.clone(Animal.descriptor);
descriptor.legs.value = legs;
Object.defineProperties(this, descriptor);
}
Animal.prototype.destroy = function() {
console.log('Animal.destroy()');
for(var key in Animal.descriptor) {
delete this[key];
}
};
Animal.prototype.run = function() {
console.log('Animal.run()');
};
Animal.descriptor = {
legs: {
value: null,
enumerable: true,
configurable: true
}
};
// ------------------------
// Inheritance
Object.inherit(Mammal, Animal);
/**
* Class Mammal
* @param {Number} legs
* @param {Number} teeth
* @constructor
*/
function Mammal(legs, teeth) {
// call the Animal constructor
Mammal._super.apply(this, arguments);
var descriptor = Object.clone(Mammal.descriptor);
descriptor.teeth.value = teeth;
Object.defineProperties(this, descriptor);
}
Mammal.prototype.destroy = function() {
console.log('Mammal.destroy()');
Mammal._super.prototype.destroy.call(this);
for(var key in Mammal.descriptor) {
delete this[key];
}
};
Mammal.prototype.walk = function() {
console.log('Mammal.walk()');
};
Mammal.descriptor = {
teeth: {
value: null,
enumerable: true,
configurable: true
}
};
// ------------------------
// Inheritance
Object.inherit(Cat, Mammal);
/**
* Class Cat
* @param {Number} legs
* @param {Number} teeth
* @param {Number} tails
* @constructor
*/
function Cat(legs, teeth, tails) {
// call the Mammal constructor
Cat._super.apply(this, arguments);
var descriptor = Object.clone(Cat.descriptor);
descriptor.tails.value = tails;
Object.defineProperties(this, descriptor);
}
Cat.prototype.destroy = function() {
console.log('Cat.destroy()');
Cat._super.prototype.destroy.call(this);
for(var key in Cat.descriptor) {
delete this[key];
}
};
Cat.prototype.climb = function() {
console.log('Cat.climb()');
};
Cat.descriptor = {
tails: {
value: null,
enumerable: true,
configurable: true
}
};
/********************
* USAGE / INSTANCE
********************/
var cat = new Cat(8, 100, 3);
alert(JSON.stringify(cat));
console.info('created', cat);
console.log('cat instanceof Cat', cat instanceof Cat);
console.log('cat instanceof Mammal', cat instanceof Mammal);
console.log('cat instanceof Animal', cat instanceof Animal);
cat.run(); // Animal.prototype.run
cat.walk(); // Mammal.prototype.walk
cat.climb(); // Cat.prototype.climb
cat.destroy(); // Cat -> Mammal -> Animal
alert(JSON.stringify(cat));
console.info('destroyed', cat);
Последний раз редактировалось nerv_, 16.03.2014 в 20:59.
Причина: вместо термина "множественное" в данном случее правильней использовать "многоуровневое"
|
|
14.03.2014, 10:29
|
|
|
Регистрация: 10.07.2008
Сообщений: 3,873
|
|
Сообщение от nerv_
|
Object.toString = Object.prototype.toString;
|
Нафига? В Object и так есть toString
Сообщение от nerv_
|
// Inheritance
Object.inherit(…, …);
|
Я бы подумал, что это какие-то манипуляции с __proto__, может быть лучше в Function или вообще отдельно?
Кстати, мне последнее время нравится идея создавать объекты, не описывая конструкторы:
var Animal = {
destroy: function () {
console.log('Animal.destroy()');
},
run: function () {
console.log('Animal.run()');
}
};
var Mammal = Object.create(Animal);
Object.assign(Mammal, {
walk: function () {
console.log('Mammal.walk()');
}
});
var Cat = Object.create(Mammal);
как-то так… правда тут с instanceof непонятки могут возникнуть
Последний раз редактировалось Octane, 14.03.2014 в 10:37.
|
|
14.03.2014, 10:43
|
sinistral
|
|
Регистрация: 28.03.2011
Сообщений: 5,418
|
|
Сообщение от Octane
|
Нафига? В Object и так есть toString
|
а что это за статический метод? откуда он взялся?
Octane, согласен, ES6 - крутая вещь
|
|
14.03.2014, 10:53
|
|
|
Регистрация: 10.07.2008
Сообщений: 3,873
|
|
Сообщение от melky
|
а что это за статический метод? откуда он взялся?
|
Object.toString === Function.prototype.toString // true
|
|
14.03.2014, 12:30
|
|
junior
|
|
Регистрация: 29.11.2011
Сообщений: 3,924
|
|
Сообщение от Octane
|
Нафига? В Object и так есть toString
|
есть, только не работает =)
Сообщение от Octane
|
Я бы подумал, что это какие-то манипуляции с __proto__
|
Никакой черной магии, все законно
Немного переписал функцию наследования согласно тому, что есть в Node.js
Если точнее, то вместо Class._super стало Class.super_
Сообщение от Octane
|
Кстати, мне последнее время нравится идея создавать объекты, не описывая конструкторы
|
а еще нет прототипов и нет супер методов
Сообщение от Octane
|
может быть лучше в Function или вообще отдельно?
|
чего?
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук
Последний раз редактировалось nerv_, 14.03.2014 в 12:42.
|
|
14.03.2014, 13:02
|
Профессор
|
|
Регистрация: 14.09.2012
Сообщений: 162
|
|
|
|
15.03.2014, 01:15
|
|
|
Регистрация: 10.07.2008
Сообщений: 3,873
|
|
Сообщение от nerv_
|
а еще нет прототипов и нет супер методов
|
В том и идея, что описываем прототипы, вместо конструкторов. Кроме как в конструкторе, ссылка super чаще всего бывает нужна только в выдуманных примерах, а тут нет конструкторов.
Единственное неудобство в instanceof, но можно воспользоваться isPrototypeOf
var barsic = Object.create(Cat);
if (Cat.isPrototypeOf(barsic)) {
…
}
а еще getPrototypeOf (IE9+) есть, так что без super вполне можно обойтись
Object.getPrototypeOf(Cat) → Mammal
http://www.reddit.com/r/javascript/c...s_approach_is/
Не навязываю, что надо делать именно так, просто как пример интересного подхода
Сообщение от Octane
|
может быть лучше в Function или вообще отдельно?
Сообщение от nerv_
|
чего?
Сообщение от BallsShaped
|
Метод .inherit
|
|
|
Я про то, что в Object методу inherit не место.
Последний раз редактировалось Octane, 15.03.2014 в 05:46.
|
|
16.03.2014, 17:33
|
|
junior
|
|
Регистрация: 29.11.2011
Сообщений: 3,924
|
|
Сообщение от Octane
|
В том и идея, что описываем прототипы, вместо конструкторов
|
Куда писать те свойства, кот. должны быть созданы для каждого экземпляра, как не в конструктор?
Т.е. моя позиция в том, что придется описывать и то и другое.
А еще у нас есть дескрипторы.
Сообщение от Octane
|
Единственное неудобство в instanceof, но можно воспользоваться isPrototypeOf
|
я бы не стал так утверждать)
Сообщение от Octane
|
Кроме как в конструкторе, ссылка super чаще всего бывает нужна только в выдуманных примерах, а тут нет конструкторов.
|
Что то я тебя не понял Если есть необходимость в множественном наследовании, то ссылка нужна и на мой взгляд удобнее, чем
Object.getPrototypeOf(object)
Сообщение от Octane
|
Я про то, что в Object методу inherit не место.
|
не хотел расширять прототипы. В целом на усмотрение разработчика
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук
|
|
16.03.2014, 18:07
|
|
|
Регистрация: 10.07.2008
Сообщений: 3,873
|
|
Кстати, тут ни разу не множественное наследование, у тебя всегда один суперкласс.
Сообщение от nerv_
|
Куда писать те свойства, кот. должны быть созданы для каждого экземпляра, как не в конструктор?
|
Либо инициализирующий метод, либо отдельно set-метод для каждого свойства/группы свойств, что мне кажется лучше. Это намного нагляднее, чем искать что там произошло после super.apply.
Сообщение от nerv_
|
Сообщение от Octane
|
Единственное неудобство в instanceof, но можно воспользоваться isPrototypeOf
|
я бы не стал так утверждать)
|
Можно подробнее в чем проблема?
var Animal = {};
var Mammal = Object.create(Animal);
var Cat = Object.create(Mammal);
var barsic = Object.create(Cat);
alert(Animal.isPrototypeOf(barsic)); // true
alert(Mammal.isPrototypeOf(barsic)); // true
alert(Cat.isPrototypeOf(barsic)); // true
Сообщение от nerv_
|
Сообщение от Octane
|
Кроме как в конструкторе, ссылка super чаще всего бывает нужна только в выдуманных примерах, а тут нет конструкторов.
|
Что то я тебя не понял Если есть необходимость в множественном наследовании, то ссылка нужна и на мой взгляд удобнее, чем Object.getPrototypeOf(object)
|
Вместо
Cat._super.prototype.destroy.call(this);
будет
Object.getPrototypeOf(Cat).destroy.call(this);
разве что в большем количестве символов неудобство
Сообщение от nerv_
|
А еще у нас есть дескрипторы.
|
Да дескрипторы у тебя это так наворот к классическому примеру с наследованием, для меня в этом нет никакого академического интереса.
Последний раз редактировалось Octane, 16.03.2014 в 18:52.
|
|
16.03.2014, 20:19
|
|
junior
|
|
Регистрация: 29.11.2011
Сообщений: 3,924
|
|
Сообщение от Octane
|
Кстати, тут ни разу не множественное наследование, у тебя всегда один суперкласс.
|
Согласен. Я выразился не совсем точно. Следовало бы переименовать в "многоуровневое" (например). А нативного множественного наследования в js нет, только mixin'ы.
Сообщение от Octane
|
Можно подробнее в чем проблема?
|
следуя общепринятым соглашениям по наименованию, с больших букв пишутся имена "классов". А раз это класс - это функция, следовательно можно использовать оператор new:
var Animal = {};
var Cat = Object.create(Animal);
var obj = new Cat();
console.log(obj);
Конечно, тут можно заявлять, что встроенные операторы языка нам не нужны, но если они есть, почему бы ими не воспользоваться?
Т.е. ты ломаешь то, что заложено в язык и к чему привыкли js-разработчики.
Сообщение от Octane
|
Да дескрипторы у тебя это так наворот к классическому примеру с наследованием
|
скорее как сахар. Признаться, я их тоже не люблю, но в одном проекте потребовались
Вместе с тем, считаю очень удобным то, что, например, приватные свойства
this._property;
можно делать неперебираемыми.
Сообщение от Octane
|
Либо инициализирующий метод, либо отдельно set-метод для каждого свойства/группы свойств, что мне кажется лучше. Это намного нагляднее, чем искать что там произошло после super.apply.
|
1. "Либо инициализирующий метод" та же функция конструктор
2. "либо отдельно set-метод для каждого свойства/группы свойств, что мне кажется лучше" не вижу причин, по которым нельзя сделать те же самые сет-методы в "стандартной" реализации
Впрочем, сколько людей, столько и мнений
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук
|
|
|
|