11

我正在创建两个对象(继承),它们都继承自 Base。第二个对象的属性赋值覆盖第一个对象中的值。

有什么想法吗?如何进行适当的继承,以便基类将包含其继承后代的公共成员,但后代可以分配自己的值而不会相互干扰。

var testParams1 = { title: "john" };
var testParams2 = { title: "mike" };

Function.prototype.inheritsFrom = function (baseClass)
{
    this.prototype = new baseClass;
    this.prototype.constructor = this;
    this.prototype.base = baseClass.prototype;
    return this;
};

function Base() {
    this.viewModel = {};
    this.viewModel.title = (function ()
    {
        var savedVal = "";
        return function (newVal)
        {
            if (typeof (newVal) === "undefined")
            {
                return savedVal;
            }
            savedVal = newVal;
        };
    })();
}
function Inherits(params) {
    this.viewModel.title(params.title);
}

Inherits.inheritsFrom(Base);

///// preparing code:
var testObj1 = new Inherits(testParams1);
var testObj2 = new Inherits(testParams2);

///// testing code:
equals(testObj1.viewModel.title(), testParams1.title, "first instance ok"); // returns "john"
equals(testObj2.viewModel.title(), testParams2.title, "second instance ok");  // also returns "john"
4

1 回答 1

32

问题)

  • Base构造函数只被调用一次。
    this.prototype = 新基类;
  • 特权方法将this跨实例引用相同的对象,因为这些方法是在构造函数中创建的(仅调用一次)。

基于意见的问题

  • Function.prototype如果您打算与不属于您的 JavaScript 一起工作,您应该尽量避免修改本机原型(即 . )。

解决方案

  • 为创建的每个新实例调用构造函数和父构造函数。
  • 继承父原型链(不是父类的实例)。
  • 保持原型链中创建的实例数与调用构造函数数的比例为 1:1。

Max问题的最终解决方案

请特别注意inherits功能和ParentClass.call(this, title);线路。要寻找的构造函数是ParentClassChildClass

/**
 * 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);
于 2013-01-29T05:30:28.817 回答