创建JavaScript类的最佳方法是什么?

39

在大多数情况下,方法2更好,但这可能取决于您的保存方法中有什么代码(例如,在方法1中,“save()”将可以访问在“User()”内创建的任何局部变量)。 - nnnnnn
2
变量和属性不是同一回事。在方法2中,.save()方法确实可以访问对象的属性。无论哪种方式,.save()方法都可以访问this.name。但是在方法2中,.save()将无法访问构造函数中的局部变量 - 在您的示例中没有这样的变量,但更复杂的“类”可能需要或可能不需要这样做。 - nnnnnn
我看到了一篇文章,讲述了这个问题以及更多内容 http://www.phpied.com/3-ways-to-define-a-javascript-class/ - AurA
@nnnnnn 是的。但它会将变量“testing”暴露给外部。 - Arunoda Susiripala
3
我更喜欢方法1,因为它在单个定义中包含了所有“类”声明。顺便提一下,这是一篇关于面向对象编程的好文章:http://killdream.github.com/blog/2011/10/understanding-javascript-oop/。 - Eldelshell
显示剩余6条评论
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继承的'class',您必须更新构造函数以遵循格式。您可以通过执行以下操作来解决此问题:

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

这将在新的“Customer”作用域中调用“Person”,使您无需了解“Person”构造。

速度

看一下这些基准测试: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);

猜猜怎么着?简(Jane)是个清洁工!真心话!原因是这样的,由于您在Employee的实例化时没有将this.jobs定义为新对象,它现在只是查找原型链直到找到'jobs'并像原样使用它。很有趣,对吧?

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

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>


10

“Best”是一个非常主观的词。

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

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

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

“最佳”将根据您的具体要求确定。


7
JavaScript是一种“基于对象”而非“基于类”的语言。共享行为,即类的概念,是通过链接原型来实现的。
方法1并不创建一个类。每次调用它时,您都会按照定义创建属性和函数,并且它们不与其他“实例”共享。如果您替换了this.save,那么只有一个实例会具有更改后的行为。
方法2实现了共享行为。因此,它是人们所关联的类和实例。如果您用不同的函数替换this.save,那么所有派生实例将立即具有新的行为。
如果“最佳”意味着不会消耗内存重新定义函数并共享常见行为(这通常等同于基于类的编程),则方法2是正确的选择。

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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接