Showing posts with label Ninjstsu. Show all posts
Showing posts with label Ninjstsu. Show all posts

Saturday, September 16, 2017

Sneaky JavaScript Technics IV

A ninja is a lazy fighter. As Sun Tzu stated "Every battle is won or lost before it's ever fought". Here are quick tips to simplify your development when dealing with optional parameters.

Handling default parameters

The are many way to default parameters when they are not passed during function invocation.

ES6 proposes a syntax to formalize them, you can try it by yourself.

The following example: function test (firstParam = "default value") { return firstParam; }

is transpiled into: "use strict"; function test() { var firstParam = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "default value"; return firstParam; }

Indeed, the arguments object contains the list of parameters that were passed during function invocation. It looks like an array (but it is not) and exposes length as well as all passed parameters.

This is quite complex and I usually go with:

function test (firstParam) { if (firstParam === undefined) { firstParam = "default value" } return firstParam; }

Or, shorter,

function test (firstParam) { firstParam = firstParam || "default value"; return firstParam; }

This last syntax may lead to errors when dealing with falsy values

All these non ES6 syntaxes are working fine but they come with two drawbacks:

  • You have to manually handle the missing parameters
  • The condition adds cyclomatic complexity to your function

The lazy solution

Wouldn't it be nice to have a way to wrap a function so that default parameters would be handled without having to code anything?

Considering that optional parameters are usually at the end of a function, let's introduce a new method to the function object that will default its last parameters.

So considering this function: function add(value, increment) { return value + increment; }

We could define inc as: var inc = add.withDefaults(1);

That would be equivalent to the ES6 version of: function inc(value, increment = 1) { return add(value, increment); }

Forewords

Before going straight to the proposal, you need to understand the following concepts:

  • A function exposes the size of its signature through the property length: it allows any developer to know the number of expected parameters
  • The arguments object is not an array but it can be easily converted into one using the following pattern: [].slice.call(arguments)
  • You can create an array of any size using new Array(size) It will be filled with undefined values
  • If the provided default parameters are not enough to set missing ones, they are replaced with undefined (as expected)

Now you are ready.

Proposal

Here is the proposal to handle default values:

Function.prototype.withDefaults = function () { var defaultParameters = [].slice.call(arguments), wrappedFunction = this; return function () { var receivedParameters = [].slice.call(arguments), missingCount = wrappedFunction.length - receivedParameters.length, actualParameters, sliceFrom; if (missingCount > 0) { sliceFrom = defaultParameters.length - missingCount; actualParameters = receivedParameters .concat( new Array(Math.max(-sliceFrom, 0)), defaultParameters.slice(Math.max(sliceFrom, 0)) ); } else { actualParameters = receivedParameters; } return wrappedFunction.apply(this, actualParameters); } }

As well as the associated test case.

Improvements

This solution is not perfect. Indeed, the resulting function has a signature length of 0. Consequently, you can't chain with another call to default parameters of such a wrapped function.

There are several ways to work around this limitation but I wanted to keep this article short and simple

Thursday, March 30, 2017

Sneaky JavaScript Technics III

A ninja is a master of disguise. How do you apply the JavaScript ninjutsu to hide an object member so that nobody can see (and use) it? Here are several ways.

The context

These days, I started a new task on my gpf-js project and it implies manipulating an AST structure within NodeJS. Because the structure analysis requires to have access to the parent nodes, I augmented the AST items with a link to their parent. The result is later serialized into JSON for tracing purpose but the generated circular reference broke the conversion.

Actually, I could have used the replacer parameter of the JSON.stringify method but this circular reference also caused trouble in my own code. Hence I had to find another way.

Reflection

The JavaScript language offers some reflection mechanisms. Indeed, the for..in syntax is capable of listing all enumerable properties of any object.

I would recommend using Object.keys instead. However, the lack of support of old browsers requires that you polyfilled it with for..in combined with hasOwnProperty .

This fundamental machinery is widely used:

  • ESlint offers a rule to detect unsafe uses
  • JavaScript evolved to make the enumerable state configurable on a property

Object.defineProperty

The simplest way to add a property to an object is to simply do it: var obj = {}; obj.newProperty = "value";

This newly created property will be enumerable by default.

ECMAScript 5.1 introduced Object.defineProperty to create a property with options. This feature is implemented by most recent browsers with some limitations when using it on DOM objects.

