Класс Расширения 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?
9 ответов:
Обновлено ниже для 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:если вы находитесь в положении необходимости поддерживать их, связанный документ MDN предлагает использовать полифилл или следующее приближение:
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 дает вам теперь возможность использовать класс & выходит ключевые слова :
тогда ваш код будет:
у вас есть базовый класс:
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 } }
попробуйте это:
Function.prototype.extends = function(parent) { this.prototype = Object.create(parent.prototype); }; Monkey.extends(Monster); function Monkey() { Monster.apply(this, arguments); // call super }
Edit: я поставил здесь быструю демонстрацию http://jsbin.com/anekew/1/edit. обратите внимание, что
extends
- это зарезервированное слово в JavaScript и вы можете получить предупреждение, когда пылеобразования ваш код, вы можете просто назвать его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"
Если вам не нравится подход прототипа, потому что он действительно не ведет себя хорошо ООП-образом, вы можете попробовать это:
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
Это расширение (извините за каламбур) решения 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; }
для последнего бита подготовки мы улучшаем наш прототип функции с новым 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(); } );
для традиционного расширения вы можете просто написать суперкласс как функцию конструктора, а затем применить этот конструктор для унаследованного класса.
function AbstractClass() { this.superclass_method = function(message) { // do something }; } function Child() { AbstractClass.apply(this); // Now Child will have superclass_method() }
пример на angularjs:
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'); } }])
Для Кулибиных:
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...
создать "экземпляр" с геттером и сеттером:
function doNotExtendMe(toBePrivate){ var morePrivates; return { // add getters, setters and any stuff you want } }
Я могу предложить один вариант, просто прочитали в книге, кажется, самый простой:
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
резюме:
существует несколько способов, которые могут решить проблему расширения функции конструктора с прототипом в Javascript. Какой из этих методов является "лучшим" решением, основано на мнении. Однако вот два часто используемых метода для расширения прототипа функции конструктора.
ES 2015 классы:
class Monster { constructor(health) { this.health = health } growl () { console.log("Grr!"); } } class Monkey extends Monster { constructor (health) { super(health) // call super to execute the constructor function of Monster this.bananaCount = 5; } } const monkey = new Monkey(50); console.log(typeof Monster); console.log(monkey);
вышеуказанный подход использования
ES 2015
классы не более чем синтаксические сахар над шаблоном прототипного наследования в javascript. Вот первый журнал, где мы оцениваемtypeof Monster
мы можем наблюдать, что это функция. Это потому, что классы-это просто конструкторские функции под капотом. Тем не менее, вам может понравиться этот способ реализации прототипного наследования и окончательно должен выучить его. Он используется в основных системах, таких какReactJS
иAngular2+
.функции фабрике, используя
Object.create()
:function makeMonkey (bananaCount) { // here we define the prototype const Monster = { health: 100, growl: function() { console.log("Grr!");} } const monkey = Object.create(Monster); monkey.bananaCount = bananaCount; return monkey; } const chimp = makeMonkey(30); chimp.growl(); console.log(chimp.bananaCount);
этот метод использует
Object.create()
метод, который принимает объект, который будет прототипом вновь созданный объект возвращается. Поэтому мы сначала создаем прототип объекта в этой функции, а затем вызываемObject.create()
, который возвращает пустой объект с__proto__
свойство, установленное для объекта монстра. После этого мы можем инициализировать все свойства объекта, в этом примере мы назначаем bananacount для вновь созданного объект.