在Javascript中定义可重用对象的基本方法是什么?我说可重用以排除单例技术,例如直接用对象文字表示法声明变量。我在某处看到克罗克福德在他的书中定义了四种这样的方法,但我宁愿不用为这一小段信息买一本书。
以下是我熟悉的方法:
使用this
,并使用new
构建(我认为这称为经典?)
function Foo() {
var private = 3;
this.add = function(bar) { return private + bar; }
}
var myFoo = new Foo();
使用类似的原型
function Foo() {
var private = 3;
}
Foo.prototype.add = function(bar) { /* can't access private, correct? */ }
返回文字,而不是使用this
或new
function Foo() {
var private = 3;
var add = function(bar) { return private + bar; }
return {
add: add
};
}
var myFoo = Foo();
我可以想到这些相对较小的变化可能无关紧要。我错过了什么样的风格?更重要的是,每种方法的优缺点是什么?是否有人建议坚持,或者是偏好和圣战?
答案 0 :(得分:14)
使用原型。从构造函数返回特定对象会使它们成为非构造函数,并且将方法分配给this
会使继承变得不那么方便。
优点:
如果某人忘记了new
,他们仍会获得该对象。
您可以创建真正的私有变量,因为构造函数中定义的对象的所有方法都共享其范围。
缺点:
new
或没有new
。 new Foo() instanceof Foo
也会产生false
。prototype
优点:
让构造函数保持整洁。
这是JavaScript中的标准处理方式,所有内置构造函数都将其方法放在原型上。
继承变得更容易,更正确;您可以(并且应该)使用Object.create(ParentConstructor.prototype)
代替new ParentConstructor()
,然后从ParentConstructor
内拨打Constructor
。如果要覆盖方法,则可以在原型上执行此操作。
您可以在创建对象后对其进行“修改”。
您可以扩展您无权访问的构造函数的原型。
缺点:
它可能有点过于冗长,如果您想更改函数的名称,您还必须更改添加到原型中的所有函数。 (或者将原型定义为具有constructor
的兼容属性描述符的一个大对象文字。)
它们不共享特定于实例的范围,因此您不能真正拥有私有变量。
this.*
优点:
缺点:
Array.prototype.slice.call(collectionLikeObject)
。答案 1 :(得分:4)
这主要是一个偏好问题。没有一种方法可以制作鸡汤面,而且均匀的物体也是一样的。
我不使用这3个中的任何一个,尽管它们都是为了自己的目的而工作。我使用一个名为Object:deploy的自定义函数,并像这样使用它。
var a = { hey: 'hello' },
b = {}.deploy(a);
console.log(b.hey); // 'hello'
使用prototype
对大多数人来说是最好的,因为自动滴漏。
function A() {};
A.prototype.hello = "Hey";
var a = new A();
console.log(a.hello); // 'Hey'
A.prototype.hello = "Hello";
console.log(a.hello); // 'Hello'
与普遍看法相反,您可以在prototype
中使用私有变量。
function Hello() {};
(function () {
var greeting = "Hello";
Hello.prototype.greet = function () { return greeting };
}).apply(this);
但即使这是可能的,通常也会做得更好......
function Hello() {};
Hello.prototype.greeting = "Hello";
Hello.prototype.greet = function () { return this.greeting };
答案 2 :(得分:2)
第二种方法(原型)与其他语言(如Python)中的标准类更相似,因为您有一个共同的“原型”对象,所有实例都在共享。比较第一种和第二种方法:
new Foo()
”时,您都会创建一个全新的对象并插入所有方法。这不是非常节省时间或空间,因为每个Foo实例都有自己的所有方法表。您可以通过创建两个Foo对象并询问foo1.add == foo2.add
(false)来测试它。方法3与此非常相似;我不确定方法1和方法3之间的语义差异(如果有的话)。foo1.add == foo2.add
,你就会成真。这更节省空间和时间。它还允许您在创建实例后向原型添加更多方法,并且它们将看到新方法。正如您所说,方法2的问题在于您无法访问私有成员。但您仍然可以将非私有成员添加到对象本身,并使用原型方法访问它们:
function Foo() {
this.private = 3;
}
Foo.prototype.add = function(bar) { return this.private + bar }
需要注意的是foo.private
在外部是可见的。
答案 3 :(得分:1)
原型1没有每个对象实例开销,只有每个类开销的add函数。仅这一点就是我不喜欢其他两种方法的原因。
答案 4 :(得分:0)
不要忘记继承。有一天你需要。对于组织继承,最好的方法是组合技术:
function Foo() {
this.array = [1, 2, 3];
this.add = function(bar) { return private + bar; }
}
Foo.prototype.add = function(bar) { }
var myFoo = new Foo();
在构造函数中设置字段对于避免子对象修改它们很有用。 将方法设置为原型比在构造函数中每次都这样做更快。