Как наследовать от класса в javascript? - PullRequest
95 голосов
/ 21 января 2010

В PHP / Java можно сделать:

class Sub extends Base
{
}

И автоматически все открытые / защищенные методы, свойства, поля и т. Д. Класса Super становятся частью класса Sub, который при необходимости можно переопределить.

Что эквивалентно этому в Javascript?

Ответы [ 15 ]

187 голосов
/ 21 января 2010

В JavaScript у вас нет классов , но вы можете получить наследование и повторное использование поведения разными способами:

Псевдоклассическое наследование (через прототипирование):

function Super () {
  this.member1 = 'superMember1';
}
Super.prototype.member2 = 'superMember2';

function Sub() {
  this.member3 = 'subMember3';
  //...
}
Sub.prototype = new Super();

Должен использоваться с оператором new:

var subInstance = new Sub();

Применение функции или «построение цепочки»:

function Super () {
  this.member1 = 'superMember1';
  this.member2 = 'superMember2';
}


function Sub() {
  Super.apply(this, arguments);
  this.member3 = 'subMember3';
}

Этот подход также следует использовать с оператором new:

var subInstance = new Sub();

Разница с первым примером состоит в том, что когда мы apply конструктор Super к объекту this внутри Sub, он добавляет свойства, присвоенные this в Super, непосредственно в новом например, например subInstance содержит свойства member1 и member2 напрямую (subInstance.hasOwnProperty('member1') == true;).

В первом примере эти свойства достигаются через цепочку прототипов , они существуют во внутреннем объекте [[Prototype]].

Паразитарное наследование или конструкторы мощности:

function createSuper() {
  var obj = {
    member1: 'superMember1',
    member2: 'superMember2'
  };

  return obj;
}

function createSub() {
  var obj = createSuper();
  obj.member3 = 'subMember3';
  return obj;
}

Этот подход основан в основном на "расширении объектов", вам не нужно использовать оператор new, и, как вы можете видеть, ключевое слово this не задействовано.

var subInstance = createSub();

ECMAScript 5-е изд. Object.create метод:

// Check if native implementation available
if (typeof Object.create !== 'function') {
  Object.create = function (o) {
    function F() {}  // empty constructor
    F.prototype = o; // set base object as prototype
    return new F();  // return empty object with right [[Prototype]]
  };
}

var superInstance = {
  member1: 'superMember1',
  member2: 'superMember2'
};

var subInstance = Object.create(superInstance);
subInstance.member3 = 'subMember3';

Вышеуказанный метод является прототипным методом наследования, предложенным Крокфордом .

Экземпляры объектов наследуются от других экземпляров объектов, вот и все.

Этот метод может быть лучше простого "дополнения объекта", поскольку унаследованные свойства не копируются во все новые экземпляры объекта, поскольку объект base устанавливается как [[Prototype]] расширенный объект, в приведенном выше примере subInstance физически содержит только свойство member3.

78 голосов
/ 21 января 2010

Я изменил то, как я это делаю сейчас, я стараюсь избегать использования функций конструктора и их свойства prototype, но мой старый ответ от 2010 года все еще находится внизу. Теперь я предпочитаю Object.create(). Object.create доступно во всех современных браузерах.

Следует отметить, что Object.create обычно намного медленнее , чем при использовании new с конструктором функции.

//The prototype is just an object when you use `Object.create()`
var Base = {};

//This is how you create an instance:
var baseInstance = Object.create(Base);

//If you want to inherit from "Base":
var subInstance = Object.create(Object.create(Base));

//Detect if subInstance is an instance of Base:
console.log(Base.isPrototypeOf(subInstance)); //True

jsfiddle

Одним из больших преимуществ использования Object.create является возможность передавать аргумент defineProperties , который дает вам значительный контроль над доступом и перечислением свойств класса, а также используйте функции для создания экземпляров, они служат в качестве конструкторов, так как вы можете выполнить инициализацию в конце, а не просто возвращать экземпляр.

var Base = {};

function createBase() {
  return Object.create(Base, {
    doSomething: {
       value: function () {
         console.log("Doing something");
       },
    },
  });
}

var Sub = createBase();

function createSub() {
  return Object.create(Sub, {
    doSomethingElse: {
      value: function () {
        console.log("Doing something else");
      },
    },
  }); 
}

var subInstance = createSub();
subInstance.doSomething(); //Logs "Doing something"
subInstance.doSomethingElse(); //Logs "Doing something else"
console.log(Base.isPrototypeOf(subInstance)); //Logs "true"
console.log(Sub.isPrototypeOf(subInstance)); //Logs "true

