Host Methods vs Native Methods

Introduction

This was intended as a short and overly simplified post about Host methods and Native methods.

What they are?

Native methods are built-in functions provided by the ECMAScript core specification. So things like Object methods (e.g. Object.create), Array methods (e.g. Array#forEach) etc.

Host methods are functions provided by the host environment (most of the time when working in web development the host environment will be the user’s web browser). So things like the DOM API and the Events object are host objects/methods (e.g. attachEvent is a host method and addEventListener is a host method)

How to detect them?

Detecting Native methods is relatively straight forward. The real problem comes when you need to determine whether the object/method you’re detecting actually works the way the specification dictates it should work. So just checking it is available to use isn’t good enough.

Detecting host methods is similar but a lot more problematic, because the ECMAScript specification states that the host environment can implement certain methods however they like and so there is no guarantee that your checks for certain host methods (which may work today) will work in future.

We’ll give an example of each so you can get an idea of what I mean…

To detect a Native method such as Array#forEach you should be able to do the following:

if (!Array.prototype.forEach) { 
    /* polyfill for missing forEach method */ 
}

Note: polyfill is a term that Remy Sharp coined which means ‘a shim that mimics a future API’ (see: http://remysharp.com/2010/10/08/what-is-a-polyfill/)

But the issue you could encounter in this example is if you’re inheriting a project from another developer and they have already extended the Native Array object with a forEach method and their polyfill version of the missing forEach function doesn’t work how the specification has dictated it should then you could find your code errors at hard to debug stages because of the difference in implementation where you’re passing parameters into a polyfill’ed method and that method hasn’t been implemented properly so the extra parameters either throw an error or (potentially worse) silently fail.

This is where you either ‘suck it and see’ (which is a bad idea, but not always unavoidable), or you attempt genuine ‘feature detection’ which means (in this example) you create a test Array and test the forEach method works how you expect it to.

The downsides to this approach (although it is the most robust and future-proof way of writing your code) is that all these checks are a performance penalty. If you’re sure the method you’re checking for is going to work how you expect it to then should you waste time/effort writing additional checks/tests to ensure the method works how the specification dictates? What happens if you do the full feature detection and discover the method doesn’t work how you expect it? You’ll still then need to implement some kind of fallback or lose the functionality that relies on that method.

These are important decisions that need to be made and ones that are outside the realms of this post I’m afraid (simply because there are no easy answers).

Now, detecting Host methods is actually worse because they can be implemented in any fashion the host environment chooses.

So far it has been noted that checking the typeof result for a Host method will normally result in either function, object or unknown, so if you get one of these back as a result then it’s a good chance the host object you’re checking for is available to use, but as you should be able to tell by now, this is a flawed process… fun heh!

Again, this isn’t a reliable assumption to make, because in a future/new host environment they might have a typeof result that is none of the above. Literally you could check the typeof for a method and its result could be spacecraft - there are no rules as far as the Host environment is concerned!

But for testing a host method exists, the following function has become the de-facto standard:

/*
 * Feature Testing a Host Method
 * Because a callable host object can legitimately have any typeof result then it can't be relied upon.
 *
 * @notes:
 * The reason for the && !!object[property] is because in ECMAScript version 3, 
 * a null object has typeof result 'object' (which is considered a bug).
 * In future versions (ECMAScript 6+) the typeof result will be 'null' (as it should be).
 * 
 * @reference: http://michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
 */

function isHostMethod(object, property) {
    var type = typeof object[property];

    return type == 'function' || // This is the result we're expecting (as the test is for a method)
           (type == 'object' && !!object[property]) || // Protect against ES3 'null' typeof result being 'object'
           type == 'unknown'; // For IE < 9 when Microsoft used ActiveX objects for Native Functions (we're checking property of ActiveX object)
}

So lets take a quick re-cap of what’s going on here:

When is it OK to modify them?

Modifying built-in Native objects isn’t as dangerous as host objects (as already noted by Kangax http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/) but care needs to be taken to ensure the augmented object works as the spec dictates (something that isn’t possible all the time, for example like with Object.create).

As far as host objects are concerned, never ever ever ever modify or augment them, just too dangerous.


Links