JavaScript Delete() Only Affects The Referenced Object Regardless Of Prototype Chain

Posted June 2, 2011 at 10:45 AM by Ben Nadel

Tags: Javascript / DHTML

Yesterday, I blogged about extending the window (global) object in JavaScript. In that post, I used the delete() function (or keyword if you like) to alter various parts of the resulting prototype chain in order to create a dynamic runtime environment. That approach was possible because of a feature that I learned from Cody Lindley: the delete() function only affects the referenced object, regardless of the prototype chain that contains said object. Unlike property references, which will travel up the prototype chain looking for values, a call to the delete() function will only affect the given reference.

To see this behavior in action, take a look at the following demo. We're going to create a prototype chain that has a "name" property defined on several objects within that prototype chain. Notice how calls to delete() interact with subsequent property references:

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Delete() Only Affects Referenced Object In Prototype Chain</title>
  • <script type="text/javascript">
  •  
  •  
  • // Create a "super" object that another object will extend
  • // (via the prototype chain).
  • var superObject = {
  • name: "Super Object"
  • };
  •  
  •  
  • // Create an object that extends the super object.
  • myObject = Object.create( superObject );
  •  
  •  
  • // Check to see what the current name value is.
  • console.log( "1 - Name:", myObject.name );
  •  
  •  
  •  
  • // Now, overwrite the name value - this will store the name
  • // property in the lowerst object in the prototype chain
  • // (myObject).
  • myObject.name = "My Object";
  •  
  • // Check to see what the current name value is.
  • console.log( "2 - Name:", myObject.name );
  •  
  •  
  •  
  • // Now, delete the name property.
  • delete( myObject.name );
  •  
  • // Check to see what the current name value is.
  • console.log( "3 - Name:", myObject.name );
  •  
  •  
  •  
  • // The previous delete removed the "name" property from the
  • // lowest object in the prototype chain (the one referenced
  • // by the myObject variable). Now, try to call delete again.
  • delete( myObject.name );
  •  
  • // Check to see what the current name value is.
  • console.log( "4 - Name:", myObject.name );
  •  
  •  
  •  
  • // Now, delete the name from the super object reference.
  • delete( superObject.name );
  •  
  • // Check to see what the current name value is.
  • console.log( "5 - Name:", myObject.name );
  •  
  •  
  • </script>
  • </head>
  • <body>
  • <!-- Left intentionally blank. -->
  • </body>
  • </html>

As you can see, we getting and setting the name property on the various object within this prototype chain. When we run the above code, we get the following console output:

1 - Name: Super Object
2 - Name: My Object
3 - Name: Super Object
4 - Name: Super Object
5 - Name: undefined

Really, the loggings of interest in the above output are line items 3 and 4. In 3, we had just called delete() on myObject. This removed the "name" property from the myObject instance, leaving the "name" property on the superObject instance intact. As such, a reference to the name property in 3 resulted in "Super Object."

In line item 4, we had just called delete() again on the myObject instance. A subsequent reference to the "name" property, however, resulted in "Super Object." As you can see, multiple invokations of delete() on the myObject reference do not alter "name" properties located higher up in the prototype chain. In fact, it is only after we call delete() directly on the superObject instance that we are able to completely remove the "name" property from the object (as an abstract whole).

This behavior leaves us in an interesting position: on the one hand, property references will travel up the prototype chain looking for values; but, on the other hand, property deletes will only affect the directly referenced object. As such, it's not hard to imagine someone implementing this kind of logic and getting unexpected results:

  • if (someObject.property){
  •  
  • delete( someObject.property );
  •  
  • }

If the containing algorithm expected the delete() to change the outcome of subsequent calls to the same piece of control flow, the structure of the prototype chain could potentially throw a monkey wrench into the logic. Depending on what your code is doing, a good way to deal with this asymmetric traversal mechanism would be to use the Object.hasOwnProperty() method in conjunction with the property reference:

  • if (
  • someObject.hasOwnProperty( "property" ) &&
  • someObject.property
  • ){
  •  
  • delete( someObject.property );
  •  
  • }