jsfiddle

Это мой оригинальный ответ от 2010 года:

function Base ( ) {
  this.color = "blue";
}

function Sub ( ) {

}
Sub.prototype = new Base( );
Sub.prototype.showColor = function ( ) {
 console.log( this.color );
}

var instance = new Sub ( );
instance.showColor( ); //"blue"
44 голосов
/ 25 февраля 2016

Для тех, кто попадает на эту страницу в 2018 году или после

С последней версией стандарта ECMAScript (ES6) вы можете использовать де-ключ class

Обратите внимание, что определение класса не является обычным object, следовательно, между членами класса нет запятых. Чтобы создать экземпляр класса, вы должны использовать ключевое слово new. Чтобы наследовать от базового класса, используйте extends:

class Vehicle {
   constructor(name) {
      this.name = name;
      this.kind = 'vehicle';
   }
   getName() {
      return this.name;
   }   
}

// Create an instance
var myVehicle = new Vehicle('rocky');
myVehicle.getName(); // => 'rocky'

Чтобы наследовать от базового класса, используйте extends:

class Car extends Vehicle {
   constructor(name) {
      super(name);
      this.kind = 'car'
   }
}

var myCar = new Car('bumpy');

myCar.getName(); // => 'bumpy'
myCar instanceof Car; // => true
myCar instanceof Vehicle; // => true

Из производного класса вы можете использовать super из любого конструктора или метода для доступа к его базовому классу:

  • Чтобы вызвать родительский конструктор, используйте super().
  • Чтобы позвонить другому участнику, используйте, например, super.getName().

Существует больше возможностей для использования классов. Если вы хотите углубиться в тему, я рекомендую « Классы в ECMAScript 6 » доктора Акселя Раушмайера. *

источник

7 голосов
/ 21 января 2010

Ну, в JavaScript нет «наследования классов», есть просто «наследование прототипов». Таким образом, вы не делаете класс «грузовик», а затем помечаете его как подкласс «автомобиль». Вместо этого вы делаете объект «Джек» и говорите, что он использует «Джон» в качестве прототипа. Если Джон знает, сколько «4 + 4», значит, Джек тоже это знает.

Я предлагаю вам прочитать статью Дугласа Крокфорда о наследовании прототипов здесь: http://javascript.crockford.com/prototypal.html Он также показывает, как можно сделать так, чтобы JavaScript имел «похожее» наследование, как в других языках OO, а затем объясняет, что это на самом деле означает нарушение javaScript. таким образом, он не предназначен для использования.

6 голосов
/ 07 августа 2014

Я считаю эту цитату наиболее поучительной:

По сути, JavaScript "класс" - это просто объект Function, который служит конструктором плюс присоединенный объект-прототип. ( Источник: Гуру Кац )

Мне нравится использовать конструкторы, а не объекты, поэтому я неравнодушен к методу "псевдоклассического наследования" , описанному здесь CMS . Вот пример множественного наследования с цепочкой прототипов :

// Lifeform "Class" (Constructor function, No prototype)
function Lifeform () {
    this.isLifeform = true;
}

// Animal "Class" (Constructor function + prototype for inheritance)
function Animal () {
    this.isAnimal = true;
}
Animal.prototype = new Lifeform();

// Mammal "Class" (Constructor function + prototype for inheritance)
function Mammal () {
    this.isMammal = true;
}
Mammal.prototype = new Animal();

// Cat "Class" (Constructor function + prototype for inheritance)
function Cat (species) {
    this.isCat = true;
    this.species = species
}
Cat.prototype = new Mammal();

// Make an instance object of the Cat "Class"
var tiger = new Cat("tiger");

console.log(tiger);
// The console outputs a Cat object with all the properties from all "classes"

console.log(tiger.isCat, tiger.isMammal, tiger.isAnimal, tiger.isLifeform);
// Outputs: true true true true

// You can see that all of these "is" properties are available in this object
// We can check to see which properties are really part of the instance object
console.log( "tiger hasOwnProperty: "
    ,tiger.hasOwnProperty("isLifeform") // false
    ,tiger.hasOwnProperty("isAnimal")   // false
    ,tiger.hasOwnProperty("isMammal")   // false
    ,tiger.hasOwnProperty("isCat")      // true
);

