Методы переопределения JavaScript - PullRequest
69 голосов
/ 30 июля 2011

Допустим, у вас есть следующий код:

function A() {
    function modify(){
       x = 300;
       y = 400;
    }
    var c = new C();
}

function B() {
    function modify(){
       x = 3000;
       y = 4000;
    }
    var c = new C();
}


C = function () {
   var x = 10;
   var y = 20;

   function modify() {
      x = 30;
      y = 40;
   };

   modify();
   alert("The sum is: " + (x+y));
}

Теперь вопрос в том, есть ли способ, которым я могу переопределить метод modify из C методами, которые находятся в A иB. В Java вы бы использовали ключевое слово super, но как достичь такого в JavaScript?

Ответы [ 7 ]

124 голосов
/ 30 июля 2011

Редактировать: Прошло шесть лет с момента написания первоначального ответа, и многое изменилось!

  • Если вы используете более новую версию JavaScript, возможно, скомпилированную с помощью такого инструмента, как Babel , вы можете использовать реальные классы .
  • Если вы используете классоподобные конструкторы компонентов, предоставляемые Angular или React , вы захотите заглянуть в документы по этой платформе.
  • Если вы используете ES5 и создаете «фальшивые» классы вручную, используя прототипы, ответ ниже остается верным, как и прежде.

Удачи!


Наследование JavaScript выглядит немного иначе, чем Java. Вот как выглядит нативная объектная система JavaScript:

// Create a class
function Vehicle(color){
  this.color = color;
}

// Add an instance method
Vehicle.prototype.go = function(){
  return "Underway in " + this.color;
}

// Add a second class
function Car(color){
  this.color = color;
}

// And declare it is a subclass of the first
Car.prototype = new Vehicle();

// Override the instance method
Car.prototype.go = function(){
  return Vehicle.prototype.go.call(this) + " car"
}

// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"

var c = new Car("red");
c.go() // "Underway in red car"

К сожалению, это немного уродливо и не включает в себя очень хороший способ «super»: вам нужно вручную указать, какой метод родительских классов вы хотите вызвать. В результате, существует множество инструментов, которые делают создание классов более приятным. Попробуйте поискать Prototype.js, Backbone.js или аналогичную библиотеку, которая содержит более приятный синтаксис для выполнения ООП в js.

35 голосов
/ 02 марта 2016

Поскольку это самый популярный продукт в Google, я бы хотел дать обновленный ответ.

Использование классов ES6 значительно упрощает наследование и переопределение методов:

'use strict';

class A {
    speak() {
        console.log("I'm A");
    }
}

class B extends A {
    speak() {
        super.speak();

        console.log("I'm B");
    }
}

var a = new A();
a.speak();
// Output:
// I'm A

var b = new B();
b.speak();
// Output:
// I'm A
// I'm B

Ключевое слово super относится к родительскому классу при использовании в наследующем классе.Кроме того, все методы родительского класса привязаны к экземпляру дочернего, поэтому вам не нужно писать super.method.apply(this);.

Что касается совместимости: таблица совместимости ES6 показываеттолько самые последние версии основных игроков поддерживают классы (в основном).Браузеры V8 имеют их с января этого года (Chrome и Opera), и Firefox, использующий движок SpiderMonkey JS, увидит классы в следующем месяце с их официальным релизом Firefox 45.Что касается мобильных устройств, Android по-прежнему не поддерживает эту функцию, в то время как iOS 9, выпущенная пять месяцев назад, имеет частичную поддержку.

К счастью, существует Babel , библиотека JS для повторного использования.компиляция кода Harmony в код ES5.Классы и множество других интересных функций в ES6 могут сделать ваш код Javascript намного более читабельным и понятным.

6 голосов
/ 31 июля 2011

Однажды следует избегать эмуляции классического ОО и использовать вместо него прототипный ОО.Хорошая служебная библиотека для прототипа OO: traits .