This way, the delete() works at the given object level and the hasOwnProperty() method ensures that the property lookup will also work at the given object level.

This asymmetric prototype traversal behavior has never actually caused me any problems. Typically, I only use delete() in conjunction with objects that exist outside of any complex prototype chain (ie. object literals specifically designed for value indexing). But, understanding this JavaScript behavior will certainly make me a bit more cognizant about when it's a good idea to apply the hasOwnProperty() method.




Reader Comments

Jun 2, 2011 at 12:54 PM // reply »
273 Comments

@Ben,

How about:

  • function steroidsDelete(obj, prop)
  • {
  • if (prop in obj)
  • if (obj.hasOwnProperty(prop))
  • {
  • delete obj[prop];
  • // delete even shadowed props:
  • if (prop in obj)
  • steroidsDelete(obj, prop);
  • }
  • else
  • steroidsDelete(obj, prop);
  • }

That's off the top of my head. Probably syntactically incorrect. And apologies to those who are addicted to one-statement braces. And I don't think I'll ever figure out how to indent things right in your code blocks here. But you get the idea.

You don't want to do anything if the prop string isn't the name of a property somewhere in the prototype chain. If it is, however, that makes the rest of it a well-founded function. You'll eventually hit the delete.

Sorry if steroids is a touchy prefix for a bodybuilder. :-)


Jun 2, 2011 at 1:01 PM // reply »
11,314 Comments

@WebManWalking,

I haven't run this, but I think this may throw you into an infinite loop. Unless you can get access to the object's prototype reference, it doesn't look like you'll ever be able to progress up the prototype chain. I think you would need to do something like this (pseudo code):

  • function deleteProp( object , prop ){
  •  
  • delete( object[ prop ] );
  •  
  • // Check to see if property still detected.
  • if (prop in object){
  •  
  • // Move up the prototype chain and try again.
  • deleteProp( object.getPrototype(), prop );
  •  
  • }
  •  
  • }

Of course, there is no function "getPrototype()" and things like __proto__ are not cross-browser compatible. I have seen, however, in some of the MVC frameworks, that they actually add a prototype feature into the object properties during class creation.


Jun 2, 2011 at 1:14 PM // reply »
273 Comments

@Ben, you're right. I forgot to move up the prototype chain, which would have required a lot of cross-browser feature sniffing. Thanks for the correction.

You got the idea, though. That's the main thing.


Jun 2, 2011 at 1:23 PM // reply »
11,314 Comments

@WebManWalking,

No problem, I figured that was what you meant. It would be cool if gaining access to the prototype chain was a bit easier in general.


Jun 2, 2011 at 3:02 PM // reply »
273 Comments

@Ben,

From the ECMAScript version 5 spec:

15.2.3.2 Object.getPrototypeOf ( O )

When the getPrototypeOf function is called with argument O, the following steps are taken:

1. If Type(O) is not Object throw a TypeError exception.

2. Return the value of the [[Prototype]] internal property of O.

Here's the document:

http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf

I just now tried the following in the Big Five browsers:

javascript:alert(Object.getPrototypeOf(Object.create({name:"val"})));

The inner Object.create assured that I was referencing an object that had a prototype. Here were the results:

(1) Firefox 4.0.1 - alert with [object Object]
(2) Google Chrome 12.0 - alert with [object Object]
(3) MSIE 7.0 - threw an error (Object doesn't support this property or method), of course
(4) Opera 11.11 - didn't do anything (probably threw an erro, but I don't have Dragonfly installed there yet)
(5) Safari 5.0.5 - alert with [object Object]

So we're almost there.


Jun 2, 2011 at 3:10 PM // reply »
273 Comments

