Расширение класса JavaScript
У меня есть базовый класс:
function Monster() {
this.health = 100;
}
Monster.prototype.growl = function() {
console.log("Grr!");
}
Что я хочу расширить и создать другой класс с помощью
function Monkey extends Monster() {
this.bananaCount = 5;
}
Monkey.prototype.eatBanana {
this.bananaCount--;
this.health++; //Accessing variable from parent class monster
this.growl(); //Accessing function from parent class monster
}
Я провел довольно много исследований, и, как представляется, в JavaScript есть много запутанных решений для этого. Каким будет самый простой и надежный способ выполнить это в JS?
Ответы
Ответ 1
Обновлено ниже для ES6
Март 2013 и ES5
Этот документ MDN хорошо описывает расширяющие классы:
https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript
В частности, теперь они обрабатывают его:
// define the Person Class
function Person() {}
Person.prototype.walk = function(){
alert ('I am walking!');
};
Person.prototype.sayHello = function(){
alert ('hello');
};
// define the Student class
function Student() {
// Call the parent constructor
Person.call(this);
}
// inherit Person
Student.prototype = Object.create(Person.prototype);
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
// replace the sayHello method
Student.prototype.sayHello = function(){
alert('hi, I am a student');
}
// add sayGoodBye method
Student.prototype.sayGoodBye = function(){
alert('goodBye');
}
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
// check inheritance
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
Обратите внимание, что Object.create()
не поддерживается в некоторых старых браузерах, включая IE8:
![Object.create browser support]()
Если вам необходимо поддерживать их, связанный документ MDN предлагает использовать polyfill или следующее приближение:
function createObject(proto) {
function ctor() { }
ctor.prototype = proto;
return new ctor();
}
Использование этого типа Student.prototype = createObject(Person.prototype)
предпочтительнее использования new Person()
, поскольку избегает вызова функции родительского конструктора при наследовании прототипа и вызывает только родительский конструктор когда вызывается конструктор наследования.
май 2017 и ES6
К счастью, дизайнеры JavaScript услышали наши просьбы о помощи и приняли более подходящий способ приблизиться к этой проблеме.
MDN имеет еще один отличный пример наследования класса ES6, но я покажу тот же самый набор классов, что и выше, воспроизведенный в ES6:
class Person {
sayHello() {
alert('hello');
}
walk() {
alert('I am walking!');
}
}
class Student extends Person {
sayGoodBye() {
alert('goodBye');
}
sayHello() {
alert('hi, I am a student');
}
}
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
// check inheritance
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
Чистое и понятное, точно так же, как мы все хотим. Имейте в виду, что хотя ES6 довольно распространен, не поддерживается везде:
![Поддержка браузера ES6]()
Ответ 2
Попробуйте следующее:
Function.prototype.extends = function(parent) {
this.prototype = Object.create(parent.prototype);
};
Monkey.extends(Monster);
function Monkey() {
Monster.apply(this, arguments); // call super
}
Изменить: Я поставил быструю демонстрацию здесь http://jsbin.com/anekew/1/edit. Обратите внимание, что extends
является зарезервированным словом в JS, и вы можете получать предупреждения, когда вы набираете код, вы можете просто называть его inherits
тем, что я обычно делаю.
Используя этот помощник и используя объект props
как только параметр, наследование в JS становится немного проще:
Function.prototype.inherits = function(parent) {
this.prototype = Object.create(parent.prototype);
};
function Monster(props) {
this.health = props.health || 100;
}
Monster.prototype = {
growl: function() {
return 'Grrrrr';
}
};
Monkey.inherits(Monster);
function Monkey() {
Monster.apply(this, arguments);
}
var monkey = new Monkey({ health: 200 });
console.log(monkey.health); //=> 200
console.log(monkey.growl()); //=> "Grrrr"
Ответ 3
ES6 дает вам возможность использовать ключевые слова class и extends:
Тогда ваш код будет выглядеть следующим образом:
У вас есть базовый класс:
class Monster{
constructor(){
this.health = 100;
}
growl() {
console.log("Grr!");
}
}
Что вы хотите расширить и создать другой класс с помощью:
class Monkey extends Monster {
constructor(){
super(); //don't forget "super"
this.bananaCount = 5;
}
eatBanana() {
this.bananaCount--;
this.health++; //Accessing variable from parent class monster
this.growl(); //Accessing function from parent class monster
}
}
Ответ 4
Если вам не нравится подход к прототипу, потому что он не очень хорошо работает с OOP-способом, вы можете попробовать следующее:
var BaseClass = function()
{
this.some_var = "foobar";
/**
* @return string
*/
this.someMethod = function() {
return this.some_var;
}
};
var MyClass = new Class({ extends: BaseClass }, function()
{
/**
* @param string value
*/
this.__construct = function(value)
{
this.some_var = value;
}
})
Использование облегченной библиотеки (2k minified): https://github.com/haroldiedema/joii
Ответ 5
Это расширение (извините за каламбур) решения elclanrs, чтобы включать подробные сведения о методах экземпляра, а также использовать расширяемый подход к этому аспекту вопроса; Я полностью признаю, что это объединено благодаря Дэвиду Фланагану "JavaScript: окончательное руководство" (частично скорректированное для этого контекста). Обратите внимание, что это явно более многословно, чем другие решения, но, вероятно, это принесет пользу в долгосрочной перспективе.
Сначала мы используем функцию "расширенного" Дэвида, которая копирует свойства для указанного объекта:
function extend(o,p) {
for (var prop in p) {
o[prop] = p[prop];
}
return o;
}
Затем мы реализуем его утилиту определения подкласса:
function defineSubclass(superclass, // Constructor of our superclass
constructor, // Constructor of our new subclass
methods, // Instance methods
statics) { // Class properties
// Set up the prototype object of the subclass
constructor.prototype = Object.create(superclass.prototype);
constructor.prototype.constructor = constructor;
if (methods) extend(constructor.prototype, methods);
if (statics) extend(constructor, statics);
return constructor;
}
За последний бит подготовки мы улучшаем прототип функции с помощью David new jiggery-pokery:
Function.prototype.extend = function(constructor, methods, statics) {
return defineSubclass(this, constructor, methods, statics);
};
После определения нашего класса Monster мы делаем следующее (которое можно повторно использовать для любых новых классов, которые мы хотим расширить/наследовать):
var Monkey = Monster.extend(
// constructor
function Monkey() {
this.bananaCount = 5;
Monster.apply(this, arguments); // Superclass()
},
// methods added to prototype
eatBanana: function() {
this.bananaCount--;
this.health++;
this.growl();
}
);
Ответ 6
Для традиционного расширения вы можете просто написать суперкласс как функцию конструктора,
а затем примените этот конструктор для вашего унаследованного класса.
function AbstractClass() {
this.superclass_method = function(message) {
// do something
};
}
function Child() {
AbstractClass.apply(this);
// Now Child will have superclass_method()
}
Пример для угловых:
http://plnkr.co/edit/eFixlsgF3nJ1LeWUJKsd?p=preview
app.service('noisyThing',
['notify',function(notify){
this._constructor = function() {
this.scream = function(message) {
message = message + " by " + this.get_mouth();
notify(message);
console.log(message);
};
this.get_mouth = function(){
return 'abstract mouth';
}
}
}])
.service('cat',
['noisyThing', function(noisyThing){
noisyThing._constructor.apply(this)
this.meow = function() {
this.scream('meooooow');
}
this.get_mouth = function(){
return 'fluffy mouth';
}
}])
.service('bird',
['noisyThing', function(noisyThing){
noisyThing._constructor.apply(this)
this.twit = function() {
this.scream('fuuuuuuck');
}
}])
Ответ 7
Для автодоменов:
function BaseClass(toBePrivate){
var morePrivates;
this.isNotPrivate = 'I know';
// add your stuff
}
var o = BaseClass.prototype;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// MiddleClass extends BaseClass
function MiddleClass(toBePrivate){
BaseClass.call(this);
// add your stuff
var morePrivates;
this.isNotPrivate = 'I know';
}
var o = MiddleClass.prototype = Object.create(BaseClass.prototype);
MiddleClass.prototype.constructor = MiddleClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// TopClass extends MiddleClass
function TopClass(toBePrivate){
MiddleClass.call(this);
// add your stuff
var morePrivates;
this.isNotPrivate = 'I know';
}
var o = TopClass.prototype = Object.create(MiddleClass.prototype);
TopClass.prototype.constructor = TopClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// to be continued...
Создайте "экземпляр" с помощью getter и setter:
function doNotExtendMe(toBePrivate){
var morePrivates;
return {
// add getters, setters and any stuff you want
}
}
Ответ 8
Я могу предложить один вариант, просто прочитав в книге, он кажется самым простым:
function Parent() {
this.name = 'default name';
};
function Child() {
this.address = '11 street';
};
Child.prototype = new Parent(); // child class inherits from Parent
Child.prototype.constructor = Child; // constructor alignment
var a = new Child();
console.log(a.name); // "default name" trying to reach property of inherited class