JavaScript Prototype Chain Mutator

In JavaScript world, JSON serialization is widely used. When fetching data from server via Ajax, the data is usually represented in JSON; or loading configuration/data from file in Node.js application, the configuration/data is usually in JSON format.

JSON serialization is powerful and convenient, but there is limitation. For security and other reason, behavior and type information are forbidden in JSON. Functions members are removed when stringify a JavaScript object, also functions are not allowed in JSON.

Comparing Yaml to Ruby, this limitation isn’t that convenient when writing JavaScript application. For example, to consume the JSON data fetched via ajax from server, I really wish I can invoke some method on the deserialized model.

Here is simple example:

Ideal World
1
2
3
4
5
6
7
8
9
10
11
12
13
class Rect
constructor: (width, height) ->
@width = width if width?
@height = height if height?
area: ->
@width * @height
$.get '/rect/latest', (rectJSON) ->
rect = JSON.parse(rectJSON)
console.log rect.area() # This code doesn't work because there is rect is a plain object

The code doesn’t work, because rect in a plain object, which doesn’t contains any behavior. Someone called the rect DTO, Data Transfer Object, or POJO, Plain Old Java Object, a concept borrowed from Java world. Here we call it DTO.

To add behaviors to DTO, there are variant approaches. Such as create a behavior wrapper around the DTO, or create a new model with behavior and copy all the data from DTO to model. These practices are borrowed from Java world, or traditional Object Oriented world.

In fact, in JavaScript, there could be a better and smarter way to achieve that: Object Mutation, altering object prototype chain on the fly to convert a object into the instance of a specific type. The process is really similar to biologic genetic mutation, converting a species into another by altering the gene, so I borrow the term mutation.

With the idea, we can achieve this:

Mutate rect with Mutator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Rect
constructor: (width, height) ->
@width = width if width?
@height = height if height?
area: ->
@width * @height
$.get '/rect/latest', (rectJSON) ->
rect = JSON.parse(rectJSON)
mutate(rect, Rect)
console.log rect.area()

The key to implement mutate function is to simulate new operator behavior, alerting object.__proto__ and apply constructor to the instance! For more detail, check out the library mutator Bower version NPM version, which is available as both NPM package and bower package.

When implementing the mutator, in IE, again, in the evil IE, the idea doesn’t work. Before IE 11, JavaScript prototype chain for instance is not accessible. There is nothing equivalent to object.__proto__ in IE 10 and prior. The most similar workaround is doing a hard-copy of all the members, but it still fails in type check and some dynamical usage.

Background

object.__proto__ is a Mozilla “private” implementation until EcmaScript 6.
It is interesting that most JavaScript support it except IE.
Luckily, IE 11 introduced some features in EcmaScript 6, object.__proto__ is one of them.

Process.nextTick Implementation in Browser

Recursion is a common trick that is often used in JavaScript programming. So infinite recursion will cause stack overflow errors.
Some languages resolves this issue by introduce automatically tail call optimization, but in JavaScript we need to take care it on our own.

To solve the issue, Node.js has the utility functions nextTick to ensure specific code is invoked after the current function returned.
In Browser there is no standard approach to solve this issue, so workarounds are needed.

Thanks to Roman Shtylman(@defunctzombie), who created the node-process for Browserify, which simulate the Node.js API in browser environment.
Here is his implementation:

node-process

Infinite Recursion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
process.nextTick = (function () {
var canSetImmediate = typeof window !== 'undefined'
&& window.setImmediate;
var canPost = typeof window !== 'undefined'
&& window.postMessage && window.addEventListener;
if (canSetImmediate) {
return function (f) { return window.setImmediate(f) };
}
if (canPost) {
var queue = [];
window.addEventListener('message', function (ev) {
var source = ev.source;
if ((source === window || source === null) && ev.data === 'process-tick') {
ev.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
fn();
}
}
}, true);
return function nextTick(fn) {
queue.push(fn);
window.postMessage('process-tick', '*');
};
}
return function nextTick(fn) {
setTimeout(fn, 0);
};
})();

Here is some comments on the implementation.

setTimeout

To simulate the nextTick behavior, setTimeout(fn, 0) is a well-known and easy to adopt approach. The issue of this method is that setTimeout function does heavy operations, call it in loop causes significant performance issue. So we should try to use cheaper approach when possible.

setImmidate

There is a function called setImmediate, which behaves quite similar to nextTick but with a few differences when dealing with IO stuff. But in browser environment, there is no IO issue, so we can definitely replace the nextTick with it.

