Extending Natives

  1. Modifying native prototypes
  2. Inheriting from native objects
  3. Method borrowing
  4. Summary

Native JavaScript objects store their methods in prototypes.

For example, When a new object is created, it doesn’t have anything. Then how would toString work?

var obj = { }
alert( obj.toString() )

That’s because obj = {} is a short for obj = new Object, where Object is a native JavaScript constructor function.

..And the object, made by new Object naturally receives object.__proto__ == Object.prototype, where Object.prototype is a native object with Object.prototype.toString property (and not only).

So, all properties of Object.prototype become available in objects. That’s why all objects have toString method.

Same story with Array, Function and other objects. Their methods are in Array.prototype, Function.prototype etc.

When an object property is accessed, the interpreter searches it:

  1. In the object itself,
  2. Then in it’s __proto__,
  3. Then in it’s __proto__.__proto__ etc.

The chain is followed until the property is found or the next __proto__ == null.

The only object with __proto__ = null is Object.prototype. All chains eventually end at Object.prototype or, in other words, all objects inherit from Object.

Primitive values like 5 are implicitly converted to objects when a method is called, and the result is again a primitive.

Modifying native prototypes

Native prototypes can be modified. New methods can be introduced.

We could write a method to repeat a string many times (read [here](#new Array) about new Array syntax):

String.prototype.repeat = function(times) {
  return new Array(times+1).join(this)
}

alert( "a".repeat(3) ) // aaa

Same way we could add a method Object.prototype.each(func) to apply func to every property.

Object.prototype.each = function(f) {
  for(var prop in this) {
    var value = this[prop]
    f.call(value, prop, value) // calls f(prop, value), this=value
  }
}

// Try it: (works wrong!)  
var obj = { name: 'John', age: 25 }
obj.each(function(prop, val) {  
  alert(prop) // name -> age -> (!) each
})

As you can see from the comments, the code is wrong. It outputs extra each property, because for..in walks the prototype.

Fortunately, this functionality can be fixed if we add hasOwnProperty check to for..in.

Object.prototype.each = function(f) {
  for(var prop in this) {
*!*
    if (Object.prototype.hasOwnProperty(prop)) continue // filter
*/!*
    var value = this[prop]
    f.call(value, prop, value) 
  }
}

// Now correct
var obj = { name: 'John', age: 25 }
obj.each(function(prop, val) {  
  alert(prop) // name -> age 
})

In this case it worked, now the code is correct. But generally we don’t want to put hasOwnProperty in every loop…

New properties of Object.prototype appear in all for..in loops. Don’t add them.

Native properties and `for..in`

Built-in properties and methods are not listed in for..in, because they have a special [[Enumerable]] flag set to false.

In browsers which support modern JavaScript (IE from version 9), this flag can be set for custom properties too.

For other objects, which are not iterated in for..in loops (Strings, Functions etc), there are pro and contra against adding new methods:

  • New methods allow to write shorter and cleaner code.
    Compare "a".repeat(5) and str_repeat("a", 5). The first looks better for most people.
  • New properties may conflict. Imagine different people writing same named method in their libraries. Integration may be hard.

    Prototypes are global for all your code, and modifying them is architecturally bad for same reason as introducing new global variables.

  • But that’s in theory. In practice, there is a well-known library Prototype.JS which extends prototypes without terrible consequences Smile

Modifying built-in prototypes is generally considered bad, but may be useful to implement modern ECMA-262 5th edition methods in older browsers.

For example, if there is no Object.create, then add it:

if (!Object.create) {
  Object.create = function(proto) {
    function F() {}
    F.prototype = proto
    return new F
  }
}

Inheriting from native objects

Native objects can be inherited. For example, Array.prototype keeps all methods for new Array instances.

If we want these methods be accessible in our myArray, then myArray.__proto__ == Array.prototype does the trick.

Let’s build a function which creates such objects:

// constructor
function MyArray() { 
  this.stringify = function() {
    return this.join(', ')
  }
}
// Make sure that (new MyArray) has the right __proto__ 
MyArray.prototype = Array.prototype

// Test
var myArr = new MyArray()
myArr.push(1)
myArr.push(2)
alert( myArr.stringify() ) // 1, 2
alert( myArr.length ) // 2 in all except IE

That works great in all browsers except IE, which doesn’t autoupdate the length property.

Method borrowing

If you want to use a piece of Array functionality, it is not required to inherit it.

Instead, you can borrow a method, and then apply it without inhereting:

var join = Array.prototype.join 
// same but shorter
var join = [].join

.. And call it with a non-standard this:

var obj = {
  0: 'first',
  1: 'second',
  length: 2
}

alert( [].join.call(obj, ', ') ) // first, second

Method Array.prototype.join is described in the specification ES-5, p.15.4.4.5. It doesn’t check for object type. All it does is a joining loop over properties with indicies 0..length-1, so here it works fine.

Array methods are often borrowed for manipulating array-like objects.

Summary

  • Methods of native objects are stored in their prototypes.
  • Native prototypes can be inherited or extended.
  • Modifying top-level Object.prototype breaks for..in loops. Other prototypes are less dangerous to modify, but most developers don’t recommend it.
    The notable exception is adding a support for modern methods for older browsers, like Object.create, Object.keys, Function.prototype.bind and others.

Tutorial

Donate

Donate to this project