嵌套对象的原型继承
Prototypal inheritance with nested objects
我正试图了解 Javascript 中的原型继承。我想我已经掌握了基本概念,但是当我在玩这个的时候,我 运行 进入了下面,这仍然让我感到困惑。
有一个非常相似的问题和答案here,但它没有完全回答为什么会发生这种情况,至少对我来说不是。
我这样创建一个新对象:
var User = {
username: "",
name: {
first: "",
last: ""
}
}
接下来我创建两个 "instances" 该对象:
var user1 = Object.create(User);
var user2 = Object.create(User);
现在我这样设置名称 属性:
user1.name = { first: "John", last: "Jackson"}
user2.name = { first: "James", last: "Jameson"}
现在我
alert(user1.name.first) \ -> John
alert(user2.name.first) \ -> James
一切如预期。到目前为止一切顺利。
但是,如果我这样设置 name.first 属性:
user1.name.first = "John";
user2.name.first = "James";
然后我得到
alert(user1.name.first) \ -> James
alert(user2.name.first) \ -> James
很明显,现在 属性 是在原型对象 User
上设置的(或者更确切地说是包含的 name
对象),而不是在当前对象 user1
中覆盖它.为什么会这样?
进一步如果我这样做
user1.name.middle = "Mortimer"
我现在可以了
alert(User.name.middle) // -> Mortimer
这不是我所期望的。通常,每当在派生对象上设置 属性 时,该对象要么已经将 属性 作为 ownProperty
,在这种情况下,只需分配值,要么 属性在派生对象上新创建为 ownProperty
,覆盖原型 属性。就像我分配给 user1.name
.
时发生的一样
那么,为什么对包含在原型对象中的对象进行赋值会导致这种(至少对我而言)意外和违反直觉的行为?
按照我的理解,当进行赋值时,第一个检查是查看 user1
是否有一个名为 name
的 ownProperty
,但它没有。如果这是一个读取操作,现在将查找 prototype
属性 并 User
检查它是否有 ownProperty
name
。但是既然这是一个集合操作,为什么在通常只是简单地创建一个丢失的 ownProperty 时走原型链?
But since this is a set operation why walk the prototype chain when usually a missing ownProperty is simply created?
当您说 user1.name.first = "John"
时,user1.name
部分必须先解析,然后才能检索或设置 .first
属性。在你的例子中 user1.name
部分只存在于原型对象上,所以它是 that object whose .first
属性 you are setting.
类似地,当您说 user1.name.middle = "Mortimer"
时,user1.name
部分再次解析为原型中的嵌套对象,因此您在 .middle
属性 =34=]那个对象,这就是为什么User.name.middle
也returns"Mortimer"
.
如果您说 user1.name.first
和 user1.name
无法解析(在当前对象或其原型链中),那么您将有一个 TypeError: Cannot set property 'first' of undefined
。 (您可以通过说 user1.address.street = "something"
在现有代码中尝试该概念 - 您会得到 TypeError
,因为 user1.address
不存在于 user1
或其原型链上。 )
由于您已经阅读了类似的问题和答案,但似乎仍然难以理解该行为,因此我会尽量使我的解释尽可能清楚。这是我认为你出错的地方(强调我的):
Generally, whenever a property is set on a derived object, that object either already has that property as an ownProperty in which case the value is simply assigned, or the property is newly created as an ownProperty on the derived object, overriding the prototype property. Just like happens when I assign to user1.name.
这里的问题是您假设 user.name.first
算作 "a property . . . set on a derived object"(User
的一个实例)。然而,事实并非如此。在 JavaScript 中,属性的继承很浅(单层深)。 user.name
只是一个通过原型引用共享的对象值,所以从一个地方对它的修改到处都会反映出来。
将 user1.name
和 user2.name
想像为以下示例代码段中的 firstReference
和 secondReference
,希望您能更清楚地了解该行为。
var User = {
username: "",
name: {
first: "",
last: ""
}
}
var firstReference = User.name
var secondReference = User.name
firstReference.name.first = 'First!'
console.log(secondReference.name) //=> 'First!' (logical and expected result)
Object.create
方法使用第一个参数作为原型创建一个对象,第二个可选参数是一个具有自身属性的附加对象。
我认为这里的问题是 name
根据定义在原型中,因此不是自己的 属性。
如果你想要单独的属性,那么你应该使用第二个参数。原型是您存储方法和共享属性的地方。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
有更多详细信息。
关键是你想要原型和自己的属性。
我正试图了解 Javascript 中的原型继承。我想我已经掌握了基本概念,但是当我在玩这个的时候,我 运行 进入了下面,这仍然让我感到困惑。
有一个非常相似的问题和答案here,但它没有完全回答为什么会发生这种情况,至少对我来说不是。
我这样创建一个新对象:
var User = {
username: "",
name: {
first: "",
last: ""
}
}
接下来我创建两个 "instances" 该对象:
var user1 = Object.create(User);
var user2 = Object.create(User);
现在我这样设置名称 属性:
user1.name = { first: "John", last: "Jackson"}
user2.name = { first: "James", last: "Jameson"}
现在我
alert(user1.name.first) \ -> John
alert(user2.name.first) \ -> James
一切如预期。到目前为止一切顺利。
但是,如果我这样设置 name.first 属性:
user1.name.first = "John";
user2.name.first = "James";
然后我得到
alert(user1.name.first) \ -> James
alert(user2.name.first) \ -> James
很明显,现在 属性 是在原型对象 User
上设置的(或者更确切地说是包含的 name
对象),而不是在当前对象 user1
中覆盖它.为什么会这样?
进一步如果我这样做
user1.name.middle = "Mortimer"
我现在可以了
alert(User.name.middle) // -> Mortimer
这不是我所期望的。通常,每当在派生对象上设置 属性 时,该对象要么已经将 属性 作为 ownProperty
,在这种情况下,只需分配值,要么 属性在派生对象上新创建为 ownProperty
,覆盖原型 属性。就像我分配给 user1.name
.
那么,为什么对包含在原型对象中的对象进行赋值会导致这种(至少对我而言)意外和违反直觉的行为?
按照我的理解,当进行赋值时,第一个检查是查看 user1
是否有一个名为 name
的 ownProperty
,但它没有。如果这是一个读取操作,现在将查找 prototype
属性 并 User
检查它是否有 ownProperty
name
。但是既然这是一个集合操作,为什么在通常只是简单地创建一个丢失的 ownProperty 时走原型链?
But since this is a set operation why walk the prototype chain when usually a missing ownProperty is simply created?
当您说 user1.name.first = "John"
时,user1.name
部分必须先解析,然后才能检索或设置 .first
属性。在你的例子中 user1.name
部分只存在于原型对象上,所以它是 that object whose .first
属性 you are setting.
类似地,当您说 user1.name.middle = "Mortimer"
时,user1.name
部分再次解析为原型中的嵌套对象,因此您在 .middle
属性 =34=]那个对象,这就是为什么User.name.middle
也returns"Mortimer"
.
如果您说 user1.name.first
和 user1.name
无法解析(在当前对象或其原型链中),那么您将有一个 TypeError: Cannot set property 'first' of undefined
。 (您可以通过说 user1.address.street = "something"
在现有代码中尝试该概念 - 您会得到 TypeError
,因为 user1.address
不存在于 user1
或其原型链上。 )
由于您已经阅读了类似的问题和答案,但似乎仍然难以理解该行为,因此我会尽量使我的解释尽可能清楚。这是我认为你出错的地方(强调我的):
Generally, whenever a property is set on a derived object, that object either already has that property as an ownProperty in which case the value is simply assigned, or the property is newly created as an ownProperty on the derived object, overriding the prototype property. Just like happens when I assign to user1.name.
这里的问题是您假设 user.name.first
算作 "a property . . . set on a derived object"(User
的一个实例)。然而,事实并非如此。在 JavaScript 中,属性的继承很浅(单层深)。 user.name
只是一个通过原型引用共享的对象值,所以从一个地方对它的修改到处都会反映出来。
将 user1.name
和 user2.name
想像为以下示例代码段中的 firstReference
和 secondReference
,希望您能更清楚地了解该行为。
var User = {
username: "",
name: {
first: "",
last: ""
}
}
var firstReference = User.name
var secondReference = User.name
firstReference.name.first = 'First!'
console.log(secondReference.name) //=> 'First!' (logical and expected result)
Object.create
方法使用第一个参数作为原型创建一个对象,第二个可选参数是一个具有自身属性的附加对象。
我认为这里的问题是 name
根据定义在原型中,因此不是自己的 属性。
如果你想要单独的属性,那么你应该使用第二个参数。原型是您存储方法和共享属性的地方。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
有更多详细信息。
关键是你想要原型和自己的属性。