在构造函数和原型中声明javascript对象方法

Declaring javascript object method in constructor function vs. in prototype

本文关键字:javascript 对象 方法 声明 构造函数 原型      更新时间:2023-09-26

在创建javascript对象时,我可以在构造函数或原型中放置方法声明。例如,假设我想要一个具有Name属性和Bark方法的Dog类。我可以将Bark方法的声明放入构造函数:

var Dog = function(name) {
    this.Name = name;
    this.Bark = function() {
        alert(this.Name + " bark");
    };
}

或者我可以把它作为一种方法放在原型对象上:

var Dog = function(name) {
    this.Name = name;
}
Dog.prototype.Bark = function() {
    alert(this.Name + " bark");
};

当我实例化Dog类型的对象时,这两种方法似乎都很好:

var dog = new Dog("Fido");
dog.Bark();  //Both approaches show "Fido bark"

我应该选择其中一种方法而不是另一种方法吗?使用其中一个比使用另一个有什么好处吗?在幕后,这两种方法最终会做完全相同的事情吗?大多数人倾向于采用哪种方法?

谢谢你的帮助。

对于您给出的示例,您应该使用原型方法。总的来说,这取决于情况。第一种方法(初始化构造函数中的方法)的主要优点是,可以通过在方法中使用构造函数中定义的局部变量来利用闭包。这些变量在构造函数之外无法直接访问,因此实际上是"私有"的,这意味着API比将这些变量定义为对象的属性更干净。一些经验法则:

  • 如果你的方法没有使用构造函数中定义的局部变量(你的例子没有),那么就使用原型方法
  • 如果您正在创建大量的Dog,请使用原型方法。这样,所有"实例"(即Dog构造函数创建的对象)都将共享一组函数,而构造函数的方式是,每次调用Dog构造函数时都会创建一组新的函数,使用更多的内存
  • 如果您正在创建少量的Dog,并且发现在构造函数中使用本地"私有"变量可以改进代码,那么这可能是更好的方法。如果性能或内存消耗是主要问题,请使用您的判断并进行一些基准测试

可以使用混合方法,即在构造函数中只定义需要访问本地私有构造函数变量的方法,而将其他方法分配给原型。

例如,下面的代码在构造函数中使用一个局部变量来跟踪这只狗吠叫的次数,同时保持实际次数的私有性,因此与吠叫相关的方法在构造函数中定义。摇尾巴不需要访问树皮的数量,因此该方法可以在原型上定义。

var Dog = function(name) {
    this.name = name;
    var barkCount = 0;
    this.bark = function() {
        barkCount++;
        alert(this.name + " bark");
    };
    this.getBarkCount = function() {
        alert(this.name + " has barked " + barkCount + " times");
    };
};
Dog.prototype.wagTail = function() {
    alert(this.name + " wagging tail");
};
var dog = new Dog("Dave");
dog.bark();
dog.bark();
dog.getBarkCount();
dog.wagTail();

两者不同:第一个解决方案将只在原型对象上存储对方法的引用,而第二个解决方案则将方法存储在对象的每个上。这意味着每个对象都将包含一个额外的指针,从而占用更多的内存。

每个对象方法允许该方法引用构造函数中的变量(闭包),因此它允许您访问一些无法从原型方法访问的数据。

最后,原型方法可以在之后进行更改,也就是说,您可以在原型对象的运行时重新定义Bark,这种更改将适用于具有该原型的所有对象(因为该方法总是通过原型查找的)。

我看到的绝大多数javascript代码都使用原型方法。我认为这有三个原因,我可以不假思索地想到。

首先,你要避免每个类都是一个巨大的构造函数:构造函数逻辑在构造函数函数中,其他方法的逻辑在其他地方声明——这主要是一个清晰的事情/关注点分离的事情,但在javascript中,你需要你能得到的每一点清晰。

第二是效率。当您在构造函数中声明方法时,您将为对象的每个实例创建一个新的函数对象实例,并将构造函数的作用域绑定到这些函数中的每一个(也就是说,它们可以引用,例如,构造函数的参数,只要对象存在,这些参数就永远不会被gc化)。当您在原型上声明方法时,所有实例都使用函数对象的一个副本——原型属性不会复制到实例上。

第三个原因是,当使用prototype方法时,可以通过各种方式"扩展"类,例如Backbone.js和CoffeeScript的类构造使用的原型链接。