this.constructor.prototype - 不能完全覆盖,但可以写个别道具吗?

时间:2014-01-22 23:48:38

标签: javascript

TL; DR? 为什么我不能在构造函数中覆盖构造函数的原型?

我正在弄清楚我的原型继承模式。我不喜欢原型通常是从​​构造函数外部定义的,并且想要在逻辑上更好地封装事物。

我发现我期望工作的一条神奇的线路没有。

function Orifice(){
  this.exhaust=function(){};
  this.ingest=function(){};
}
var standardOrifice = new Orifice();

function Sphincter(){
  this.constructor.prototype = standardOrifice; // <-- does not work
  this.relax=function(){};
  this.tighten=function(){};
}

有趣的是,我可以将单个属性写入this.constructor.prototype,但我不能像构造函数定义之外那样覆盖整个原型对象。

这样的东西很有效:

  this.constructor.prototype.exhaust = standardOrifice.exhaust;
  this.constructor.prototype.ingest = standardOrifice.ingest;

我可以创建一个简单的克隆函数来处理这个问题:

function extend(target){
  return {
    from: function(obj){
      target.__proto__ = obj.constructor.prototype;
      for (key in obj) if (obj.hasOwnProperty(key)) target[key]=obj[key];
      return target;
    }
  };
}

值得庆幸的是,到目前为止,在我的测试中,这种技术似乎运行良好,但我不确定是否有可能缺少的细节或性能案例。

function Sphincter(){
  extend(this.constructor.prototype).from(standardOrifice);
  //...
}

为什么我不能在构造函数中覆盖构造函数的原型?但是我可以在构造函数之外吗?单独编写属性是否在构造函数中起作用?

2 个答案:

答案 0 :(得分:5)

  

为什么我不能在构造函数中覆盖构造函数的原型?

你可以,但为时已晚。新实例已经生成,继承自旧原型。也许阅读how new works

  

我不喜欢原型通常是从​​构造函数外部定义的。

就是这样。你实际上不应该在构造函数中设置原型 - 它会在每次创建新实例时执行。这就是原型不应该是什么。另请参阅Assigning prototype methods *inside* the constructor function - why not?

  

并希望在逻辑上更好地封装内容。

您可能希望了解各种(揭示)module patterns。或者甚至可能在某个Class框架上。

  

我目前正在寻找更具体的理由,我不应该以我一直呈现的模式出发。

它在Internet Explorer中不起作用。它不适用于任何不支持__proto__属性的ES5兼容环境。你永远不应该使用它在现有对象上设置原型。相反,对Object.create使用Correct javascript inheritance(或其垫片) - 这要求您在构造函数之外覆盖原型。

我的建议是在构造函数之外调用你的extend帮助器,它仍然有一个很好的语法。

答案 1 :(得分:1)

回答具体问题

  

为什么我不能在构造函数中覆盖构造函数的原型?

这是因为在对象已经被实例化之后,构造函数实际上被称为。并且由于您的对象已经设法在构造函数触及任何内容之前实例化,因此您的构造函数也已经被赋予了“默认”原型。

this.constructor.prototype添加属性似乎有效 - 因为您实际上正在操纵构造函数的预分配默认原型对象,所有实例都继承该对象。

在我的示例中,this.constructor.prototype最终引用了构造函数的默认分配原型:所以完全覆盖它意味着从那一刻开始的所有新实例将具有该新原型 - - 正如Bergi所说,“为时已晚” - 您当前的实例将没有新的原型,因为它仍然具有旧的默认分配原型,因为它已经被实例化了。

避免废话的更好模式

我已经明白,我的问题中提出的技术根本不会这样做。问题本身通常是错误的。通过将Bergi的智慧与我个人的偏见相结合,我想出了这种模式,以避免必须完全找到原始问题的答案:

function extend(p){
  return { to: function(C){ for (k in p) if (p.hasOwnProperty(k)) 
  C.prototype[k]=p[k]; return C; } };
};

var orifice = new function Orifice(){
  this.exhaust=function(){};
  this.ingest=function(){};
};

var Sphincter = extend(orifice).to(function Sphincter(){
  this.relax=function(){};
  this.tighten=function(){};
});


这是扩展功能,扩展名为:

function extend(parentObject){
  return { 
    to: function(ChildConstructor){
      for (key in parentObject) 
        if (parentObject.hasOwnProperty(key)) 
          ChildConstructor.prototype[key] = parentObject[key];
      return ChildConstructor;
    }
  };
};

我用它来测试它的工作原理:

// TESTING
var s=new Sphincter();
var tests=['relax','tighten','exhaust','ingest'];
for (var i in tests) console.log("s."+tests[i]+"() is "+(tests[i]in s?"present :)":"MISSING!"));