JavaScript覆盖方法


87

假设您有以下代码:

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与在方法AB。在Java中,您将使用super-keyword,但是如何在JavaScript中实现类似的功能?


6
modify不是方法而是一个嵌套函数-有这两个之间的区别...
森那维达斯

2
在Java中,您可以使用super关键字来访问超类的非私有字段和方法。您不会使用它来覆盖它们。
FK82

Answers:


138

编辑:原始答案已经写了六年了,很多变化了!

  • 如果您使用的是JavaScript的较新版本(可能已通过Babel这样的工具进行了编译),则可以使用real classes
  • 如果您使用的是AngularReact提供的类类组件构造函数,则需要查看该框架的文档。
  • 如果您使用的是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"

不幸的是,这有点丑陋,并且没有一种非常好的“超级”方法:您必须手动指定要调用的父类的方法。结果,有各种各样的工具可以使创建类更好。尝试查看Prototype.js,Backbone.js或类似的库,其中包含用于在js中进行OOP的更好语法。


2
替代使用工具“使创建类更好”的方法,您根本无法创建类。js中的经典OO仿真总是很混乱。
雷诺斯

1
(不是建设性的评论)毫无根据地成为浏览器世界中的“低级”语言。还在学习,谢谢!
dnuske

9
我相信不是Car.prototype = new Vehicle(); 这应该是Car.prototype = Object.create(Vehicle.prototype); 没有?
乔丹

@Martin是正确的,请参见javascript继承
Matoni

Car.prototype = new Vehicle();的原因之一;之所以不正确,是因为它在接收颜色时没有任何东西可以传递给Vehicle()。
Tushar Arora

62

由于这是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兼容性表仅显示(主要)主要播放器支持类的最新版本。从今年1月开始,V8浏览器就已经安装了它们(Chrome和Opera),而使用SpiderMonkey JS引擎的Firefox将在下个月正式发布Firefox 45时上课。在移动端,Android仍不支持此功能,而五个月前发布的iOS 9已部分支持。

幸运的是,这里有Babel,这是一个JS库,用于将Harmony代码重新编译为ES5代码。ES6中的类和许多其他很酷的功能可以使您的Javascript代码更具可读性和可维护性。


10
此答案值得更多功劳,目前是正确的答案。
Klompenrunner

6

应该避免仿效经典OO而改用原型OO。traits是一个很好的原型OO工具库。

而不是覆盖方法并建立继承链(应该始终主张对象组合胜于对象继承),您应该将可重用函数捆绑为特征,并使用这些特征创建对象。

现场例子

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

10
您没有回答这个问题。如果有的话,这应该是一个评论。
FK82


3

您的示例中的Modify()是私有函数,只能在A,B或C定义内的任何位置访问。您需要将其声明为

this.modify = function(){}

C没有引用其父级,除非将其传递给C。如果将C设置为继承自A或B,它将继承其公共方法(而不是像您定义了Modify()那样的私有函数)。一旦C从其父级继承了方法,就可以覆盖继承的方法。


Modify是本地函数。javascript中没有任何私有
语言-Raynos

本地/私有,不是同一件事,只是一个不同的术语?
Alex Heyd

2

modify()如果要覆盖,最后一次调用的方法将在全局上下文中调用,modify()您首先必须继承AB

也许您正在尝试这样做:

在这种情况下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
C.prototype.modify()会有错误的this值。即C.prototype代替c的实例。请使用,.call(this)但您的答案只是一个重复:)
Raynos

1

除非您将所有变量都设置为“ public”,否则就不要这么做,即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 ;

无论AB现在有个别修改成员xy这不会是我们是否有书面的情况... = C相反。

然后,xymodify都是“公共”的成员,这样分配不同的Function他们

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

将以Function该名称“覆盖”原始文件。

最后,对的调用modify无法在Function声明中完成,因为当我们将假定的“超类”设置为假定的“子类”的prototype属性时,将再次执行对“超类”的隐式调用。

但是,好吧,这或多或少就是您在JavaScript中执行此类操作的方式。

HTH,

FK


<name>.prototype = new C;无论如何,您在掩盖原型的所有成员上没有意义
Raynos

@Raynos:是的。也就是说,如果不实例化对象,则所有继承Objects都将共享同一个成员。因此,改变在将改变在并因此改变在。这显然是不希望的。CCxAxCxB
FK82

你错过了重点。您可以删除该行,并且代码仍将起作用
Raynos

@Raynos:恐怕您正在失去自己的观点。;-)我们希望AB为继承C。如果该行丢失,则不是这种情况。实际上,在这种情况下,唯一的原型A以及B“影子”都可以使用Object.prototype
FK82

看代码。A和B在原型中不使用C的任何成员。因此,从C进行“继承”是没有用的。这是因为A和B重新定义xymodify且因此阴影所有的C的成员。如果不使用C,将C放入原型有什么意义?这是死代码。
雷诺斯

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.