我已经使用面向对象编程语言编程超过十年,但现在我正在学习JavaScript,这是我第一次遇到基于原型的继承。我倾向于通过研究好的代码来快速学习。有没有一个很好的JavaScript应用程序(或库)的例子,它正确地使用了原型继承?你能简要描述一下原型继承是如何/在哪里使用的吗,这样我就知道从哪里开始阅读了?
我已经使用面向对象编程语言编程超过十年,但现在我正在学习JavaScript,这是我第一次遇到基于原型的继承。我倾向于通过研究好的代码来快速学习。有没有一个很好的JavaScript应用程序(或库)的例子,它正确地使用了原型继承?你能简要描述一下原型继承是如何/在哪里使用的吗,这样我就知道从哪里开始阅读了?
如前所述,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'
);
然而,这种方法的问题在于每次创建对象时都会重新创建该对象。另一种方法是将对象声明在原型堆栈上,如下所示:
// 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在回复中提到的。
testOne
函数中,privateVariable
只是 IIFE 作用域内的一个变量,并在所有实例之间共享,因此您不应该将实例特定的数据存储在其中。(在 testTwo
中,它是实例特定的,因为每次调用 testTwo()
都会创建一个新的、针对每个实例的作用域。) - shesekDog.prototype
上定义方法来缓解这个问题。因此,我们可以在Dog
函数之外使用Dot.prototype.bark = function () {...}
而不是使用this.bark = function () {...}
。(有关更多详细信息,请参见此答案) - Huang C.五年前,我写了JavaScript中的经典继承。它展示了JavaScript是一种无类、基于原型的语言,并且它具有足够的表达能力来模拟一个经典系统。我的编程风格自那时以来已经进化,就像任何好的程序员一样。我已经学会了完全接受原型主义,并从经典模型的限制中解放了自己。
迪恩·爱德华兹的Base.js、Mootools的Class或约翰·雷西格的简单继承是在JavaScript中进行经典继承的方式。
newObj = Object.create(oldObj);
呢?否则,将 oldObj
替换为构造函数的原型对象应该也可以工作。 - Cykerfunction 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);
Circle.prototype.constructor
属性,它将指向错误的东西(Shape
而不是Circle
)。最后添加:Circle.prototype.constructor = Circle;
- T.J. Crowder我建议你查看YUI和Dean Edward的Base
库:http://dean.edwards.name/weblog/2006/03/base/
关于YUI,你可以快速查看lang模块,尤其是YAHOO.lang.extend
方法。然后,你可以浏览一些小部件或实用程序的源代码,看看它们如何使用该方法。
这是我发现的最清晰的例子,来自Mixu的Node书(http://book.mixu.net/node/ch6.html):
I favor composition over inheritance:
Composition - Functionality of an object is made up of an aggregate of different classes by containing instances of other objects. Inheritance - Functionality of an object is made up of it's own functionality plus functionality from its parent classes. If you must have inheritance, use plain old JS
If you must implement inheritance, at least avoid using yet another nonstandard implementation / magic function. Here is how you can implement a reasonable facsimile of inheritance in pure ES3 (as long as you follow the rule of never defining properties on prototypes):
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();
This is not the same thing as classical inheritance - but it is standard, understandable Javascript and has the functionality that people mostly seek: chainable constructors and the ability to call methods of the superclass.
ES6 class
和extends
ES6 class
和extends
只是为先前可能的原型链操作提供语法糖,因此可以说是最经典的设置。
首先要了解有关原型链和 .
属性查找的更多信息,请访问:https://dev59.com/a3RB5IYBdhLWcg3wn4UL#23877420
现在让我们来分解发生的事情:
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://dev59.com/YGEh5IYBdhLWcg3wVCPK
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined
简化的图示,没有所有预定义的对象:
__proto__
(C)<---------------(D) (d)
| | | |
| | | |
| |prototype |prototype |__proto__
| | | |
| | | |
| | | +---------+
| | | |
| | | |
| | v v
|__proto__ (D.prototype)
| | |
| | |
| | |__proto__
| | |
| | |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
我建议看一下PrototypeJS的Class.create:
第83行 @ http://prototypejs.org/assets/2009/8/31/prototype.js
我见过的最好的例子在Douglas Crockford的JavaScript: The Good Parts中。它绝对值得购买,以帮助您获得对该语言的平衡看法。
Douglas Crockford负责JSON格式,并在雅虎担任JavaScript大师。