Расширяющий класс JavaScript

javascript oop object inheritance ecmascript-6

76495 просмотра

10 ответа

У меня есть базовый класс:

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?

Автор: Lucas Penney Источник Размещён: 17.05.2019 02:49

Ответы (10)


130 плюса

Решение

Обновлен ниже для 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

Если вам требуется их поддержка, в связанном документе 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

Автор: Oliver Spryn Размещён: 04.03.2013 12:33

12 плюса

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
        }

}
Автор: Abdennour TOUMI Размещён: 06.08.2016 12:46

10 плюса

Попробуй это:

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"
Автор: elclanrs Размещён: 04.03.2013 12:35

6 плюса

Если вам не нравится подход с прототипом, потому что он не очень хорошо работает, вы можете попробовать это:

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;
    }
})

Использование облегченной библиотеки (уменьшено до 2 тыс.): Https://github.com/haroldiedema/joii

Автор: Harold Размещён: 22.01.2014 03:55

1 плюс

Я могу предложить один вариант, просто прочитав в книге, он кажется самым простым:

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
Автор: Yarik Размещён: 17.03.2017 07:13

1 плюс

Это расширение (извините за каламбур) решения 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;
}

Для последней части подготовки мы улучшаем наш прототип Function с помощью нового 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();
        }
    }
);
Автор: GMeister Размещён: 09.10.2014 08:45

0 плюса

Для традиционного расширения вы можете просто написать суперкласс как функцию конструктора, а затем применить этот конструктор для вашего унаследованного класса.

     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');
    }
  }])
Автор: Dan Key Размещён: 25.08.2015 07:06

0 плюса

Для автодидактов:

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
    }
}
Автор: Steffomio Размещён: 26.12.2016 10:47

0 плюса

Резюме:

Есть несколько способов, которые могут решить проблему расширения функции конструктора с помощью прототипа в 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__свойством, установленным для объекта Monster. После этого мы можем инициализировать все свойства объекта, в этом примере мы назначаем bananacount вновь созданному объекту.

Автор: Willem van der Veen Размещён: 31.08.2018 08:00

0 плюса

абсолютно минимальная (и правильная, в отличие от многих приведенных выше) версия:

function Monkey(param){
  this.someProperty = param;
}
Monkey.prototype = Object.create(Monster.prototype);
Monkey.prototype.eatBanana = function(banana){ banana.eat() }

Это все. Вы можете прочитать здесь более длинное объяснение

Автор: gdanov Размещён: 16.12.2018 11:20
Вопросы из категории :
32x32