Javascript原型继承与对象属性阴影 [英] Javascript Prototypal Inheritance & object properties shadowing

查看:41
本文介绍了Javascript原型继承与对象属性阴影的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

var person = { name :"dummy", personal_details: { age : 22, country : "USA" } };

var bob = Object.create(person);

bob.name = "bob";
bob.personal_details.age = 23;


console.log(bob.personal_details === person.personal_details);
// true : since it does not shadow object of prototype object

console.log(bob.name  === person.name);
// false : since it shadows name

////now
bob.personal_details  = {a:1};
console.log(bob.personal_details === person.personal_details); 

//false

当对象 bob 尝试覆盖 person ,那么它就会在bob自身中被遮盖.

When object bob tries to override "name" property of person then, it gets shadowed in bob itself.

但是如果出现 personal_details ,则违反了同一规则.

But in case of personal_details the same rule is violated.

我很好奇为什么会这样?

I am quite curious to know why is it like so ??

以下是jsbin的链接: http://jsbin.com/asuzev/1/编辑

Here is the link to jsbin : http://jsbin.com/asuzev/1/edit

推荐答案

可以通过一些示例很容易地说明发生了什么:

What is going on can be illustrated quite easily with a few examples:

var person = { name :"dummy", personal_details: { age : 22, country : "USA" } }
var bob = Object.create(person)

bob.name = "bob"
bob.personal_details.age = 23

输出类似:

console.log( bob );
/// { name :"bob", personal_details: { age : 23, country : "USA" } }

console.log( person )
/// { name :"dummy", personal_details: { age : 23, country : "USA" } }

现在在人员对象上设置了23岁,因为bob.personal_details 是通过bob的原型链直接引用 person.personal_details 的.导航到对象结构后,就可以直接使用 person.personal_details 对象.

Age 23 is now set on the person object because bob.personal_details is a direct reference to person.personal_details via bob's prototype chain. Once you navigate down the object structure you are working directly with the person.personal_details object.

但是,如果您用另一个对象覆盖bob的 personal_details 属性,则该原型链接将被更本地的属性覆盖.

However if you override bob's personal_details property with another object, that prototype link will be overridden by a more local property.

bob.personal_details = { a: 123 }

现在的输出是:

console.log( bob );
/// { name :"bob", personal_details: { a : 123 } }

console.log( person )
/// { name :"dummy", personal_details: { age : 23, country : "USA" } }

