如何从javascript中的类继承?

时间:2010-01-21 07:29:41

标签: javascript oop

在PHP / Java中可以做到:

class Sub extends Base
{
}

自动Super类的所有公共/受保护方法,属性,字段等都成为Sub类的一部分,必要时可以覆盖它。

Javascript中的等价物是什么?

15 个答案:

答案 0 :(得分:187)

在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直接包含属性member2subInstance.hasOwnProperty('member1') == true;[[Prototype]])。

在第一个示例中,这些属性是通过原型链到达的,它们存在于内部function createSuper() { var obj = { member1: 'superMember1', member2: 'superMember2' }; return obj; } function createSub() { var obj = createSuper(); obj.member3 = 'subMember3'; return obj; } 对象上。

寄生继承或Power Constructors:

new

此方法基本上基于“对象扩充”,您无需使用this运算符,如您所见,var subInstance = createSub(); 关键字不参与。

Object.create

ECMAScript 5th Ed。 // 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'; 方法:

[[Prototype]]

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

对象实例继承自其他对象实例,就是它。

这种技术可以比简单的“对象扩充”更好,因为继承的属性不会复制到所有新的对象实例上,因为 base 对象被设置为subInstance扩展对象,在上面的示例member3中仅包含{{1}}属性。

答案 1 :(得分:78)

我现在已经改变了我的方式,我尽量避免使用构造函数和它们的prototype属性,但是我在2010年的旧答案仍然在底部。我现在更喜欢Object.create()。所有现代浏览器都提供Object.create

我应该注意Object.create通常much slower比使用函数构造函数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"

答案 2 :(得分:44)

对于那些在2018年或之后

到达此页面的人

使用最新版本的ECMAScript标准(ES6),您可以使用de keywork 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()

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

source

答案 3 :(得分:7)

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

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

答案 4 :(得分:6)

我发现这句话是最具启发性的:

  

