2013-03-04 100 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