It can be used in different ways and most of them are beyond the scope of this article. I will mostly focus on the possibility to create a non-enumerable property:

var obj = {}; Object.defineProperty(obj, "newProperty", { value: "value", writable: true, enumerable: false, configurable: false });

By setting enumerable to false, this property will not be enumerated when using the for..in syntax. By setting configurable to false, this property can't be deleted and can't be reconfigured to make it visible again.

The advantages are:

  • It is easy to implement
  • It is available since IE9

But, regarding the initial purpose, it comes with drawbacks:

To make the task harder for a hacker to figure out the property name, you may generate a complex random name and store it in a 'private' variable.

Symbol

Another simple way to add a property to an object is to use the square bracket syntax: var obj = {}; var newPropertyName = "newProperty"; obj[newPropertyName] = "value";

In that example, the newly created property will be named "newProperty" as per the value of the variable. The only difference with the dot syntax is the use of a variable to name the property.

But what if the variable is not a string?

For most standard objects (and primitive types), the variable value is converted to string. Consequently, the following syntax is valid (even if it does not make sense): var obj = {}; function any () {} obj[any] = "value";

The created property name will be "function any() {}"

This obviously means that you can use names that are not valid identifiers. Hence, it is mandatory to use the bracket syntax to access them.

However, there is one mechanism that behaves differently. It was introduced with ECMAScript 2015. Every time you call the Symbol function, it returns a unique value. This value type is primitive but it does not convert to string.

This feature is implemented only by most recent browsers and not by IE.

The advantages are:

  • It is easy to implement
  • There is no way to access the property unless you have the symbol value

Wednesday, November 4, 2015

Sneaky JavaScript Technics II

How do you secure an asynchronous method to make sure that the code is executed only once? Here is an example using promises.

The context

I often talk about promises as a way to handle asynchronous calls. In this short article, I would like to illustrate an advanced technique that allows you to synchronize concurrent calls to an asynchronous API with promises.

Let consider the following API:

/** * Intialize the API. * WARNING: must be called only once! * * @param {Function} callback Will be triggered when the API is ready * it receives a dictionary exposing the api. */ function intializeApi (callback) { /*...*/ }

As a client of this API, you need to find a place in your code where you would call it this way:

// Intialize the API intializeApi(function (api) { // Use the API });

Handling concurrent calls

As the comment says, you can call this function only once. What happens if you develop several modules that requires this API? You have to wrap the function in a module that is loaded before any other and expose a method to secure the call.

(function (context) { "use strict"; /*global intializeApi*/ var _api; context.safeInitializeApi = function () { if (_api) { return Promise.resolve(_api); } return new Promise(function (resolve/*, reject*/) { intializeApi(function (api) { _api = api; resolve(api); }); }); }; }(this));

This sounds great: when the callback is triggered, it keeps track of the returned object and any subsequent call will use the cached result.

But...

What if the you consider the following code:

// module1 safeInitializeApi() .then(function (api) { // Use the API }); // module2 safeInitializeApi() .then(function (api) { // Use the API });

The function safeInitializeApi is called twice sequentially. Because the initializeApi is asynchronous, it won't have the possibility to execute the fulfillment handler (that sets _api) before the second call.

As a result, you still call the API twice.

Solution

The following part was based on an incorrect assumption. However, even if the proposed solution is useless (and I will explain the reason at the end of the article), is remains valid (it works). To understand where I come from, the

Here is a more sophisticated version of the safeInitializeApi:

