问题
-
Base 类构造函数只被调用一次。
this.prototype = new baseClass;
- 特权方法将跨实例引用相同的
this 对象,因为这些方法是在构造函数中创建的(仅调用一次)。
基于意见的问题
- 如果您打算与不属于您的 JavaScript 一起工作,则应尽量避免修改本机原型(即
Function.prototype)。
解决方案
- 为每个创建的新实例调用构造函数和父构造函数。
- 继承父原型链(不是父类的实例)。
- 在原型链中将创建的实例数与调用构造函数的次数保持 1:1 的比例。
Max 问题的最终解决方案
请特别注意inherits 函数和ParentClass.call(this, title); 行。
要查找的构造函数是 ParentClass 和 ChildClass
/**
* Allows a child constructor to safely inherit the parent constructors prototype chain.
* @type {Function}
* @param {!Function} childConstructor
* @param {!Function} parentConstructor
*/
function inherits(childConstructor, parentConstructor){
var TempConstructor = function(){};
TempConstructor.prototype = parentConstructor.prototype; // Inherit parent prototype chain
childConstructor.prototype = new TempConstructor(); // Create buffer object to prevent assignments directly to parent prototype reference.
childConstructor.prototype.constructor = childConstructor; // Reset the constructor property back the the child constructor
};
//////////////////////////////////////
/** @constructor */
function ParentClass(title) {
this.setTitle(title);
var randId_ = Math.random();
/** @return {number} */
this.getPrivlegedRandId = function()
{return randId_;};
};
/** @return {string} */
ParentClass.prototype.getTitle = function()
{return this.title_;};
/** @param {string} value */
ParentClass.prototype.setTitle = function(value)
{this.title_ = value;};
//////////////////////////////////////
/**
* @constructor
* @param {string} title
* @param {string} name
*/
ChildClass = function (name, title) {
ParentClass.call(this, title); // Call the parent class constructor with the required arguments
this.setName(name);
}
inherits(ChildClass, ParentClass); // Inherit the parent class prototype chain.
/** @return {string} */
ChildClass.prototype.getName = function()
{return this.name_;};
/** @param {string} value */
ChildClass.prototype.setName = function(value)
{this.name_ = value;};
钻进兔子洞
对于那些好奇为什么它有效而不是简单地记住 inherits 函数的人。
如何使用原型链解析属性
当在实例级别找不到属性时,JavaScript 将尝试通过搜索实例构造函数原型链来解决丢失的属性。如果在第一个原型对象中没有找到该属性,它将搜索父原型对象,依此类推,直到Object.prototype。如果在Object.prototype 中找不到,则会抛出错误。
从子构造函数调用父构造函数:尝试#1
// Bad
var ChildConstructor = function(arg1, arg2, arg3){
var that = new ParentConstructor(this, arg1, arg2, arg3);
that.getArg1 = function(){return arg1};
return that;
}
使用new ChildConstructor 创建的任何变量都将返回ParentConstructor 的实例。
ChildConstructor.prototype 将不会被使用。
从子构造函数调用父构造函数:尝试#2
// Good
var ChildConstructor = function(arg1, arg2, arg3){
ParentConstructor.call(this, arg1, arg2, arg3);
}
现在构造函数和父构造函数被适当地调用。但是,只有在构造函数中定义的方法才会存在。不会使用父原型上的属性,因为它们尚未链接到子构造函数原型。
继承父原型:尝试#1
// Bad
ChildConstructor.prototype = new ParentConstructor();
根据是否使用ParentConstructor.call(this),父构造函数将被调用一次或多次调用。
继承父原型尝试#2
// Bad
ChildConstructor.prototype = ParentConstructor.prototype;
虽然这在技术上可行,但对ChildConstructor.prototype 的任何分配也将分配给ParentConstructor.prototype,因为对象是通过引用而不是通过复制传递的。
继承父原型尝试#3
// Almost there
var TempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = new TempConstructor();
这允许您将属性分配给ChildConstructor.prototype,因为它是一个临时匿名函数的实例。
在TempConstructor 的实例上找不到的属性将检查它的原型链以查找该属性,因此您已成功继承父原型。唯一的问题是ChildConstructor.prototype.constructor 现在指向TempConstructor。
继承父原型尝试#4
// Good
var TempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = new TempConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;
齐心协力
var ParentConstructor = function(){
};
var ChildConstructor = function(){
ParentConstructor.call(this)
};
var TempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = new TempConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;
您已成功从父类继承!看看能不能做得更好。
继承函数
function inherits(childConstructor, parentConstructor){
var TempConstructor = function(){};
TempConstructor.prototype = parentConstructor.prototype; // Inherit parent prototype chain
childConstructor.prototype = new TempConstructor(); // Create buffer object to prevent assignments directly to parent prototype reference.
childConstructor.prototype.constructor = childConstructor; // Reset the constructor property back the the child constructor (currently set to TempConstructor )
};
var ParentConstructor = function(){
};
var ChildConstructor = function(){
ParentConstructor.call(this)
};
inherits(ChildConstructor, ParentConstructor);