因此,从现在开始,通过访问 bob.personal_details ,您将引用 {a:123} 对象,而不是原始的 {年龄:23​​,国家/地区:美国";} 对象.所做的所有更改都将在该对象上发生,并且基本上与 bob person 对象无关.

So by accessing bob.personal_details—from now on—you are referencing the { a: 123 } object not the original { age : 23, country : "USA" } object of person. All changes made will occur on that object and basically have nothing to do with bob or the person object.

为了使事情变得有趣,在上述所有操作之后,您认为会发生什么?

To make things interesting, what do you think happens when you do this, after all the above:

delete bob.personal_details

您最终将原始原型链接恢复为 person.personal_details (因为您已删除添加的本地属性),因此控制台日志将显示:

You end up reviving the original prototype link to person.personal_details (because you've removed the local property you added), so a console log would reveal:

console.log( bob );
/// { name :"bob", personal_details: { age : 23, country : "USA" } }

基本上,JavaScript引擎将沿着原型链进行工作,直到找到每个原型对象上要请求的属性或方法为止.设置的链条越靠上,意味着以后将覆盖其他项.

Basically the JavaScript engine will work its way back along the prototype chain, until it finds the property or method you are requesting on each prototype object. The further up the chain an item is set, will mean it will override the others later on down.

现在再问一个问题,如果再次触发以下内容会发生什么?

Now another question, what happens if you fire off the following again?

delete bob.personal_details

什么都没有,不再为bob分配一个名为 personal_details 的实际属性, delete 仅适用于当前对象,而不遵循原型链.

Nothing, there is no longer an actual property assigned to bob called personal_details, delete will only work on the current object and not follow down the prototype chain.

查看原型链如何工作的另一种方法基本上是想象一堆对象.当JavaScript扫描特定的属性或方法时,它将通过以下结构向下读取:

Another way of looking at how the prototype chain works is basically imagining a stack of objects. When JavaScript scans for a particular property or method it would read downwards through the following structure:

bob :          {  }
  person :     { name: 'dummy', personal_details: { age: 22 } }
    Object :   { toString: function(){ return '[Object object]'; } }

例如,假设我要访问 bob.toString . toString 是JavaScript基本对象 Object 上存在的方法,该对象几乎是所有对象的基本原型.当解释器获得对对象上特定方法或属性的读取请求时,它将遵循以下事件链:

So as an example, say I wanted to access bob.toString. toString is a method that exists on JavaScript's base object Object which is the base prototype for nearly everything. When the interpreter gets a read-request for a particular method or property on an object it will follow this chain of events:

  1. bob 是否有一个名为 toString 的属性?否.
  2. bob .__ proto __ ,即 person 是否具有称为 toString 的属性?否.
  3. bob .__ proto __.__ proto __ ,即 Object 是否具有名为 toString 的属性?
  4. 返回对 function(){的引用return'[Object object]';}
  1. Does bob have a property called toString? No.
  2. Does bob.__proto__ i.e. person have a property called toString? No.
  3. Does bob.__proto__.__proto__ i.e. Object have a property called toString? Yes
  4. Return the reference to function(){ return '[Object object]'; }

到达点4时,解释器将返回对Object上 toString 方法的引用.如果未在 Object 上找到该属性,则很可能会触发未定义属性的错误(因为它是链中的最后一个).

Once it reaches point 4 the interpreter will have returned the reference to the toString method found on Object. If the property hadn't been found on Object an error of undefined property would most likely have been fired (because it is the last in the chain).

现在,如果我们以前面的示例为例,这次在 bob 上定义一个 toString 方法-因此:

Now if we take the example before, and this time define a toString method on bob - so:

bob :          { toString: function(){ return '[Bob]'; } }
  person :     { name: 'dummy', personal_details: { age: 22 } }
    Object :   { toString: function(){ return '[Object object]'; } }

如果我们尝试再次读取bob上的 toString 方法,这次我们得到:

If we try and read-access the toString method on bob again, this time we get:

  1. bob 是否有一个名为 toString 的属性?是的.
  1. Does bob have a property called toString? Yes.

该过程在第一个障碍处停止,并从bob返回 toString 方法.这意味着 bob.toString()将返回 [Bob] ,而不是 [Object object] .

The process stops at the first hurdle and returns the toString method from bob. This means that bob.toString() will return [Bob] rather than [Object object].

正如Phant0m简洁指出的那样,对对象的写请求遵循不同的路径,并且永远不会沿着原型链向下移动.理解这一点是为了弄清什么是读请求,什么是写请求之间的区别.

As has been succinctly stated by Phant0m write requests on an object follow a different path and will never travel down the prototype chain. Understanding this is to work out the difference between what is a read, and what is a write request.

bob.toString                   --- is a read request
bob.toString = function(){}    --- is a write request
bob.personal_details           --- is a read request
bob.personal_details = {}      --- is a write request
bob.personal_details.age = 123 --- is a read request, then a write request.

最后一项是引起混乱的一项.并且该过程将遵循以下路线:

The last item is the one that is causing confusion. And the process would follow this route:

  1. bob 是否有一个名为 personal_details 的属性?否.
  2. person 是否有一个名为 personal_details 的属性?是的.
  3. 将引用返回为 {age:22} ,该引用以浮动方式存储在内存中.
  1. Does bob have a property called personal_details? No.
  2. Does person have a property called personal_details? Yes.
  3. Return the reference to { age: 22 } which is stored floating somewhere in memory.

现在开始新的过程,因为对象导航或赋值的每个部分都是对属性或方法的新请求.因此,现在我们有了我们的 personal_details 对象,我们将其切换到写入请求,因为左侧的属性或变量始终具有 = equals的赋值.

Now a new process is started because each part of an object navigation or assignment is a new request for a property or method. So, now we have our personal_details object we switch to a write request, because a property or variable on the left had side of an = equals is always an assignment.

  1. 将值 123 写入对象 {age:22}
  2. 上的属性 age

因此原始请求可以看作是这样的:

So the original request could be seen as something like this:

(bob.personal_details)            --- read
    (personal_details.age = 123)  --- write

如果 bob 拥有自己的 personal_details 属性,则过程将相同,但是写入的目标对象将不同.

If bob had owned its own property of personal_details the process would have been the same but the target object being written to would have been different.

按照您的问题提出答案:

Put along the lines of your question:

很难理解原型对象上的属性被视为READ_ONLY,但是如果属性是一个对象,那么人们就可以拥有它并可以自由地修改其属性!我的理解正确吗?

Its difficult to digest that properties on prototype objects are treated as READ_ONLY, but if property is an object then one can get hold of it and can freely modify its properties !! Is my understanding right ?

原型属性似乎是只读的,但是仅当作为继承它们的对象的属性直接访问时-因为这些属性实际上根本不在继承对象上存在.如果您浏览到原型对象本身,则可以将其视为任何普通对象(具有读写功能),因为它就是普通对象.最初可能会造成混淆,但这是性质或原型继承,而这完全取决于您如何访问要使用的属性.

Prototyped properties are seemingly read-only, but only when accessed directly as a property of the object that is inheriting them -- because those properties don't actually exist on the inheriting object at all. If you navigate down to the prototype object itself it can then be treated just as any normal object (with read and write) because that is exactly what it is — a normal object. It might be confusing initially, but this is the nature or prototype inheritance, it's all about how you access the properties you're working with.

这篇关于Javascript原型继承与对象属性阴影的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