(function (context) { "use strict"; /*global intializeApi*/ var _api, _pendingPromises; context.safeInitializeApi = function () { var deferred; if (_api) { return Promise.resolve(_api); } if (!_pendingPromises) { _pendingPromises = []; return intializeApi() .then(function (api) { _api = api; _pendingPromises.forEach(function (deferred) { deferred.resolve(api); }); return api; }); } // Build a new promise resolved upon API fulfillment deferred = {}; deferred.promise = new Promise(function (resolve, reject) { deferred.resolve = resolve; deferred.reject = reject; }); _pendingPromises.push(deferred); return deferred.promise; }; }(this));

The trick consists in creating new promises that will be resolved when the initial one is fulfilled. The array of promises (_pendingPromises) acts as a critical section to know if the method has already been called or not.

Handling errors

How do you handle errors?

Unlike fulfillment handlers that can be chained (.then() returns a promise), rejection handlers can't be. Hence, we need to create another promise to wrap the initial one and forward fulfillment or rejection on all pending promises.

This leads to this wrapper:

(function (context) { "use strict"; /*global intializeApi*/ var _api, _pendingPromises; context.safeInitializeApi = function () { var deferred; if (_api) { return Promise.resolve(_api); } deferred = {}; deferred.promise = new Promise(function (resolve, reject) { deferred.resolve = resolve; deferred.reject = reject; }); if (!_pendingPromises) { _pendingPromises = [deferred]; intializeApi() .then(function (api) { _api = api; _pendingPromises.forEach(function (deferred) { deferred.resolve(api); }); }) .catch(function (reason) { _pendingPromises.forEach(function (deferred) { deferred.reject(reason); }); }); } else { _pendingPromises.push(deferred); } return deferred.promise; }; }(this));

Final solution

If you want this trick to be reusable, here is a function that wraps any function or method to ensure that it can be called only once. This sample page will allow you to see it in action.

/*global intializeApi*/ function onceWrapper (callback) { "use strict"; var _result, _pendingPromises; return function () { var deferred; if (_result) { return Promise.resolve(_result); } deferred = {}; deferred.promise = new Promise(function (resolve, reject) { deferred.resolve = resolve; deferred.reject = reject; }); if (!_pendingPromises) { _pendingPromises = [deferred]; callback.apply(this, arguments) .then(function (result) { _result = result; _pendingPromises.forEach(function (deferred) { deferred.resolve(result); }); }) .catch(function (reason) { _pendingPromises.forEach(function (deferred) { deferred.reject(reason); }); }); } else { _pendingPromises.push(deferred); } return deferred.promise; }; } // Example of use var safeInitializeApi = onceWrapper(intializeApi);

Tuesday, February 3, 2015

Sneaky JavaScript Technics I

As a technical support, I had to deal with situations where a quick and dirty fix was necessary to get things done while waiting for the official patch.
I learned a lot by doing so and I would like to share some of these stories with you.

The context

Maybe you already know it, but a input type=hidden field does not generate any change event.

For some reasons, I had to detect when the value of one specific hidden field was changed in order to trigger the execution of a custom function. I had no way to change the code that was setting the value of the field so I had to find a solution that was field-centric.

A quick note before going any further: I don't pretend that any of the solutions below is the right thing to do but, as a matter of fact, I learned a lot by trying and doing mistakes. The key here is that I tried several ways to solve my problem and I finally kept the one that suited my needs at that moment (knowing that a more stable solution would come).

First attempt: active monitoring

My first thought was to monitor the field using a timer in order to check if the field changed.

(function () { "use strict"; var _fieldHandle = document.getElementById("hiddenFieldID"), lastValue = _fieldHandle.value; function actionToBeTriggered(/*value*/) { // What needs to be done when the value changed } function didTheHiddenFieldChanged() { if (_fieldHandle.value !== lastValue) { lastValue = _fieldHandle.value; actionToBeTriggered(lastValue); } } window.setTimer(didTheHiddenFieldChanged, 10); })();

Considering that this script is loaded (and evaluated) after the field has been created, it has two big disadvantages:

  • As for any polling mechanism, it uselessly consumes CPU on a regular basis.
  • Because of the timer use, the detection occurs only when the JavaScript engine has some free CPU time to executes the function.

Indeed, the following code will generate trouble: document.getElementById("hiddenFieldID").value = 1; // Some code document.getElementById("hiddenFieldID").value = 2;

Only the value "2" would be seen.

Worst case, let say that the last value detected was "2", then no change would be detected.

Second attempt: hooking "set"

After reading an interesting article about one of the new features of the ECMAScript 5.1: Object.defineProperty, I wanted to see if Chrome would allow me to redefine the value property of the hidden field.

I tried the following:

(function () { "use strict"; function actionToBeTriggered(/*value*/) { // What needs to be done when the value changed } Object.defineProperty(document.getElementById("hiddenFieldID"), "value", { get: function () { return 0; }, set: actionToBeTriggered }); })();

And... it worked.

This solution, beside the questionable support of such a mechanism, allowed me to hook a callback that was called every time the value was changed.

Conclusion

Once the patch has been delivered, we decided that the hidden field was no more necessary and replaced it with a direct call to the function.