JavaScript基于原型的inheritance的好例子

我已经使用OOP语言进行了10多年的编程,但现在我正在学习JavaScript,这是我第一次遇到基于原型的inheritance。 我倾向于通过学习良好的代码学习最快。 什么是正确使用原型inheritance的JavaScript应用程序(或库)的写得好的例子? 你可以描述(简要)如何/在哪里使用原型inheritance,所以我知道从哪里开始阅读?

Douglas Crockford在JavaScript上有一个不错的页面Prototypal Inheritance :

五年前,我用JavaScript写了古典inheritance 。 它表明JavaScript是一个无阶级的原型语言,它具有足够的performance力来模拟一个经典的系统。 从那以后,我的编程风格就有了进步,就像任何一个好的程序员都应该这样。 我已经学会了完全接受原型,并把自己从经典模型的范围中解放出来。

院长爱德华的Base.js , Mootools的类或约翰Resig的简单的inheritance工作是在JavaScript 经典inheritance的方法。

如上所述,道格拉斯·克罗克福德(Douglas Crockford)的电影对这个原因做了很好的解释,并且涵盖了如何做。 但要把它放在几行JavaScript中:

 // Declaring our Animal object var Animal = function () { this.name = 'unknown'; this.getName = function () { return this.name; } return this; }; // Declaring our Dog object var Dog = function () { // A private variable here var private = 42; // overriding the name this.name = "Bello"; // Implementing ".bark()" this.bark = function () { return 'MEOW'; } return this; }; // Dog extends animal Dog.prototype = new Animal(); // -- Done declaring -- // Creating an instance of Dog. var dog = new Dog(); // Proving our case console.log( "Is dog an instance of Dog? ", dog instanceof Dog, "\n", "Is dog an instance of Animal? ", dog instanceof Animal, "\n", dog.bark() +"\n", // Should be: "MEOW" dog.getName() +"\n", // Should be: "Bello" dog.private +"\n" // Should be: 'undefined' ); 

但是,这种方法的问题在于,每次创build对象时都会重新创build对象。 另一种方法是在原型堆栈上声明对象,如下所示:

 // Defining test one, prototypal var testOne = function () {}; testOne.prototype = (function () { var me = {}, privateVariable = 42; me.someMethod = function () { return privateVariable; }; me.publicVariable = "foo bar"; me.anotherMethod = function () { return this.publicVariable; }; return me; }()); // Defining test two, function var testTwo = ​function() { var me = {}, privateVariable = 42; me.someMethod = function () { return privateVariable; }; me.publicVariable = "foo bar"; me.anotherMethod = function () { return this.publicVariable; }; return me; }; // Proving that both techniques are functionally identical var resultTestOne = new testOne(), resultTestTwo = new testTwo(); console.log( resultTestOne.someMethod(), // Should print 42 resultTestOne.publicVariable // Should print "foo bar" ); console.log( resultTestTwo.someMethod(), // Should print 42 resultTestTwo.publicVariable // Should print "foo bar" ); // Performance benchmark start var stop, start, loopCount = 1000000; // Running testOne start = (new Date()).getTime(); for (var i = loopCount; i>0; i--) { new testOne(); } stop = (new Date()).getTime(); console.log('Test one took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds'); // Running testTwo start = (new Date()).getTime(); for (var i = loopCount; i>0; i--) { new testTwo(); } stop = (new Date()).getTime(); console.log('Test two took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds'); 

内省时有一个小小的缺点。 倾倒testOne会导致不太有用的信息。 此外,“testOne”中的私有属性“privateVariable”在所有情况下都被共享,在shesek的回复中也有所帮助。

 function Shape(x, y) { this.x = x; this.y = y; } // 1. Explicitly call base (Shape) constructor from subclass (Circle) constructor passing this as the explicit receiver function Circle(x, y, r) { Shape.call(this, x, y); this.r = r; } // 2. Use Object.create to construct the subclass prototype object to avoid calling the base constructor Circle.prototype = Object.create(Shape.prototype); 

