在Javascript中向自定义对象添加方法

时间:2011-06-08 08:34:34

标签: javascript prototype-programming

  

可能重复:
  Use of 'prototype' vs. 'this' in Javascript?

我访问了各种网站,但无法理解以下向自定义对象添加方法的方法之间的区别:

方法1:

function circle(radius){
     this.radius = radius;
     this.area = function(){ return 3.14*this.radius*this.radius;}
}

方法2:

function circle(radius){
     this.radius = radius;
}
circle.prototype.area = function(){ return 3.14*this.radius*this.radius; }

其中一种方法是否有任何性能或设计问题而另一种方法没有?

2 个答案:

答案 0 :(得分:12)

这是一种看待差异的方法:

var circle1 = circle(1);
var circle2 = circle(1);
alert(circle1.area == circle2.area);

对于Method1,您会看到false,而Method2会在true中产生。这是因为在第一种情况下,你为每个创建的对象分配一个新的闭包函数,两个对象最终会有不同的area函数,即使它们都做同样的事情。在第二种情况下,对象使用其area方法共享相同的原型对象,该方法在两种情况下当然都是相同的。

通常,Method2更好,原因如下:

  1. 对象创建速度更快,因为只需要初始化自定义属性。
  2. 你不会浪费内存来实例化同一件事的多个副本。
  3. 您可以通过调用Object.hasOwnProperty
  4. 轻松找出哪些对象属性具有自定义值

    还有一些缺点要记住。

    1. 即使您从未创建任何对象,也总是花时间设置原型。
    2. 属性访问稍慢,因为JavaScript引擎需要首先检查对象属性,然后检查原型对象的属性(现代JavaScript引擎非常好地优化了这一点)。
    3. 在原型上放置对象或数组的常见陷阱,这些陷阱将在所有对象实例之间共享。
    4. 这是一个常见错误的例子:

      function NumberCollection()
      {
      }
      NumberCollection.prototype = {
          numbers: [],
          sum: function()
          {
              var result = 0;
              for (var i = 0; i < this.numbers.length; i++)
                  result += this.numbers[i];
              return result;
          }
      }
      
      var c1 = new NumberCollection();
      c1.numbers.push(5);
      alert(c1.sum());   // Shows 5
      var c2 = new NumberCollection();
      c2.numbers.push(6);
      alert(c2.sum());   // Oops, shows 11 because c1.numbers and c2.numbers is the same
      

      这里的正确方法是:

      function NumberCollection()
      {
          this.numbers = [];
      }
      NumberCollection.prototype = {
          numbers: null,
          sum: function()
          {
              var result = 0;
              for (var i = 0; i < this.numbers.length; i++)
                  result += this.numbers[i];
              return result;
          }
      }
      

答案 1 :(得分:5)

第一种方法(让我们称之为类型1)将area()函数添加到对象本身。每次使用circle构造new对象时,其正文将复制(!)到新实例。

第二种方法(类型2)将area()函数添加到对象原型(原型在层次结构中是“一级”)。每次创建circle的新实例时,只会复制radius属性。

现在,当您在第二个对象的实例上调用area()时,JavaScript无法在对象本身上找到该函数。现在它“上升”原型链并使用它找到的该名称的第一个函数。

现在这有几个含义:

  1. 新对象实例将更小(物理代码更少)
  2. 您可以同时为所有类型为2 的现有对象更改area的实现当它们存在时(通过更改原型),您不能为类型1执行此操作
  3. 您可以在类型2的单个实例中覆盖area的功能,同时保留函数的“原始实现”