本质上,JavaScript “class” 只是一个Function对象,用作构造函数和附加的原型对象。 (Source: Guru Katz

我喜欢使用构造函数而不是对象,所以我偏向于“伪经典继承”方法described here by 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

以下是another good resource from MDN,此处为a jsfiddle so you can try it out

答案 5 :(得分:4)

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中的继承很尴尬,但这是我发现的最好的版本。

答案 6 :(得分:3)

你不能(在经典意义上)。 Javascript是一种原型语言。您将观察到您从未在Javascript中声明“类”;你只需要定义一个对象的状态和方法。为了产生继承,你需要一些对象并对其进行原型化。原型扩展了新功能。

答案 7 :(得分:1)

您可以使用.inheritWith.fastClass library。它比大多数流行的库更快,有时甚至比原生版本更快。

非常容易使用:

function Super() {
   this.member1 = "superMember";//instance member
}.define({ //define methods on Super's prototype
   method1: function() { console.log('super'); } //prototype member
}.defineStatic({ //define static methods directly on Super function 
   staticMethod1: function() { console.log('static method on Super'); }
});

var Sub = Super.inheritWith(function(base, baseCtor) {
   return {
      constructor: function() {//the Sub constructor that will be returned to variable Sub
         this.member3 = 'subMember3'; //instance member on Sub
         baseCtor.apply(this, arguments);//call base construcor and passing all incoming arguments
      },
      method1: function() { 
         console.log('sub'); 
         base.method1.apply(this, arguments); //call the base class' method1 function
      }
}

用法

var s = new Sub();
s.method1(); //prints:
//sub 
//super

答案 8 :(得分:1)

function Person(attr){
  this.name = (attr && attr.name)? attr.name : undefined;
  this.birthYear = (attr && attr.birthYear)? attr.birthYear : undefined;

  this.printName = function(){
    console.log(this.name);
  }
  this.printBirthYear = function(){
    console.log(this.birthYear);
  }
  this.print = function(){
    console.log(this.name + '(' +this.birthYear+ ')');
  }
}

function PersonExt(attr){
  Person.call(this, attr);

  this.print = function(){
    console.log(this.name+ '-' + this.birthYear);
  }
  this.newPrint = function(){
    console.log('New method');
  }
}
PersonExt.prototype = new Person();

// Init object and call methods
var p = new Person({name: 'Mr. A', birthYear: 2007});
// Parent method
p.print() // Mr. A(2007)
p.printName() // Mr. A

var pExt = new PersonExt({name: 'Mr. A', birthYear: 2007});
// Overwriten method
pExt.print() // Mr. A-2007
// Extended method
pExt.newPrint() // New method
// Parent method
pExt.printName() // Mr. A

答案 9 :(得分:1)

阅读了很多帖子之后,我想出了这个解决方案(jsfiddle here)。大多数时候我不需要更复杂的东西

var Class = function(definition) {
    var base = definition.extend || null;
    var construct = definition.construct || definition.extend || function() {};

    var newClass = function() { 
        this._base_ = base;        
        construct.apply(this, arguments);
    }

    if (definition.name) 
        newClass._name_ = definition.name;

    if (definition.extend) {
        var f = function() {}       
        f.prototype = definition.extend.prototype;      
        newClass.prototype = new f();   
        newClass.prototype.constructor = newClass;
        newClass._extend_ = definition.extend;      
        newClass._base_ = definition.extend.prototype;         
    }

    if (definition.statics) 
        for (var n in definition.statics) newClass[n] = definition.statics[n];          

    if (definition.members) 
        for (var n in definition.members) newClass.prototype[n] = definition.members[n];    

    return newClass;
}


var Animal = Class({

    construct: function() {        
    },

    members: {

        speak: function() {
            console.log("nuf said");                        
        },

        isA: function() {        
            return "animal";           
        }        
    }
});


var Dog = Class({  extend: Animal,

    construct: function(name) {  
        this._base_();        
        this.name = name;
    },

    statics: {
        Home: "House",
        Food: "Meat",
        Speak: "Barks"
    },

    members: {
        name: "",

        speak: function() {
            console.log( "ouaf !");         
        },

        isA: function(advice) {
           return advice + " dog -> " + Dog._base_.isA.call(this);           
        }        
    }
});


var Yorkshire = Class({ extend: Dog,

    construct: function(name,gender) {
        this._base_(name);      
        this.gender = gender;
    },

    members: {
        speak: function() {
            console.log( "ouin !");           
        },

        isA: function(advice) {         
           return "yorkshire -> " + Yorkshire._base_.isA.call(this,advice);       
        }        
    }
});


var Bulldog = function() { return _class_ = Class({ extend: Dog,

    construct: function(name) {
        this._base_(name);      
    },

    members: {
        speak: function() {
            console.log( "OUAF !");           
        },

        isA: function(advice) {         
           return "bulldog -> " + _class_._base_.isA.call(this,advice);       
        }        
    }
})}();


var animal = new Animal("Maciste");
console.log(animal.isA());
animal.speak();

var dog = new Dog("Sultan");
console.log(dog.isA("good"));
dog.speak();

var yorkshire = new Yorkshire("Golgoth","Male");
console.log(yorkshire.isA("bad"));
yorkshire.speak();

var bulldog = new Bulldog("Mike");
console.log(bulldog.isA("nice"));
bulldog.speak();

答案 10 :(得分:1)

感谢CMS的回答,并在使用原型和Object.create进行了一段时间的研究之后,我能够使用apply为我的继承提供一个简洁的解决方案,如下所示:

var myNamespace = myNamespace || (function() {
    return {

        BaseClass: function(){
            this.someBaseProperty = "someBaseProperty";
            this.someProperty = "BaseClass";
            this.someFunc = null;
        },

        DerivedClass:function(someFunc){
            myNamespace.BaseClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "DerivedClass";
        },

        MoreDerivedClass:function(someFunc){
            myNamespace.DerivedClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "MoreDerivedClass";
        }
    };
})();

答案 11 :(得分:1)

从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

答案 12 :(得分:1)

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

答案 13 :(得分:1)

ES6类:

Javascript没有类。 javascript中的类仅仅是基于javascript的原型继承模式之上的语法糖。您可以使用JS class来强制执行原型继承,但重要的是要意识到您实际上仍在使用内部构造函数。

当您使用extends关键字从es6'class'扩展时,这些概念也适用。这只是在原型链中创建了一个附加链接。 __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()也是在javascript中的JS中创建继承的方法。 Object.create()是一个函数,它创建一个新对象,一个将现有对象作为参数。它将把作为参数接收的对象分配给新创建的对象的__proto__属性。同样,重要的是要意识到我们已经绑定到JS所体现的原型继承范式。

示例:

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

const dog = Object.create(Dog);

dog.bark();

答案 14 :(得分:0)

您不能从JavaScript继承类,因为JavaScript中没有类。