Вместо того, чтобы переписывать методы и устанавливать цепочки наследования (всегда следует отдавать предпочтение композиции объектов по сравнению с наследованием объектов), вы должны связывать повторно используемые функции.в черты и создание объектов с ними.

Живой пример

var modifyA = {
    modify: function() {
        this.x = 300;
        this.y = 400;
    }
};

var modifyB = {
    modify: function() {
        this.x = 3000;
        this.y = 4000;
    }
};

C = function(trait) {
    var o = Object.create(Object.prototype, Trait(trait));

    o.modify();
    console.log("sum : " + (o.x + o.y));

    return o;
}

//C(modifyA);
C(modifyB);
3 голосов
/ 30 июля 2011

modify () в вашем примере - это закрытая функция, которая не будет доступна нигде, кроме как в пределах вашего определения A, B или C.Вам необходимо объявить его как

this.modify = function(){}

C, не имеющий ссылки на своих родителей, если только вы не передадите его в C. Если C настроен на наследование от A или B, он унаследует свои открытые методы (не его частные функции, такие как у вас есть modify ().Как только C наследует методы от своего родителя, вы можете переопределить унаследованные методы.

2 голосов
/ 30 июля 2011

метод modify(), который вы вызывали в последнем, вызывается в глобальном контексте если вы хотите переопределить modify(), вы сначала должны унаследовать A или B.

Может быть, вы пытаетесь сделать это:

В этом случае C наследует A

function A() {
    this.modify = function() {
        alert("in A");
    }
}

function B() {
    this.modify = function() {
        alert("in B");
    }
}

C = function() {
    this.modify = function() {
        alert("in C");
    };

    C.prototype.modify(); // you can call this method where you need to call modify of the parent class
}

C.prototype = new A();
1 голос
/ 31 июля 2011

Нет, если вы не сделаете все переменные «общедоступными», то есть сделаете их членами Function либо напрямую, либо через свойство prototype.

var C = function( ) {
    this.x = 10 , this.y = 20 ;
    this.modify = function( ) {
        this.x = 30 , this.y = 40 ;
        console.log("(!) C >> " + (this.x + this.y) ) ;
    } ;
} ;

var A = function( ) {
    this.modify = function( ) {
       this.x = 300 , this.y = 400 ;
       console.log("(!) A >> " + (this.x + this.y) ) ;
    } ;
} ;
    A.prototype = new C ;

var B = function( ) {
    this.modify = function( ) {
       this.x = 3000 , this.y = 4000 ;
       console.log("(!) B >> " + (this.x + this.y) ) ;
    } ;
} ;


new C( ).modify( ) ;
new A( ).modify( ) ;
new B( ).modify( ) ; 

Вы заметите несколько изменений.

Наиболее важно, что вызов предполагаемого конструктора "суперклассов" теперь неявно присутствует в этой строке:

<name>.prototype = new C ;

И A, и B теперь будут иметь индивидуально изменяемых членов x и y, что было бы не так, если бы мы написали ... = C.

Тогда x, y и modify все являются "публичными" членами, так что им присваивается другой Function

 <name>.prototype.modify = function( ) { /* ... */ }

"переопределит" оригинал Function с этим именем.

Наконец, вызов modify не может быть выполнен в объявлении Function, потому что неявный вызов "суперкласса" будет выполнен снова, когда мы установим для предполагаемого "суперкласса" prototype свойство предполагаемых «подклассов».

Но это более или менее так, как вы бы поступали в JavaScript.

НТН,

FK

0 голосов
/ 30 сентября 2016

function A() {
    var c = new C();
	c.modify = function(){
		c.x = 123;
		c.y = 333;
	}
	c.sum();
}

function B() {
    var c = new C();
	c.modify = function(){
		c.x = 999;
		c.y = 333;
	}
	c.sum();
}


C = function () {
   this.x = 10;
   this.y = 20;

   this.modify = function() {
      this.x = 30;
      this.y = 40;
   };
   
   this.sum = function(){
	this.modify();
	console.log("The sum is: " + (this.x+this.y));
   }
}

A();
B();
...