6

我经常看到这种模式来定义JavaScript对象原型为什么定义特性被认为是一个反

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

而且在this article它说,直接添加属性原型objct被认为是一个反模式。

来自“古典基于类”的语言,必须定义方法以外的属性听起来不太正确,更在JavaScript中,其中一个方法应该只是一个具有函数值的属性(我在这里吗? )

我想知道如果任何人都可以解释这一点,甚至提出一个更好的方式来处理这些情况

+5

它说“数据属性”,我相信它是指变量。我不认为它提到了向原型添加功能的任何事情。 – 2012-08-10 14:54:54

+0

arxanas说什么。确切的引用是:“一个类的主体只能包含方法,没有数据属性,具有数据属性的原型通常被认为是反模式,所以这只是一个最佳实践。”换句话说,通过“数据属性”,作者的意思是“不属于方法/功能的属性”。 – ruakh 2012-08-10 14:57:44

+0

你的问题是基于一个误解,不知道该怎么做。我永远不会记得看到数据被放置在原型上。我不能真正编辑你的问题在原型中有数据,因为这不是我经常看到的*模式。 – Esailija 2012-08-10 14:58:22

回答

6

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

在JS中,“类”的定义(它不像其他语言中的类一样...有时使用术语伪类)是构造函数本身。如果你的对象是由name parametrised,是有意义的编写

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

问题是,在第二种情况下,只有一个函数实例存在。你可能会同意这是一个更好的设计。即使你不这样做,它只需要更少的内存。

+1

非常感谢,我没有意识到,从构造函数的每个方法定义将是一个不同的函数实例... – opensas 2012-08-10 15:17:46

3

由于arxanas说,文中提到数据性能

我假设的原因是数据通常是特定于实例,所以将其添加到原型没有任何意义。

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


例子:下导致不正确的行为:

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); 
}; 

概括起来:

  • 将应该在所有实例之间共享的属性添加到原型。
  • 在构造函数中分配实例特定的数据。
1

就像arxanas在他的评论中写道。原型中的数据属性与传统的oop中的类级变量差不多。除非您有非常特殊的需求,否则这些服务不会每天使用。就这样。

5

该代码没有问题。推测这是什么意思:

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)是特定于实例的属性。

1

在原型上声明属性根本不是反模式。当我看到一个对象时,我认为“这就是这种类型的原型对象对数据和方法的作用。”

其他人已经警告不要在原型中给属性一个参考值,例如: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对象对于方法数据具有什么样的信息的标准来源。

我会争辩说不是包括原型中的数据属性是反模式。

相关问题