Immediates are queued in the order created, and are popped off the queue once per loop iteration. This is different from process.nextTick which will execute process.maxTickDepth queued callbacks per iteration. setImmediate will yield to the event loop after firing a queued callback to make sure I/O is not being starved. While order is preserved for execution, other I/O events may fire between any two scheduled immediate callbacks.

setImmediate(callback, [arg], [...])Node.js

The setImmediate function is perfect replacement for nextTick, but it is not supported by all the browsers. Only IE 10 and Node.js 0.10.+ supports it. Chrome, Firefox, Opera and all mobile browsers don’t.

Note: This method is not expected to become standard, and is only implemented by recent builds of Internet Explorer and Node.js 0.10+. It meets resistance both from Gecko (Firefox) and Webkit (Google/Apple).

window.setImmediateMDN

window.postMessage

window.postMessage enable developer to access message queue in the browser. By adding some additional code, we can simulate nextTick behavior based on message queue. It works in most modern browser, except IE 8. In IE 8, the API is implemented in a synchronous way, which introduce an extra level of stack-push, so it cannot be used to simulate nextTick.

Overall, there is no perfect workaround to the nextTick issue for now. All the solutions have different limitations, we can only hope that this issue can be resolved in the future ECMAScript standard.

NPMethod called on non-NPObject wrapped JSObject

I’m working on a “hybrid” android appilcation. In the app, part of the UI was written in HTML and hosted in a WebView. And I exposed several Java objects to JavaScript as Java Script Interface.

Setup WebView
1
2
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(irBlaster, "irBlaster");

The irBlaster object contains several methods, and js will invoke the specific method according to the data-attribute bound to the element.

Script that handles the button click in HTML
1
2
3
4
5
6
7
8
9
10
onIrButtonClicked: (e) =>
$button = $(e.currentTarget)
type = $button.data('irType')
sendFunc = irBlaster[type]
code = @parseCode($button.data('irCode'))
length = $button.data('irLength')
sendFunc(length, code)

The previous coffee-script works fine in my Jasmine tests with javascript mock version of irBlaster. When the button clicked, the proper method was invoked with proper arguments.
But when I run this code with real android app, WebView yields error says “NPMethod called on non-NPObject wrapped JSObject”.

The error message looks quite hard to understand the meaning, so I spent quite time to diagnose the code.

After several try, I found the following code works fine, but original one doesn’t:

Code works
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
onIrButtonClicked: (e) =>
$button = $(e.currentTarget)
type = $button.data('irType')
switch irBlaster[type]
when 'NEC'
code = @parseCode($button.data('irCode'))
length = $button.data('irLength')
irBlaster.NEC(length, code)
.
.
.

So I realize the issue was occurd in the javascript contextual binding.

Javascript is function-first-citizen language, and method call on object was simulate by invoking the function with specific context, and the code behavior won’t change if there is no reference to this pointer in the method. So it is quite normal that fetch a method from a object than invoke it without context.

That is why the Jasmine tests passed successfully. But the irBlaster isn’t real or simple javascript, but a native java object provided by js binding interface, so there is a limitation that the method on it cannot be invoked without context. Or it causes error.

So the issue can be resolved as following code by invoking the method in a “reflection” flavor:

Invoking native binding object with context provided in HTML
1
2
3
4
5
6
7
8
9
10
onIrButtonClicked: (e) =>
$button = $(e.currentTarget)
type = $button.data('irType')
sendFunc = irBlaster[type]
code = @parseCode($button.data('irCode'))
length = $button.data('irLength')
sendFunc.call(irBlaster, length, code)

In previous code, I invoke the sendFunc with call, and provids irBlaster as the context as this. So the problem solved, the code runs smoothly without issue.

Inject jQuery to current page

Now I’m hacking some web sites to try to grab the interesting informations from the page. But for some reason, all the sites I’m working on now, doesn’t included jQuery, they uses YUI or extJS, which makes me feel super inconvenient when trying hacking the page pattern.

Do I need to find a way to inject jQuery into the current page that I’m browsing.
Thanks to the browser address bar which allow us to execute javascript directly on current session. And also thanks to bookmark, which allow us to persists the script into bookmark, and invoke it with only one mouse click.

So I wrote this:

With this, we can quickly inject jQuery into current session in no time.

You can drag the following link to your browser favorites bar to:
Inject jQuery