我会看看YUI和Dean Edward的Base图书馆: http : //dean.edwards.name/weblog/2006/03/base/

对于YUI,您可以快速浏览lang模块 ,尤其是 YAHOO.lang.extend方法。 然后,您可以浏览一些小部件或实用程序的源代码,并查看它们如何使用该方法。

还有微软的ASP.NET Ajax库, http://www.asp.net/ajax/ 。

还有很多很好的MSDN文章,包括使用面向对象技术创build高级Web应用程序

这是我从Mixu的Node书中find的最清晰的例子( http://book.mixu.net/node/ch6.html ):

我赞成inheritance的构成:

构图 – 对象的function由包含其他对象的实例的不同类的聚合构成。 inheritance – 对象的function是由它自己的function加上其父类的function组成的。 如果你必须有inheritance,使用普通的老JS

如果你必须实现inheritance,至less要避免使用另一个非标准的实现/魔术function。 这里是你如何在纯ES3中实现一个合理的inheritance传真(只要你遵循从不定义原型的属性的规则):

 function Animal(name) { this.name = name; }; Animal.prototype.move = function(meters) { console.log(this.name+" moved "+meters+"m."); }; function Snake() { Animal.apply(this, Array.prototype.slice.call(arguments)); }; Snake.prototype = new Animal(); Snake.prototype.move = function() { console.log("Slithering..."); Animal.prototype.move.call(this, 5); }; var sam = new Snake("Sammy the Python"); sam.move(); 

这与经典inheritance不同,但它是标准的,可理解的Javascript,并具有人们主要寻求的function:可链式构造函数和调用超类方法的能力。

我build议看看PrototypeJS的Class.create:
83行@ assets/2009/8/31/prototype.js

ES6 classextends

ES6 classextends只是以前可能的原型链操作的语法糖,所以可以说是最经典的设置。

首先了解更多关于原型链和. 属性查找在: https : //stackoverflow.com/a/23877420/895245

现在让我们解构发生的事情:

 class C { constructor(i) { this.i = i } inc() { return this.i + 1 } } class D extends C { constructor(i) { super(i) } inc2() { return this.i + 2 } } 
 // Inheritance syntax works as expected. (new C(1)).inc() === 2 (new D(1)).inc() === 2 (new D(1)).inc2() === 3 
 // "Classes" are just function objects. C.constructor === Function C.__proto__ === Function.prototype D.constructor === Function // D is a function "indirectly" through the chain. D.__proto__ === C D.__proto__.__proto__ === Function.prototype 
 // "extends" sets up the prototype chain so that base class // lookups will work as expected var d = new D(1) d.__proto__ === D.prototype D.prototype.__proto__ === C.prototype // This is what `d.inc` actually does. d.__proto__.__proto__.inc === C.prototype.inc 
 // Class variables // No ES6 syntax sugar apparently: // https://stackoverflow.com/questions/22528967/es6-class-variable-alternatives Cc = 1 Cc === 1 // Because `D.__proto__ === C`. Dc === 1 // Nothing makes this work. dc === undefined 

没有所有预定义对象的简化图:

  __proto__ (C)<---------------(D) (d) | | | | | | | | | |prototype |prototype |__proto__ | | | | | | | | | | | +---------+ | | | | | | | | | | vv |__proto__ (D.prototype) | | | | | | | | |__proto__ | | | | | | | | +--------------+ | | | | | | | vv | (C.prototype)--->(inc) | v Function.prototype 

我见过的最好的例子是Douglas Crockford的JavaScript:好的部分 。 绝对值得购买,以帮助您获得对语言的平衡观点。

Douglas Crockford负责JSON格式,并在雅虎担任JavaScript大师。

有一个片段JavaScript基于原型的inheritance与ECMAScript版本特定的实现。 根据当前的运行时间,它将自动select在ES6,ES5和ES3实现之间使用哪一个。