前言: 如果你不确定下面所说的“原型”是什么意思,请跳到下面的解释部分,然后再回到这个答案的开头。 :-)
假设在你的第一个例子中,你通过new
调用了Person
:
var person = new Person()
如果你仅仅是想创建一个`person`对象,那么使用第二种方法和使用`new Person`创建的对象唯一不同的地方就在于继承:通过`new Person`创建的对象会将`Person.prototype`对象分配为其底层原型。
我想知道是否有任何理由我不能一直使用前者的方法
如果你不需要使用原型,那么使用构造函数可能会使事情变得更加复杂。请注意,你的第二种方式可以更简洁地编写:
var person = {
eyes: 2,
ears: 2,
arms: 2,
hands: 2,
feet: 2,
legs: 2,
species: "Homo sapien"
};
这被称为对象初始化器:它创建一个新的对象,并列出你看到的属性。如果你想要一个新的,空的对象,不需要使用 x = new Object()
; 只需使用 x = {};
。
当对象是一次性的时候,直接创建它通常是最简单的方法。
构造函数的主要优势在于它们是工厂函数,用于创建基本相似的对象:具有相同的初始属性,具有相同的底层原型等。该函数可以接受参数并将其用于适当地为其创建对象,可能会对构造参数进行一些验证等。也就是说:它们集中了初始化逻辑。
构造函数不是唯一的函数工厂方式。你也可以这样做:
function buildPerson() {
return {
eyes: 2,
ears: 2,
arms: 2,
hands: 2,
feet: 2,
legs: 2,
species: "Homo sapien"
};
}
var person = buildPerson();
如果您希望该人拥有一个原型(适用于ES5浏览器及更高版本):
var personPrototype = {
};
function buildPerson() {
var obj = Object.create(personPrototype);
obj.eyes = 2;
obj.ears = 2;
obj.arms = 2;
obj.hands = 2;
obj.feet = 2;
obj.legs = 2;
obj.species = "Homo sapien";
return obj;
}
var person = buildPerson();
(还有一种更冗长的定义这些属性的方式。)
JavaScript非常灵活。 :-)
“原型”
JavaScript使用原型继承,这是一种说法,即可以通过对象B“支持”对象A,因此,如果您要求A具有不存在的属性,则JavaScript引擎将查看该属性是否存在于B上。快速实用的例子:
var proto = {
name: "proto's name"
};
var obj = Object.create(proto);
现在不要担心Object.create
,你只需要知道它创建一个新的对象,并根据你传递给它的对象分配其基础原型。所以obj
由proto
支持。
obj
没有name
属性,但如果我们这样做:
console.log(obj.name);
...我们看到"proto's name"
。这是因为当JavaScript引擎试图从obj
获取name
的值时,发现obj
没有name
属性,因此它查找obj
的原型proto
。在那里找到了它,并使用了proto
中的值。
这仅在获取值时发生(除非在一些高级情况下,现在可以忽略)。在设置属性值时,它被设置在您设置它的对象上。所以:
var proto = {
name: "proto's name"
};
var obj = Object.create(proto);
console.log(obj.name);
obj.name = "obj's name";
console.log(obj.name);
原型的目的是重用,因此一个对象可以成为多个其他对象的原型,这并不奇怪:
var proto = {
name: "proto's name"
};
var a = Object.create(proto);
var b = Object.create(proto);
console.log(a.name);
console.log(b.name);
a.name = "a's name";
console.log(a.name);
console.log(b.name);
原型对象是普通对象,我们可以更改它们:
var proto = {
name: "proto's name"
};
var obj = Object.create(proto);
console.log(obj.name);
proto.name = "updated";
console.log(obj.name);
由于obj
没有自己的name
属性,每次访问它时,JavaScript引擎都会去查找它的原型。
new
操作符自动为创建的对象分配一个原型:它使用函数的prototype
属性上拥有的对象。因此:
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log("My name is " + this.name);
};
var p = new Person("Fred");
p.sayName();
最后:由于原型对象是正常的对象,因此它们也可以有原型:
var rootProto = {
name: "root proto's name"
};
var middleProto = Object.create(rootProto);
middleProto.middleProp = "middle property";
var obj = Object.create(middleProto);
console.log(obj.name);
console.log(obj.middleProp);
对于
name
,JavaScript引擎会查看
obj
,并没有找到
name
属性,所以它会查看
middleProto
。在那里也没有找到
name
属性,所以它会查看
rootProto
。它在那里找到了,所以使用它。
混淆点:很多人被构造函数上的属性称为
prototype
所困惑,并认为它是函数的原型。它不是。它只是函数对象上的普通属性(函数是对象,可以有属性)。唯一特殊的地方就是当您通过
new
调用函数时,其会被
new
使用。非函数对象没有
prototype
属性,它们的原型不是普通属性,而是内部属性。您可以通过将对象传递给
Object.getPrototypeOf
来获取对象的原型。
var proto = {};
var obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto;
Person
不是一个对象,而是一个构造函数。要获取一个对象,您必须使用new
运算符调用该构造函数。 - Teemunew Object()
,对于这个功能,请编写一个字面量{}
。 - Paul S.