7

我经常看到这种模式来定义 javascript 对象

function Person(name) {
    this.name = name;
}
Person.prototype.describe = function () {
    return "Person called "+this.name;
};

这篇文章中,它说直接向原型对象添加属性被认为是一种反模式。

来自“基于经典类”的语言,除了方法之外必须定义属性听起来不太正确,而且在 javascript 中,方法应该只是具有函数值的属性(我在这里吗?)

我想知道是否有人可以解释这一点,甚至提出更好的方法来处理这些情况

4

6 回答 6

7

在通常的面向对象语言中,您有一个描述成员、方法和构造函数的类的定义。

在 JS 中,“类”的定义(它不像其他语言那样真正是类......有时使用术语伪类)是构造函数本身。如果您的对象由 参数化name,则编写

function Person(name) {
    this.name = name;
}

即属性name必须在构造函数中设置。

当然也可以写

function Person(name) {
    this.name = name;
    this.describe = function() { ... };
}

它会按您的预期工作。

但是,在这种情况下,您将在每次调用构造函数时创建一个单独的方法实例。

另一方面,这里:

Person.prototype.describe = function () {
    return "Person called "+this.name;
};

你只定义一次方法。的所有实例都Person将收到一个__proto__指向Person.prototype. 所以如果你打电话

var myPerson = new Person();
myPerson.describe();

它会起作用,因为 JS 直接在对象中查找对象成员,然后在其原型等中一直到Object.prototype.

关键是在第二种情况下,该函数只存在一个实例。您可能会同意这是一个更好的设计。即使你不这样做,它也只会占用更少的内存。

于 2012-08-10T15:02:48.930 回答
6

该代码没有任何问题。据说是这个意思:

function Person(name) {
    this.name = name;
}
Person.prototype.age = 15; //<= adding a hardcoded property to the prototype

现在你会看到这个:

var pete = new Person('Pete'), mary = new Person('Mary');
pete.age; //=> 15
mary.age  //=> 15

大多数时候,这不是你想要的。分配给构造函数原型的属性在所有实例之间共享,在构造函数 ( this.name) 中分配的属性是特定于实例的。

于 2012-08-10T15:03:28.537 回答
3

正如 arxanas 所说,文章提到了data properties

我认为原因是数据通常特定于实例,因此将其添加到原型中没有意义。

此外,如果您的数据是可变类型,例如数组,并且您将其分配给原型,那么此数组实例将在所有实例之间共享,并且您不能像每个实例都有自己的数组一样使用它。


示例:以下导致不正确的行为:

function Set() {

}

// shared between instances
// each instance adds values to **the same** array
Set.prototype.elements = [];

Set.prototype.add = function(x) {
   this.elements.push(x);
};

它应该是:

function Set() {
    // each instance gets its own array
    this.elements = [];
}

Set.prototype.add = function(x) {
   this.elements.push(x);
};

把它们加起来:

  • 将应该在所有实例之间共享的属性添加到原型。
  • 在构造函数中分配实例特定的数据。
于 2012-08-10T14:58:25.573 回答
1

就像阿克萨纳斯在他的评论中写的那样。原型中的数据属性或多或少类似于传统 oop 中的类级变量。除非您有非常特殊的需要,否则这些不是每天都使用的。就这样。

于 2012-08-10T14:59:55.017 回答
1

在原型上声明属性根本不是反模式。当我看一个prototype对象时,我会想“这就是这种类型的原型对象对数据和方法所拥有的东西”。

其他人警告不要在原型中给属性一个引用值,例如:Foo.prototype.bar = [];--- 因为数组和对象是引用类型。引用类型是不可变的,因此“类”的每个实例都引用同一个数组或对象。只需null在原型中设置它们,然后在构造函数中给它们一个值。

我总是在原型中包含所有属性,原因很明确:与其他程序员交流哪些属性是公开可用的以及它们的默认值是什么,而不需要他们筛选构造函数来弄清楚。

如果您正在创建需要文档的共享库,这将变得特别有用。

考虑这个例子:

/**
 * class Point
 * 
 * A simple X-Y coordinate class
 *
 * new Point(x, y)
 * - x (Number): X coordinate
 * - y (Number): Y coordinate
 *
 * Creates a new Point object
 **/
function Point(x, y) {
    /**
     * Point#x -> Number
     *
     * The X or horizontal coordinate
     **/
    this.x = x;

    /**
     * Point#y -> Number
     *
     * The Y or vertical coordinate
     **/
    this.y = y;
}

Point.prototype = {
    constructor: Point,

    /**
     * Point#isAbove(other) -> bool
     * - other (Point): The point to compare this to
     *
     * Checks to see if this point is above another
     **/
    isAbove: function(other) {
        return this.y > other.y;
    }
};

(文档格式:PDoc

在这里仅仅阅读文档有点尴尬,因为关于xy属性的信息嵌入在构造函数中。将其与在原型中包含这些属性的“反模式”进行对比:

/**
 * class Point
 * 
 * A simple X-Y coordinate class
 *
 * new Point(x, y)
 * - x (Number): X coordinate
 * - y (Number): Y coordinate
 *
 * Creates a new Point object
 **/
function Point(x, y) {
    this.x = x;
    this.y = y;
}

Point.prototype = {

    /**
     * Point#x -> Number
     *
     * The X or horizontal coordinate
     **/
    x: 0,

    /**
     * Point#y -> Number
     *
     * The Y or vertical coordinate
     **/
    y: 0,

    constructor: Point,

    /**
     * Point#isAbove(other) -> bool
     * - other (Point): The point to compare this to
     *
     * Checks to see if this point is above another
     **/
    isAbove: function(other) {
        return this.y > other.y;
    }

};

现在查看原型可以为您提供实际对象的快照,这更容易在您的脑海中可视化,并且作者更容易编写文档。构造函数也不会被文档弄得乱七八糟,并且坚持将Point对象带入生活的业务。

Point原型拥有一切,并且是关于“原型”对象对方法数据都有什么的规范信息源。

我认为在原型中包含数据属性是反模式。

于 2014-10-28T17:14:09.233 回答
1

也许相关:通常修改您不拥有的对象被认为是反模式

这意味着,如果您没有创建对象,那么您就不会“拥有”该对象。包含:

  • 本机对象(对象、数组等)
  • DOM 对象
  • 浏览器对象模型 (BOM) 对象(例如window
  • 库对象

Nicholas C. Zakas 提供可维护的 Javascript源代码

于 2020-06-23T12:48:56.517 回答