My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Sunday, August 10, 2014

Auto instance via "use strict"

This is a very simple trick that works if you are sure your minifier will not remove any 'use strict' declarations in your scripts.

As seen in jQuery

This library is the most known/common example where $(something) will actually generate an instanceof jQuery variable even without using new.

Some background

Since basically ever, JavaScript constructors such Array, Object, Function and RegExp do not distinguish between new Array/Object/Function/RegExp or just the same without new, and there are exceptions for Date, where it always returns the same instance if invoked without new, and primitives wrappers such Boolean, Number, or String, where the meaning is very different: it's casting VS value wrapper when new is used.
This behavior could be somehow desired when we'd like to obtain the exact same return type when a generic constructor is invoked either via new or not.

Handy "use strict"

Because of its guard against the execution context, where this would be undefined instead of the global window object, we can use such behavior to simplify the famous pattern.
// basic use strict feature
function Constructor() {'use strict';
  return this || new Constructor;
}
This is basically the easiest way to replicate this pre-use-strict era pattern:
function Constructor() {
  return this instanceof Constructor ?
    this : new Constructor
  ;
}
The benchmark shows no impact on Safari, but quite a gap between the old and the new pattern in Chrome. Good news is, who cares, the trick is to write less and reduce code size so if it's even faster, it's just a strawberry on top.

A different behavior

In case somebody will use Constructor.call(anotherObject) the instance will be used inside the constructor instead of being ignored via good old pattern.
This is actually not necessarily an undesired side effect because it actually lets us reuse constructors to initialize properties with mixins too, in case the initializzation is needed.
// old pattern ...
function Constructor(value) {
  if (!(this instanceof Constructor)) {
    return new Constructor(value);
  }
  this.property = value;
}

// later on ...
var mixedObject = {};
Constructor.call(mixedObject, 123);
mixedObject.property; // undefined
Using initial pattern we basically do nothing except creating a new instance that will be lost in the process while with the pattern suggested via use strict things play nicer than before!
// use strict
function Constructor(value) {'use strict';
  if (!this) return new Constructor(value);
  this.property = value;
}

// later on ...
var mixedObject = {};
Constructor.call(mixedObject, 123);
mixedObject.property; // 123 <=== yeah!

No backward compatible!

As easy as that, in order to be able to use this pattern the engine must be compatible with use strict directive, which is actually not a big deal in nodejs or other modern server side engines, as well as most common browsers targeted for mobile apps or extensions.
In case you don't know about some target, do you remember the trick to know if the JS engine is compatible?
var hasUseStrict = (function(){'use strict';return!this}());
Please note that use strict brings other features too so be sure other behaviors won't affect your code logic.

Wait ... what about arguments?

There are two scenarios here, the one when it does not matter:
// use strict
function Constructor(a, b, c) {'use strict';
  if (!this) return new Constructor(a, b, c);
  // go on ... 
}
and the one where exact number of arguments matter:
// use strict
function Constructor(a, b, c) {'use strict';
  if (!this) {
    return Constructor.apply(
      Object.create(Constructor.prototype),
      arguments
    );
  }
  // go on ... 
}
Take care

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.