2013-03-04 103 views
51

我有一个基类:的JavaScript扩展

function Monster() { 
    this.health = 100; 
} 

Monster.prototype.growl = function() { 
    console.log("Grr!"); 
} 

,我想延长并创建另一个类:

function Monkey extends Monster() { 
    this.bananaCount = 5; 
} 

Monkey.prototype.eatBanana { 
    this.bananaCount--; 
    this.health++; //Accessing variable from parent class monster 
    this.growl(); //Accessing function from parent class monster 
} 

我已经做了相当多的研究和有似乎是用JavaScript做这件事的许多令人费解的解决方案。在JS中完成这个最简单和最可靠的方法是什么?

+0

的[?如何从在JavaScript类继承]可能的复制(http://stackoverflow.com/问题/ 2107556 /如何在javascript中继承类) – 2016-11-20 12:42:26

回答

110

下面更新了ES6

2013年3月和ES5

这MDN文档描述以及扩展类:

https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript

在这里特别地,是现在他们处理:

// define the Person Class 
function Person() {} 

Person.prototype.walk = function(){ 
    alert ('I am walking!'); 
}; 
Person.prototype.sayHello = function(){ 
    alert ('hello'); 
}; 

// define the Student class 
function Student() { 
    // Call the parent constructor 
    Person.call(this); 
} 

// inherit Person 
Student.prototype = Object.create(Person.prototype); 

// correct the constructor pointer because it points to Person 
Student.prototype.constructor = Student; 

// replace the sayHello method 
Student.prototype.sayHello = function(){ 
    alert('hi, I am a student'); 
} 

// add sayGoodBye method 
Student.prototype.sayGoodBye = function(){ 
    alert('goodBye'); 
} 

var student1 = new Student(); 
student1.sayHello(); 
student1.walk(); 
student1.sayGoodBye(); 

// check inheritance 
alert(student1 instanceof Person); // true 
alert(student1 instanceof Student); // true 

注意Object.create()是在一些较老的浏览器,包括IE8不支持:

Object.create browser support

如果你是需要支持这些位置,链接的MDN文件建议使用填充工具,或者下面的近似:

function createObject(proto) { 
    function ctor() { } 
    ctor.prototype = proto; 
    return new ctor(); 
} 

使用这个像Student.prototype = createObject(Person.prototype)是最好的,因为它继承avoids calling the parent's constructor function原型时,只调用父类的构造时继承的构造函数使用new Person()被称为。

2017年5月和ES6

值得庆幸的是,JavaScript的设计师们听到我们的呼救,并都采用了接近这个问题的一个更合适的方式。

MDN对ES6类继承另一个很好的例子,但我会表现出完全相同的一组类如上ES6转载:

class Person { 
    sayHello() { 
     alert('hello'); 
    } 

    walk() { 
     alert('I am walking!'); 
    } 
} 

class Student extends Person { 
    sayGoodBye() { 
     alert('goodBye'); 
    } 

    sayHello() { 
     alert('hi, I am a student'); 
    } 
} 

var student1 = new Student(); 
student1.sayHello(); 
student1.walk(); 
student1.sayGoodBye(); 

// check inheritance 
alert(student1 instanceof Person); // true 
alert(student1 instanceof Student); // true 

清洁和理解,就像我们都希望。请记住,虽然ES6是很常见的,它是not supported everywhere

ES6 browser support

+20

哇,并不是说我有更好的解决方案,但是这种难以理解的巨无霸的神奇咒语是可怕的。 – user949300 2014-02-04 01:49:24

+3

如果Person构造函数需要参数,例如Person(name)...? – 2015-02-19 21:10:05

+2

@PhamHuyAnh只要做一些像'function Person(n){this.name = n; }' – 2015-02-21 01:51:10

9

试试这个:

Function.prototype.extends = function(parent) { 
    this.prototype = Object.create(parent.prototype); 
}; 

Monkey.extends(Monster); 
function Monkey() { 
    Monster.apply(this, arguments); // call super 
} 

编辑:我把一个快速演示在这里http://jsbin.com/anekew/1/edit。请注意,extends是JS中的一个保留字,在编写代码时可能会收到警告,您可以简单地将其命名为inherits,这就是我通常所做的。

有了这个帮手的地方,使用对象props做为唯一的参数,在继承JS变得简单一点:

Function.prototype.inherits = function(parent) { 
    this.prototype = Object.create(parent.prototype); 
}; 

function Monster(props) { 
    this.health = props.health || 100; 
} 

Monster.prototype = { 
    growl: function() { 
    return 'Grrrrr'; 
    } 
}; 

Monkey.inherits(Monster); 
function Monkey() { 
    Monster.apply(this, arguments); 
} 

var monkey = new Monkey({ health: 200 }); 

console.log(monkey.health); //=> 200 
console.log(monkey.growl()); //=> "Grrrr" 
+0

使用这个,Monkey不会继承Monster的属性('health'),你也会得到*“ReferenceError:Monkey没有定义“*如果在调用'Monkey.extends(Monster)之前没有定义Monkey' – Phil 2013-03-04 00:39:16

+0

如果你调用super,检查编辑 – elclanrs 2013-03-04 00:42:31

+0

@Phil,它是一个函数声明它被挂起,它应该工作唯一的”问题“,你会从这段代码中得到的是”extends是一个保留字“,但是你可以很容易地将它改成任何其他的标识符 – elclanrs 2013-03-04 00:44:39

6

如果你不喜欢的原型方法,因为它并没有真正表现在一个不错的OOP方式,你可以试试这个:

var BaseClass = function() 
{ 
    this.some_var = "foobar"; 

    /** 
    * @return string 
    */ 
    this.someMethod = function() { 
     return this.some_var; 
    } 
}; 

var MyClass = new Class({ extends: BaseClass }, function() 
{ 
    /** 
    * @param string value 
    */ 
    this.__construct = function(value) 
    { 
     this.some_var = value; 
    } 
}) 

使用轻量级库(2K缩小的):https://github.com/haroldiedema/joii

+0

谢谢你的链接到这个库!它看起来很神奇:D似乎这就是我一直在寻找的那么久! <3 – x3ro 2015-10-30 13:38:27

1

这是一个elclanrs解决方案的延伸(原谅双关语)包括实例方法的细节,并对问题的这个方面采取可扩展的方法;我完全承认,由于David Flanagan的“JavaScript:权威指南”(部分调整了此背景),这一切都归结在一起。请注意,这显然比其他解决方案更详细,但可能会从长远角度来看。

首先,我们使用大卫的简单的“扩展”功能,副本属性指定的对象:

function extend(o,p) { 
    for (var prop in p) { 
     o[prop] = p[prop]; 
    } 
    return o; 
} 

然后我们实现自己的子类定义工具:

function defineSubclass(superclass,  // Constructor of our superclass 
          constructor, // Constructor of our new subclass 
          methods,  // Instance methods 
          statics) { // Class properties 
     // Set up the prototype object of the subclass 
    constructor.prototype = Object.create(superclass.prototype); 
    constructor.prototype.constructor = constructor; 
    if (methods) extend(constructor.prototype, methods); 
    if (statics) extend(constructor, statics); 
    return constructor; 
} 

对于准备的最后一位我们用David的新jiggery-pokery增强了我们的功能原型:

Function.prototype.extend = function(constructor, methods, statics) { 
    return defineSubclass(this, constructor, methods, statics); 
}; 

定义我们的怪物下课后,我们就以下(这是可以重复使用的,我们希望扩展/继承任何新类):

var Monkey = Monster.extend(
     // constructor 
    function Monkey() { 
     this.bananaCount = 5; 
     Monster.apply(this, arguments); // Superclass() 
    }, 
     // methods added to prototype 
    eatBanana: function() { 
     this.bananaCount--; 
     this.health++; 
     this.growl(); 
    } 
); 
0

对于传统的延伸,你可以简单地写超类构造函数, 和然后将这个构造函数应用于您的继承类。

 function AbstractClass() { 
     this.superclass_method = function(message) { 
      // do something 
     }; 
    } 

    function Child() { 
     AbstractClass.apply(this); 
     // Now Child will have superclass_method() 
    } 

例如在angularjs:

http://plnkr.co/edit/eFixlsgF3nJ1LeWUJKsd?p=preview

app.service('noisyThing', 
    ['notify',function(notify){ 
    this._constructor = function() { 
     this.scream = function(message) { 
      message = message + " by " + this.get_mouth(); 
      notify(message); 
      console.log(message); 
     }; 

     this.get_mouth = function(){ 
     return 'abstract mouth'; 
     } 
    } 
    }]) 
    .service('cat', 
    ['noisyThing', function(noisyThing){ 
    noisyThing._constructor.apply(this) 
    this.meow = function() { 
     this.scream('meooooow'); 
    } 
    this.get_mouth = function(){ 
     return 'fluffy mouth'; 
    } 
    }]) 
    .service('bird', 
    ['noisyThing', function(noisyThing){ 
    noisyThing._constructor.apply(this) 
    this.twit = function() { 
     this.scream('fuuuuuuck'); 
    } 
    }]) 
9

ES6给你使用现在 & 延伸关键字机会:

然后,你的代码会:

你有一个基类:

class Monster{ 
     constructor(){ 
      this.health = 100; 
     } 
     growl() { 
      console.log("Grr!"); 
     } 

} 

要延长,并创建另一个类:

class Monkey extends Monster { 
     constructor(){ 
      super(); //don't forget "super" 
      this.bananaCount = 5; 
     } 


     eatBanana() { 
      this.bananaCount--; 
      this.health++; //Accessing variable from parent class monster 
      this.growl(); //Accessing function from parent class monster 
     } 

} 
+2

这是非常清洁,可以确认工作在铬51.0,Firefox 47. – Reahreic 2016-08-08 19:58:00

+1

使用'try {} catch(e){}'块来管理,并告诉最终用户更新他的浏览器,如果它是必需的。 – 2016-08-09 10:13:10

0

对于自学能力:

function BaseClass(toBePrivate){ 
    var morePrivates; 
    this.isNotPrivate = 'I know'; 
    // add your stuff 
} 
var o = BaseClass.prototype; 
// add your prototype stuff 
o.stuff_is_never_private = 'whatever_except_getter_and_setter'; 


// MiddleClass extends BaseClass 
function MiddleClass(toBePrivate){ 
    BaseClass.call(this); 
    // add your stuff 
    var morePrivates; 
    this.isNotPrivate = 'I know'; 
} 
var o = MiddleClass.prototype = Object.create(BaseClass.prototype); 
MiddleClass.prototype.constructor = MiddleClass; 
// add your prototype stuff 
o.stuff_is_never_private = 'whatever_except_getter_and_setter'; 



// TopClass extends MiddleClass 
function TopClass(toBePrivate){ 
    MiddleClass.call(this); 
    // add your stuff 
    var morePrivates; 
    this.isNotPrivate = 'I know'; 
} 
var o = TopClass.prototype = Object.create(MiddleClass.prototype); 
TopClass.prototype.constructor = TopClass; 
// add your prototype stuff 
o.stuff_is_never_private = 'whatever_except_getter_and_setter'; 


// to be continued... 

创建 “实例” 吸气剂和二维码:

function doNotExtendMe(toBePrivate){ 
    var morePrivates; 
    return { 
     // add getters, setters and any stuff you want 
    } 
} 
0

我可以提出一个变种,只是已经在书中读到,似乎最简单的:

function Parent() { 
    this.name = 'default name'; 
}; 

function Child() { 
    this.address = '11 street'; 
}; 

Child.prototype = new Parent();  // child class inherits from Parent 
Child.prototype.constructor = Child; // constructor alignment 

var a = new Child(); 

console.log(a.name);    // "default name" trying to reach property of inherited class