P.S.: I can't upgrade my MSIE because version 7 is the standard for [non-public-facing] intranet pages, but I'd be very curious about getPrototypeOf support in versions 8 and 9.


Jun 2, 2011 at 3:12 PM // reply »
273 Comments

The standard where I work, that is.


Jun 2, 2011 at 5:16 PM // reply »
11,314 Comments

@WebManWalking,

Very cool. I've never even heard of the Object.getPrototypeOf() method. We're definitely getting closer. Unfortunately, I'm not sure how easy it would be to fill in the gaps in the older browsers.

In the end, though, it's probably a good thing that delete() doesn't crawl up the prototype chain. If it did, it would probably lead to more problems than if it didn't :) I'll leave that kind of philosophy up to the smarter people who come up with standards.


Jun 2, 2011 at 7:56 PM // reply »
273 Comments

@Ben,

Yes, steroidsDelete(myArray, "push") might have repercussions elsewhere in your code. :-)

It's fun to wander around and find new stuff in the ECMA documentation. (Aside: I said version 5. It's technically edition 5.) One of the related things I saw was that, although getting a value walks the prototype chain, setting a value does not. That's exactly analogous to delete not walking the prototype chain.

Whenever I find something useful in the ECMA PDF, I'm immediately off to do location bar experiments in the Big Five.


Jun 3, 2011 at 3:04 PM // reply »
11,314 Comments

@WebManWalking,

Yeah, setting a value always sets into the lowest object in the prototype chain. However, you have to take special caution with things like Objects and Arrays. If you have Object, "someObject", higher up in the prototype chain:

myInstance.someObject.key = value;

... puts "key" into the prototype-level someObject. Meaning, that change will be replicated in all objects that extend that level of prototype. Since a the "set" is not on the instance itself, but rather on a property of that instance, the prototype structure doesn't dictate behavior.

That's messed me up a few times :) That's when I learned that calling super-constructors is absolutely critical:

http://www.bennadel.com/blog/1566-Using-Super-Constructors-Is-Critical-In-Prototypal-Inheritance-In-Javascript.htm

JavaScript is some fun stuff! Always a good mental exercise.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Jun 18, 2013 at 9:20 PM
Mapping AngularJS Routes Onto URL Parameters And Client-Side Events
I couldn't find examples of passing multiple arguments using the when() routing statement so figured out through trial and error that you can pass multiple arguments using the following format: .whe ... read »
Jun 18, 2013 at 3:39 PM
Experimenting With The Amazon Simple Storage Service (S3) API Using ColdFusion
Hi Ben, THANKS! While not bleeding edge, it is new to me & I like learning new things every day! ... read »
Jun 18, 2013 at 12:30 PM
Disabling Auto-Correct And Auto-Capitalize Features On iPhone Inputs
Also spellcheck="false" should be mentioned as part of html5 specs ... read »
Jun 18, 2013 at 8:40 AM
Using Named Functions Within Self-Executing Function Blocks In Javascript
Hi Ben, you forgot to mention the most important thing for named self-executing functions - they can be referenced by name ONLY inside their execution context (which is parens in this case), it mean ... read »
dee
Jun 18, 2013 at 7:01 AM
My Safari Browser SQLite Database Hello World Example
hai ben, this program is really good i could understand the concept but i dint know how to save it and how to open it as you have done in the video can u give that details pls ... read »
Jun 18, 2013 at 6:04 AM
Clearing Inline CSS Properties With jQuery
Thanks a lot for for post! It helped me a lot... after being stuck since 24 hrs.. found solution from your post. Thanks again! ... read »
Jun 18, 2013 at 2:31 AM
SOTR 2013 - The Best Conference I Never Went To
I keep watching it, should keep me happily distracted until SotR14 ;) ... read »
Jun 17, 2013 at 9:45 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, As I was reading what you wrote, it occurred to me that maybe I do something similar to that in some of my client-side code. In an application I'm working on, there are a bunch of unrelated ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools