Web Development Design UK USA India, Web Software Applications Development Programming Web Development Design UK USA India, Web Software Applications Development Programming Contact website development and web design company, advanced portal development UK USA Site Map: Internet applications to discover your NET potential
 
   

JavaScript native property extensions -Common challenges and correct code implementation.
 
 
August 12 , 2013
Categories : Javascript| Javascript native property extensions | Javascript software development | Javascript development
Share : Facebook Tweet Linkedin
 

Extending JavaScript natives always causes debate with several developers bringing out the pitfalls that one could encounter while doing this. In this blog, we explore the primary areas of contention against this, and the subtleties of doing it correctly.

JavaScript types are typically constructors whose prototypes contain the methods (and properties) that define their default behavior:

--
//(results vary based on browser)
Object.getOwnPropertyNames(Function.prototype)
//["bind", "arguments", "length", "call", "name", "apply",
"constructor"]

--
Of course, you can't delete or replace a native prototype, but one can edit the values of its properties, or create new ones as below:
--

  //creating anarray method that removes a member
2 Array.prototype.removeMember = function(member) {
3 varindex = this.indexOf(member);
4 if(index > -1) {
5 this.splice(index, 1);
6 }
7 returnthis;
8 }
9  
10 ['CSS','jQuery','bootstrap'].removeMember('CSS'); //["jQuery", "boot
11  

--

As demonstrated, our code gets a helpful array extension for free. However as mentioned earlier, several developers would disapprove of this for different reasons. We’ll focus on some of the more important concerns raised:

Common Challenges

1. Future-proofing

If future browser versions (or mobile or tablet versions of browsers) implement Array.prototype.removeMember, their implementation will be overridden by our custom one. This could obviously result in a different, non standard outcome.

E.g. The Prototype.js framework implemented Function.prototype.bind. Several years later, the Ecma-262 committee includedFunction.prototype.bind in their ES 5 specification. Unfortunately for Prototype.js users, the brand new ES 5 standard required additional functionality, that was not supported by the elegantly simple Prototype.js version.

Similarly, software that utilizes third-party libraries run the danger that the native prototype augmentation (home grown) could possibly be clobbered by an alternate implementation of exactly the same property by another library.

These concerns may be partially mitigated by checking for the existence of a native property before implementing it:

Array.prototype.removeMember = Array.prototype.removeMember
|| function(member)
{
varindex = this.indexOf(member);
if(index > -1) {
this.splice(index, 1);
}
returnthis;
}

This solution is dependent upon simultaneous adoption of new functionality across browsers. If a particular browser implemented Array.prototype.removeMember first, then other browsers would still fall back on the home grown implementation which could do something entirely different. For this reason Prototype.js would have a problem with this strategy: since Array.prototype.bind is not implemented in some older browsers, those browsers would fall back on Prototype.js's more limited functionality.

2. The ‘for in’ loop

Another common concern is that extending natives messes with the object iteration cycle. Essentially, since ‘for in’ loops will visit all enumerable properties in the object's prototype chain, custom native properties will unexpectedly be part of such iterations.

Object.prototype.values = function()
{
//function specifics
}
varcompetitors = [];
varresults = {'Pegasus':'1','Accentyre':'2', 'IBMi':'3'};
for(varprop inresults) {
competitors[competitors.length] = prop;
}
competitors; //["Pegasus", "Accentyre", "IBMi", “values”]!

However, in such cases, the hasOwnProperty method can be used to filter inherited properties.

varcompetitors = [];
varresults = {'Pegasus':'1','Accentyre':'2', 'IBMi':'3'};
for(varprop inresults) {
results.hasOwnProperty(prop) && competitors.push(prop);
}
competitors; //["Pegasus", "Accentyre", "IBMi”]

Alternately, ES 5 allows properties to be designated as “non-enumerable”, which can be used to make them immune from for in iteration:

3 //does not support first generation browsers
4 Object.defineProperty(
5 Object.prototype, 'values', {enumerable: false});
6  
7 varcompetitors = [];
8 varresults = {'Pegasus':'1','Accentyre':'2', 'IBMi':'3'};
9 for(varprop inresults) {
10 competitors[competitors.length] = prop;
11 }
   
  competitors; //["Pegasus", "Accentyre", "IBMi”]

3. Shadowing

With regards to extending Object.prototype (as against native objects in general) there's another reason to be wary. Descendants of Object.prototype will miss usage of the extended property if another property gets defined with exactly the same name. So each time we define a property on Object.prototype, we are effectively generating a new reserved term. So Object.prototype extensions are not recommended by senior developers.

Correct Implementation

To extend natives, one must plan for each of the above concerns, and decide whether the extension would add power and clarity to the codebase.

Code Shims

Code shims present an excellent case for extending natives. A shim is a piece of code built to reconcile differences across environments, by supplying missing implementations. ES 5 support is patchy in older browsers, which is often frustrating for developers who would like to make the most of the newest ES 5 features, in addition to supporting older browsers. Here's a popular shim that demonstrates how this can be used:

1 if(!Array.prototype.forEach) {
2 Array.prototype.forEach = functionforEach(fun /*, thisp*/) {
3 varself = toObject(this),
4 thisp = arguments[1],
5 i = -1,
6 length = self.length >>> 0;
7  
8 if(_toString(fun) != '[object Function]') {
9 thrownewTypeError();
10 }
11  
12 while(++i < length) {
13 if(i inself) {
14 fun.call(thisp, self[i], i, self);
15 }
16 }
17 };
18  
19  
20  

The initial statement checks if Array.prototype.forEach is implemented and bails if it is. All properties included with native prototypes are defined by the ES 5 standard so its safe to assume they'll not collide with unrelated namesake properties later; no ES 5 property extends Object.prototype so pollution of for in enumerations will not occur; every ES 5 property is well documented so there's no basis for ambiguity as to how the shim must be implemented and it's clear which names are effectively reserved by the ES 5 standard.

Shimming ES 5 extensions makes plenty of sense. Without them we're hostage to the inadequacies of lesser browsers and unable to make the most of the language's standard utility set. Yes, we could utilize the same functionality offered by well-built libraries like underscore.js, but nonetheless we're locked into non-standard, inverted signatures where methods are static and objects are merely extra arguments.

Sandboxing

By Sandboxing, we are able to have our personal private array, string or function object that we could extend and use as required, without complicating the global version. There are many approaches for creating sandboxed natives, but the most browser-neutral one uses an IFRAME:

1 //Building a rough version as a demo
2  
3 varsb, iframe = document.createElement('IFRAME');
4 document.body.appendChild(iframe);
5 sb = window.frames[1];
6  
7 //later in code base...
8 sb.Array.prototype.removeMember = function(member) {
9 varindex = this.indexOf(member);
10 if(index > -1) {
11 this.splice(index, 1);
12 }
13 returnthis;
14 }
15  
16 //further ahead in code base...
17 vararr = newsb.Array('Pegasus', 'Accentyre', 'IBMi');
18 arr.removeMember(‘IMBi’);
19 arr; //['Pegasus', 'Accentyre']
20  
21 //global array is untouched
22 Array.prototype.remove; //undefined

Sandboxed natives, when written well, offer safe cross-browser replications of native extensions. They're a good compromise but a compromise just the same. After all the power of prototoype extensions is in their ability to change all instances of a certain type and provide all of them with access to exactly the same behavior set. With sandboxing we are required to understand which of our array instances are “super-arrays” and which are native.

Conclusion

Not augmenting native prototypes is like keeping your old car untouched, and never opening the hood yourself. Yes, there are precautions to be taken when working on this, but there are situations where its safe, and advantageous to do the required modifications.

Prototype.js and Mootools didn't break the Internet. Many great JavaScript projects were built on the shoulders on these frameworks and Prototype's pioneering extensions created the paths which ES 5 subsequently paved to the advantage the whole community.

Native extensions are neither right or wrong; there's more grey than black-and-white. The most effective way do is to make informed decisions and weigh each case on its merits.

 
About The Author

Mitul Dave

View My Profile



To know more about us, our services and solutions, drop us a message or mail us at info@pegasusinfocorp.com
 
The Author
 
Mitul Dave
 
 
Quick Links
 
Home Home
Services Services
Solutions Solutions
Contact Us Contact Us
Sitemap Sitemap
 
 
 
© 2024, Pegasus InfoCorp Pvt. Ltd. All Rights Reserved.