【问题标题】:Objects types in JavascriptJavascript 中的对象类型
【发布时间】:2015-08-18 15:05:13
【问题描述】:

这是一个来自处于学习阶段的新手的相当普遍的问题,我需要澄清一些问题。

我目前正在学习对象,此时我正在学习继承。在本课的这一点上,我已经学会了几种不同的方法来创建对象,但使用 this 关键字的对象似乎具有最多的功能:

function Person(){
    this.eyes = 2;
    this.ears = 2;
    this.arms = 2;
    this.hands = 2;
    this.feet = 2;
    this.legs = 2;
    this.species = "Homo sapien";
    }

我知道我可以用它来做什么,但是有一个对象是这样创建的:

var person = new Object();
person.eyes = 2;
person.ears = 2;
person.arms = 2;
person.hands = 2;
person.feet = 2;
person.legs = 2;
person.species = "Homo sapien";

因为我似乎可以用前者做我不能用后者做的事情,所以我想知道是否有任何理由我不会一直使用前一种方法。谁能帮我解决这个问题?我不需要很长的详细答案(尽管会很感激)。这只是一个我想摆脱的问题,所以我不会纠缠于此。

【问题讨论】:

  • “因为我似乎可以用前者做我不能用后者做的事情” 像什么?唯一想到的(当然,这是一个大问题)是将原型属性与实例相关联。
  • 注意,Person 不是一个对象,它是一个构造函数。要获取对象,您必须使用 new 运算符调用该构造函数。
  • 我永远不会推荐使用new Object(),因为这个功能写一个文字{}

标签: javascript object inheritance prototypal-inheritance


【解决方案1】:

前言:如果您不确定我下面的“原型”一词是什么意思,请跳到分隔线下方进行解释,然后回到答案顶部. :-)


假设在您的第一个示例中,您随后通过 new 调用 Person

var person = new Person();

...那么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 = {
    // ...properties for the person prototype...
};
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 使用 原型继承,这是一种奇特的说法,即对象 A 可以由对象 B “支持”,因此,如果您向 A 请求它没有的属性,则JavaScript 引擎将查看 B 上是否存在该属性。一个简单的实际示例:

var proto = {
    name: "proto's name"
};
var obj = Object.create(proto); // Creates an object backed by the given prototype

不用担心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); // `obj` is backed by `proto`
console.log(obj.name);          // "proto's name"
obj.name = "obj's name";
console.log(obj.name);          // "obj's name"

原型的目的是重用,所以一个对象可以是其他几个对象的原型也就不足为奇了:

var proto = {
    name: "proto's name"
};
var a = Object.create(proto);   // `a` is backed by `proto`
var b = Object.create(proto);   // `b` is also backed by `proto`
console.log(a.name);            // "proto's name"
console.log(b.name);            // "proto's name"
a.name = "a's name";
console.log(a.name);            // "a's name"
console.log(b.name);            // "proto's name"

原型对象是普通对象;我们可以改变它们:

var proto = {
    name: "proto's name"
};
var obj = Object.create(proto);
console.log(obj.name);          // "proto's name"
proto.name = "updated";
console.log(obj.name);          // "updated"

由于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"); // Creates an object backed by Person.prototype,
                            // then calls Person with this referring to the
                            // object
p.sayName();                // "My name is Fred";

最后:由于原型对象是普通对象,它们也可以有原型:

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);       // "root proto's name"
console.log(obj.middleProp); // "middle property"

对于name,JavaScript 引擎查看obj,没有看到name 属性,因此查看middleProto。它也没有看到name 属性,所以它查看rootProto。它在那里找到它,所以它使用它。

混淆点:很多人对构造函数的属性被称为prototype这一事实感到困惑,并认为它是函数的原型。它不是。它只是函数对象的普通属性(函数是对象并且可以具有属性)。 唯一它的特殊之处在于new 在您通过new 调用函数时使用它。非函数对象没有prototype 属性,它们的原型不是普通属性,它是内部的东西。您可以通过将对象的原型传递给Object.getPrototypeOf

var proto = {/*...*/};
var obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true

【讨论】:

  • 这正是我正在寻找的答案。在一个注释中,我想补充一点,这些示例是直接从我正在阅读的文本中复制而来的,我想知道为什么它会在使用此方法 (x = new Object();) 和此方法之间切换(x={ };)。无论如何,您很好地解释了为什么我想使用一种方法而不是另一种方法,这就是我在答案中寻找的。​​span>
  • @AGx-07_162:您对此的疑惑对您在 JavaScript 中的未来是一个非常好的迹象。 :-)
猜你喜欢
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-03
  • 2021-04-25
  • 2013-09-07
  • 2011-03-11
  • 2015-01-03
相关资源
最近更新 更多