// New properties can be added to the prototypes of any
// of the "classes" above and they will be usable by the instance
Lifeform.prototype.A    = 1;
Animal.prototype.B      = 2;
Mammal.prototype.C      = 3;
Cat.prototype.D         = 4;

console.log(tiger.A, tiger.B, tiger.C, tiger.D);
// Console outputs: 1 2 3 4

// Look at the instance object again
console.log(tiger);
// You'll see it now has the "D" property
// The others are accessible but not visible (console issue?)
// In the Chrome console you should be able to drill down the __proto__ chain
// You can also look down the proto chain with Object.getPrototypeOf
// (Equivalent to tiger.__proto__)
console.log( Object.getPrototypeOf(tiger) );  // Mammal 
console.log( Object.getPrototypeOf(Object.getPrototypeOf(tiger)) ); // Animal
// Etc. to get to Lifeform

Вот еще один хороший ресурс из MDN , а вот jsfiddle, так что вы можете попробовать его .

4 голосов
/ 06 июня 2015

Наследование Javascript немного отличается от Java и PHP, потому что у него нет классов. Вместо этого он имеет объекты-прототипы, которые предоставляют методы и переменные-члены. Вы можете объединить эти прототипы в цепочку, чтобы обеспечить наследование объектов. Наиболее распространенный шаблон, который я обнаружил при исследовании этого вопроса, описан в Mozilla Developer Network . Я обновил их пример, чтобы включить вызов метода суперкласса и показать журнал в сообщении с предупреждением:

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  log += 'Shape moved.\n';
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

// Override method
Rectangle.prototype.move = function(x, y) {
  Shape.prototype.move.call(this, x, y); // call superclass method
  log += 'Rectangle moved.\n';
}

var log = "";
var rect = new Rectangle();

log += ('Is rect an instance of Rectangle? ' + (rect instanceof Rectangle) + '\n'); // true
log += ('Is rect an instance of Shape? ' + (rect instanceof Shape) + '\n'); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
alert(log);

Лично я нахожу наследование в Javascript неловким, но это лучшая версия, которую я нашел.

3 голосов
/ 21 января 2010

вы не можете (в классическом смысле). Javascript - это прототип языка. Вы заметите, что вы никогда не объявляете «класс» в Javascript; вы просто определяете состояние и методы объекта. Чтобы произвести наследование, вы берете какой-то объект и создаете его прототип. Прототип расширен с новой функциональностью.

1 голос
/ 29 августа 2018

ES6 классы:

Javascript не имеет классов. Классы в javascript - это просто синтаксическая сахарная сборка поверх шаблона наследования прототипа , который есть в javascript. Вы можете использовать JS class для обеспечения наследования прототипов, но важно понимать, что вы все еще используете функции конструктора под капотом.

Эти понятия также применяются, когда вы расширяете класс es6 с помощью ключевого слова extends. Это просто создает дополнительную ссылку в цепочке прототипов. __proto__

Пример:

class Animal {
  makeSound () {
    console.log('animalSound');
  }
}

class Dog extends Animal {
   makeSound () {
    console.log('Woof');
  }
}


console.log(typeof Dog)  // classes in JS are just constructor functions under the hood

const dog = new Dog();

console.log(dog.__proto__ === Dog.prototype);   
// First link in the prototype chain is Dog.prototype

console.log(dog.__proto__.__proto__ === Animal.prototype);  
// Second link in the prototype chain is Animal.prototype
// The extends keyword places Animal in the prototype chain
// Now Dog 'inherits' the makeSound property from Animal

Object.create ()

Object.create() также способ создания наследования в JS в javascript. Object.create() - это функция, которая создает новый объект и принимает существующий объект в качестве аргумента. Он назначит объект, который был получен в качестве аргумента, свойству __proto__ вновь созданного объекта. Опять же, важно понимать, что мы связаны с прототипной парадигмой наследования, которую воплощает JS.

* +1025 * Пример:

const Dog = {
  fluffy: true,
  bark: () => {
      console.log('woof im a relatively cute dog or something else??');
  }
};

const dog = Object.create(Dog);

dog.bark();
1 голос
/ 28 июня 2017
function Base() {
    this.doSomething = function () {
    }
}

function Sub() {
    Base.call(this); // inherit Base's method(s) to this instance of Sub
}

var sub = new Sub();
sub.doSomething();
1 голос
/ 22 апреля 2016

Начиная с ES2015, именно так вы и делаете наследование в JavaScript

class Sub extends Base {

}
  1. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes
  2. http://exploringjs.com/es6/ch_classes.html
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...