I Object

By YUI TeamMay 10, 2007
One of the two really clever ideas in JavaScript is that objects are dynamic collections with differential inheritance. Differential inheritance means that when object B inherits from object A, object B does not have to contain a copy of all of object A's stuff. Object B only needs to contain the differences. This saves time and memory, and it allows the augmenting of an object system after the objects have been instantiated. That can be really useful in Ajax applications which have to deal with variable network latency. Unfortunately, JavaScript got the details wrong. As we saw in for in Intrigue, the for statement can interact badly with methods supplied by the prototype. A little bit of care is required to make for statements work correctly. Similar care is also required when using objects as general collections. Since methods act like any other member, you can sometimes get false positives from inherited methods. For example, let's say we have an application which will look at the words in a text, and that we will take some sort of action if a word matches a word on our list. Using the string split method, we can easily transform a text into an array of words. And we can use an object to make our list of special words. var myList = { hope: true, springs: true, eternal: true }; We then loop over the array of words, and on each iteration we will match a word against the list. if (myList[arrayOfWords[i]]) { // A match! } So we won't match "construct". But we will match "hope". Great. Our code works. Or does it? This code will also match "constructor" even though "constructor" is not in our list. Why is this? It is because every object has "construct" member. So we get a false positive. Any member anywhere in the prototype chain can cause a false positive. So how can we fix this? JavaScript's in operator is useless because it produces the same false positive. We could take advantage of the fact that all of the values in myList are true by doing exact comparisons. if (myList[arrayOfWords[i]] === true) { // A match! } That works. But suppose we want myList's values to be functions which will be called when a key matches. In that case, we can't look for a known value, and if (typeof myList[arrayOfWords[i]] === 'function') { // A match! } won't work because myList.constructor is also a function. What we can do is explicitly reject keys that come from the prototype chain. var word = arrayOfWords[i]; if (myList.hasOwnProperty(word) && myList[word]) { // A match! } So the solution here is the same solution required to make for statements work correctly: the hasOwnProperty method. If we use it to guard against unintended inheritance, then we can safely use JavaScript's objects as general containers. We can make JavaScript work correctly at the cost of a bit of ugliness in our code. It is a nuisance, it is irritating, and it feels like a waste of time. You could also say the same thing about washing your hands after using the restroom. Do it anyway. Good hygiene is good for you. And your code.