Расширение класса 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