isopod
v0.5.1
Published
Universal (isomorphic) Javascript object serialization / deserialization
Downloads
64
Maintainers
Readme
About
Have you ever wanted to clone an object (including all its methods and prototyped goodness) from one Javascript runtime to another?
This library will, you know, do that.
Getting started
When using it in node.js, you could:
npm install isopod
...and from your node source code do:
const isopod = require('isopod');
When using from the browser, download it, then in your html include a <script>
pointing to the correct file. The script will make isopod
a global variable.
For example you might run:
npm install isopod
...and then you might put this in your html (assuming the node_modules
directory is statically served to the client):
<script src="/isopod/dist/isopod.min.js"></script>
...and now isopod
would be a global variable available to any subsequent client scripts.
What does it include?
isopod
has but two methods: serialize
and deserialize
.
Why would I use it?
Normally when you send some Javascript data from one program (e.g. your server) to another (e.g. your client) it loses "richness". Functions, regular expressions, circular objects, objects with non-standard prototypes—all of these things would not normally transfer over.
One way to think about why this happens is that any data that gets sent somewhere else by your Javascript program will (basically) need to get converted into a string, and is generally converted back into data (parsed) on the receiving end. Commonly the sender will JSON.stringify
the data and the receiver will JSON.parse
the incoming string.
This means that Javascript objects get converted into JSON strings, which are only natively equipped to represent plain objects, plain arrays, strings, numbers, booleans, and null.
isopod.serialize
converts anything more complex than that into a plain object so that it can be stringified. Importantly this plain, "dehydrated", object still contains all the information necessary to reconstruct the details of the original. As such, when parsed on the other end, we should get a clone of the dehydrated object. isopod.deserialize
can "rehydrate" this into a clone of the original.
Any change that occurs to the cloned object will not affect the original and vice-versa. Those objects exist in different runtimes and cannot directly affect each other. If you want automated synchronization, isopod
is not that, though you could build something like that "on top" of it.
With isopod
you can remotely clone:
- Plain objects, arrays, strings, numbers, booleans, and null (these are no different than what you can do without
isopod
) undefined
, ±Infinity
, andNaN
- Functions (but not closures, nor bound functions)
- Regular expressions
- Errors
- Dates
- Symbols
- Sets
- Maps
- Anything with a non-standard prototype
- Anything with circular or duplicate references to the same object or symbol
- Array buffers / typed arrays / data views
Note that cloning will exclude non-enumerable properties (except for the .constructor
property—this in order to clone prototypes effectively). Here's a not-necessarily complete list of what you might not yet be able to clone:
- Promises¹
- Generators
- Proxies
¹ The plan is never to support cloning promises—it involves cloning too much hairy state for a non-obvious use case. If you're looking to resolve any values on an object, you could do so and then pass the result to isopod.serialize
. Check out bluebird's Promise.props
or even promise-resolve-deep for help resolving inside an object.
How would I use it?
Install and include isopod
into both environments: client and server, one iframe and another iframe, tessel and electron app—whatever two Javascript runtimes you want to clone data between.
Then for any data you'd like to "richly clone", just before sending it across call isopod.serialize
on it. Just after parsing the data on the other end call isopod.deserialize
on it.
For example, you might have an express route handler on your node server that looks like:
...
// an example of some data that is sufficiently complex
function Thing (names) {
this.self = this;
this.names = new Set(names);
}
Thing.prototype.jump = function () {
this.state = 'jumping';
};
const thing = new Thing(['robert', 'bob', 'rob']);
...
app.get('/thing', function (req, res, next) {
const serialized = isopod.serialize(thing);
res.json(serialized);
});
...
And an AJAX request on your client that looks like:
...
fetch('/thing')
.then(function (response) {
return response.json();
})
.then(function (jsonObj) {
const deserialized = isopod.deserialize(jsonObj);
// should now be able to do the following
deserialized.names.has('bob'); // => true
deserialized.jump();
console.log(deserialized.state); // 'jumping'
console.log(deserialized.self === deserialized); // true
});
...
For something more concrete, you can find runnable examples in the examples folder.
Similar libraries
Contributing
Pull requests / issues / comments / hate mail welcome!
If you'd like to run the tests, download this repo, open a terminal, navigate to it, then run:
npm install
...and once that's done:
npm test