39

可能重复:
在 Javascript 中使用“原型”与“这个”?
Javascript中的面向对象问题

您能否推荐以下哪些是最好的或它们的优缺点?

方法一

function User() {

    this.name = "my name";
    this.save = function() {

    };
}

方法二

function User() {

    this.name = "my name";
}

User.prototype.save = function() {

}
4

4 回答 4

57

来自 Java 和 PHP 的背景,我最初对 JavaScript 中的整个“类”概念感到困惑。这里有几点需要考虑。

建造

首先,因为您可能会将您的类定义为..

Customer = function () {}
Customer.prototype = new Person();

如果您在构造过程中定义属性和方法,您最终会遇到各种各样的代码噩梦。原因是像这样的一段代码Customer.prototype = new Person();需要在 Customer 构造函数中调用 Person 以实现真正的继承。否则您最终将不得不始终知道原始设置的内容。举个例子:

Person = function (name) {
    this.name = name;
    this.getName = function () {return this.name}
}
Customer = function (name) {
    this.name = name;
}
Customer.prototype = new Person();

现在我们将更新 Person 以设置它们是否是“新的”:

Person = function (name, isNew) {
    this.name = name;
    this.isNew = isNew;
    this.getName = function () {return (this.isNew ? "New " : "") + this.name; }
}

现在在从 Person 继承的任何“类”上,您必须更新构造函数以遵循表单。您可以通过执行以下操作来解决此问题:

Customer = function () {
    Person.apply(this, arguments);
}

这将在新的“客户”范围内调用“人员”,让您不必了解人员构造。

速度

看看这些基准:http: //jsperf.com/inherited-vs-assigned
基本上,我在这里试图证明的是,如果您要大量创建这些对象那么最好的方法是在原型上创建它们。像这样创建它们:

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

非常慢,因为对于每个对象创建,它都会创建一个新函数- 它不会简单地查找原型链以查找已经存在的函数。相反,如果您只有几个对象的方法被极其频繁地调用,则在本地定义它们有助于降低查找原型链所损失的速度。

共享属性

这总是让我着迷。假设您有以下内容:

Person = function () {
    this.jobs = {};
    this.setJob = function (jobTitle, active) {this.jobs[jobTitle] = active; }
}

Employee = function () {}
Employee.prototype = new Person();

var bob = new Employee();
bob.setJob('janitor', true);

var jane = new Employee();
console.log(jane.jobs);

你猜怎么着?简是个看门人!可不是闹着玩的!这就是为什么。由于您没有将 this.jobs 定义为Employee实例化的新对象,因此它现在只是查找原型链,直到找到“工作”并按原样使用它。有趣,对吧?

因此,如果您想跟踪实例,这很有用,但在大多数情况下,您会发现它非常令人沮丧。您可以通过执行以下操作来解决此问题:

Employee = function () { Person.apply(this); }

这迫使“Person”创建一个新的“this.jobs”。

私有变量

由于 JavaScript 中没有什么是真正“私有的”,因此获取私有变量的唯一方法是在构造函数中创建它们,然后在构造函数中初始化任何依赖它们的东西。

Person = function () {
    var jobs = {};
    this.setJob = function (jobTitle, active) {jobs[jobTitle] = active; }
    this.getJob = function (jobTitle) { return jobs[jobTitle]; }
}

但是,这也意味着您必须在继承类的每个实例化上调用该构造函数。


建议

http://ejohn.org/blog/simple-javascript-inheritance/

这是一个超级基础的课程设置。这很简单。有用。它会在 90% 的时间里做你需要做的事情,而不必处理 JavaScript 必须提供的所有疯狂:)

</rant>

于 2012-11-02T06:37:10.037 回答
10

最佳是一个非常主观的术语。

方法 1 提供了真正私有变量的可能性。例如:

function User() {
   var name = "fred";
   this.getName = function () { return name;};
}

方法 2 将使用更少的内存,因为所有 User 对象共享一个保存函数。

“最佳”将由您的具体要求决定。

于 2012-11-02T06:04:45.747 回答
7

JavaScript 是一种“基于对象”的语言,而不是“基于类”的语言。共享行为,即类概念,是通过链接原型来实现的。

方法 1 不创建类。每次调用它时,您都会按照定义创建属性和函数,并且它们不会与其他“实例”共享。如果您更换this.save,那么只有一个实例会改变行为。

方法 2 实现共享行为。因此,它是人们与类和实例相关联的东西。如果您用this.save不同的函数替换,那么所有派生实例将立即具有新行为。

如果“最佳”意味着不消耗内存重新定义函数和共享常见行为(这是基于类的编程倾向于等同的),那么方法 2 就是要走的路。

于 2012-11-02T07:44:59.453 回答
6

正如其他人所提到的,有两个主要区别:

  • 方法 1 将为 User 的每个实例创建一个新函数
  • 方法 1 将能够访问构造函数范围内的局部变量

这是一个演示这些的示例:

function User() {

    var name = "my name";

    this.foo = function() {
        // one function per User instance
        // can access 'name' variable
    };
}

User.prototype.bar = function() {
    // one function shared by all User instances
    // cannot access 'name' variable
};

var a = new User();
var b = new User();

console.log(a.foo === b.foo); // false; each instance has its own foo()
console.log(a.bar === b.bar); // true; both instances use the same bar()
于 2012-11-02T06:28:56.990 回答