在构造函数中声明javascript对象方法与原型
在创buildjavascript对象时,我可以在构造函数或原型中放置一个方法声明。 例如,说我想要一个具有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"); };
当我实例化Dogtypes的对象时,这两种方法似乎都能正常工作:
var dog = new Dog("Fido"); dog.Bark(); //Both approaches show "Fido bark"
我应该更喜欢这些方法之一吗? 使用其中一个有什么优势? 在幕后,这两种方法最终会做同样的事情吗? 大多数人倾向于哪种方法?
谢谢您的帮助。
对于你给的例子,你应该使用原型的方法。 一般来说,这取决于。 第一种方法(在构造函数中初始化方法)的主要优点是可以利用方法中构造函数中定义的局部variables来利用闭包。 这些variables不能直接在构造函数之外访问,所以实际上是“私有的”,这意味着你的API比这些variables被定义为对象的属性更清晰。 一些一般的经验法则:
- 如果你的方法不使用你的构造函数中定义的局部variables(你的例子没有),那么使用原型方法。
- 如果你正在创造大量的
Dog
,使用原型的方法。 这样,所有的“实例”(即由Dog
构造函数创build的对象)将共享一组函数,而构造函数方式,每次调用Dog
构造函数时都会创build一组新的函数,并使用更多的内存。 - 如果你正在创build一小部分
Dog
并发现使用本地,你的构造函数中的“私有”variables提高了你的代码,这可能是更好的方法。 如果性能或内存消耗是主要问题,请使用您的判断并做一些基准testing。
有可能使用混合方法,只有需要访问本地私有构造函数variables的方法在构造函数中定义,而其他方法则被分配给原型。
例如,下面的代码在构造函数中使用一个局部variables来跟踪这只狗吠叫的次数,同时保持实际的数字是私有的,所以吠叫相关的方法在构造函数中定义。 尾巴摇摆不需要访问树皮的数量,因此可以在原型上定义该方法。
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();
这两者是不同的:第一个将只存储对原型对象的方法的引用,而第二个解决scheme将在每个对象上存储方法。 这意味着每个对象将包含一个额外的指针,因此每个对象占用更多的内存。
per-object方法允许方法在构造函数中引用variables(闭包),因此它允许您访问某些无法从原型方法访问的数据。
最后,可以稍后更改原型方法,即可以在运行时在原型对象上重新定义Bark
,并且此更改将适用于具有此原型的所有对象(因为该方法总是通过原型查找)。
我见过的绝大多数javascript代码都使用原型方法。 我认为这有三个原因,我可以想到我的头顶。
首先是避免让每个类都成为一个巨大的构造函数:构造函数逻辑进入构造函数,其他方法的逻辑在其他地方声明 – 这主要是一个清晰的事情/分离关注的事情,但在JavaScript中,你需要每一个位的清晰度,你可以得到你的手。
其次是效率。 当您在构造函数中声明方法时,您正在为该对象的每个实例创build一个函数对象的新实例,并且将构造函数的作用域绑定到这些函数中的每一个函数(也就是说,它们可以引用对构造函数的参数,只要对象存在,这个参数就不会被修改)。 在原型上声明方法时,所有实例都使用函数对象的一个副本 – 原型属性不会复制到实例上。
第三个原因是,当你使用原型方法时,你可以用各种方法“扩展”一个类,例如Backbone.js和CoffeeScript类构造所使用的原型链。