如何从javascript中的类继承?

IT技术 javascript oop
2021-01-23 21:01:58

在 PHP/Java 中可以做到:

class Sub extends Base
{
}

并且 Super 类的所有公共/受保护的方法、属性、字段等都会自动成为 Sub 类的一部分,如果需要,可以覆盖它们。

Javascript 中的等价物是什么?

6个回答

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

与第一个例子的不同之处在于,当我们applySuper构造函数添加到this对象里面时Sub,它会直接在新实例this添加分配给on的属性Super,例如subInstance包含属性member1member2直接 ( 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';

上述方法是Crockford提出的原型继承技术

对象实例继承自其他对象实例,仅此而已。

这种技术可以比简单的“对象增强”更好,因为继承属性不通过所有复制的新的对象的情况下,由于基体对象被设定为[[Prototype]]所述的扩展的目的,在上面的例子中subInstance物理上只包含member3属性。

不要使用实例进行继承 - 使用 ES5Object.create()或自定义clone()函数(例如 mercurial.intuxication.org/hg/js-hacks/raw-file/tip/clone.js)直接从原型对象继承;有关解释,请参阅stackoverflow.com/questions/1404559/...的评论
2021-03-14 21:01:58
谢谢@Christoph,我正要提到这个Object.create方法:)
2021-03-14 21:01:58
你好@CMS,你能解释一下,为什么我需要在第一个例子中创建父类的实例来设置子类的继承?我说的是这条线:Sub.prototype = new Super();如果在脚本执行期间永远不会使用这两个类怎么办?看起来像性能问题。如果实际上没有使用子类,为什么我需要创建父类?你能详细说明一下吗?这是问题的简单演示:jsfiddle.net/slavafomin/ZeVL2谢谢!
2021-03-20 21:01:58
在所有示例中——除了最后一个——有一个用于 Super 的“类”和一个用于 Sub 的“类”,然后你创建了一个 Sub 的实例。您可以为 Object.create 示例添加一个可比较的示例吗?
2021-03-21 21:01:58
这不是正确的继承,因为您将在 Sub 的原型上拥有 Super 的实例成员。因此,所有 Sub 的实例将共享相同的member1变量,这根本是不可取的。当然他们可以重写它,但这就是没有意义。 github.com/dotnetwise/Javascript-FastClass是一个更好的糖解决方案。
2021-03-25 21:01:58

我现在改变了这样做的方式,我尽量避免使用构造函数及其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

提琴手

使用 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

提琴手

这是我 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"
如果我alert()用来查看instance.showColor()返回值,我仍然会得到undefined. jsbin.com/uqalin/1
2021-03-19 21:01:58
sub.prototype.constructor 值怎么样?我认为它也应该设置为子值。
2021-03-23 21:01:58
除了您使用保留关键字('super')作为类名之外,我无法让您的示例运行:jsbin.com/ixiyet/8/edit
2021-03-31 21:01:58
@MONsDaR 那是因为它控制台日志,它不返回任何提示显示。你在 showColor 中看到 return 语句了吗?
2021-04-01 21:01:58
@MonsDaR 我将它重命名为 Base
2021-04-08 21:01:58

对于那些在 2019 年或之后访问此页面的人

在最新版本的 ECMAScript 标准(ES6) 中,您可以使用关键字class

请注意,类定义不是常规的object因此class成员之间没有逗号。要创建类的实例,您必须使用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()

还有更多使用类。如果你想深入研究这个主题,我推荐Axel Rauschmayer 博士的“ ECMAScript 6 中的类”。*

来源

仅供参考 'instance.name' 此处的 'mycar.name' 将返回类的名称。这是 ES6 和 ESnext 的默认行为。这里 mycar.name 将返回 'Vehicle'
2021-03-20 21:01:58
在内部,class并且extends是(超有用)语法糖为原型链:stackoverflow.com/a/23877420/895245
2021-03-25 21:01:58

好吧,在 JavaScript 中没有“类继承”,只有“原型继承”。因此,您不会创建“卡车”类,然后将其标记为“汽车”的子类。相反,您创建了一个对象“Jack”并说它使用“John”作为原型。如果约翰知道“4+4”是多少,那么杰克也知道。

我建议你在这里阅读 Douglas Crockford 关于原型继承的文章:http : //javascript.crockford.com/prototypal.html他还展示了如何让 JavaScript 像其他 OO 语言一样具有“相似”的继承,然后解释这一点实际上意味着以不打算使用的方式破坏 javaScript。

让我们假设 Jack 的原型是 John。在运行时,我向 John 添加了一个属性/行为。我会从杰克那里得到那个财产/行为吗?
2021-03-20 21:01:58
你肯定会的。例如,这就是人们通常向所有字符串对象添加“trim()”方法的方式(它不是内置的)请参见此处的示例:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ …
2021-04-08 21:01:58

我觉得这句话最有启发性:

本质上,一个 JavaScript 的“类”只是一个 Function 对象,它作为一个构造函数加上一个附加的原型对象。来源:Guru Katz

我喜欢使用构造函数而不是对象,所以我偏爱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,因此您可以尝试一下