From 3cf2adb7c6a57b7c0f92e9b68885cbc0e63f0733 Mon Sep 17 00:00:00 2001 From: "wenli.lw" Date: Mon, 3 Apr 2017 15:48:21 +0800 Subject: [PATCH 1/6] support function --- .gitignore | 4 + README.md | 8 +- resurrect.js | 832 ++++++++++++++++++++++++++------------------------- test.js | 16 + 4 files changed, 449 insertions(+), 411 deletions(-) create mode 100644 .gitignore create mode 100644 test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c807848 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Created by .ignore support plugin (hsz.mobi) +.vscode +.idea +.history \ No newline at end of file diff --git a/README.md b/README.md index 6da87dd..62ee0f6 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,8 @@ Read about [how it works](http://nullprogram.com/blog/2013/03/28/). ## Examples ```javascript -function Foo() {} -Foo.prototype.greet = function() { return "hello"; }; - -// Behavior is preserved: +function foo() {} var necromancer = new Resurrect(); -var json = necromancer.stringify(new Foo()); -var foo = necromancer.resurrect(json); -foo.greet(); // => "hello" // References to the same object are preserved: json = necromancer.stringify([foo, foo]); diff --git a/resurrect.js b/resurrect.js index 31fdc42..20e366d 100644 --- a/resurrect.js +++ b/resurrect.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /** * # ResurrectJS * @version 1.0.3 @@ -81,459 +82,482 @@ * @see http://nullprogram.com/blog/2013/03/28/ */ -/** - * @param {Object} [options] See options documentation. - * @namespace - * @constructor - */ -function Resurrect(options) { - this.table = null; - this.prefix = '#'; - this.cleanup = false; - this.revive = true; - for (var option in options) { - if (options.hasOwnProperty(option)) { - this[option] = options[option]; +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define([], factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.Resurrect = factory(); + } +})(this, function () { + /** + * @param {Object} [options] See options documentation. + * @namespace + * @constructor + */ + function Resurrect(options) { + this.table = null; + this.prefix = '#'; + this.cleanup = false; + this.revive = true; + for (var option in options) { + if (options.hasOwnProperty(option)) { + this[option] = options[option]; + } } + this.refcode = this.prefix + 'id'; + this.origcode = this.prefix + 'original'; + this.buildcode = this.prefix + '.'; + this.valuecode = this.prefix + 'v'; } - this.refcode = this.prefix + 'id'; - this.origcode = this.prefix + 'original'; - this.buildcode = this.prefix + '.'; - this.valuecode = this.prefix + 'v'; -} -/** - * Portable access to the global object (window, global). - * Uses indirect eval. - * @constant - */ -Resurrect.GLOBAL = (0, eval)('this'); + /** + * Portable access to the global object (window, global). + * Uses indirect eval. + * @constant + */ + Resurrect.GLOBAL = (0, eval)('this'); -/** - * Escape special regular expression characters in a string. - * @param {string} string - * @returns {string} The string escaped for exact matches. - * @see http://stackoverflow.com/a/6969486 - */ -Resurrect.escapeRegExp = function (string) { - return string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); -}; + Resurrect.ISNATIVEFUNC = /^function\s*[^(]*\(.*\)\s*\{\s*\[native code\]\s*\}$/; -/* Helper Objects */ + /** + * Escape special regular expression characters in a string. + * @param {string} string + * @returns {string} The string escaped for exact matches. + * @see http://stackoverflow.com/a/6969486 + */ + Resurrect.escapeRegExp = function (string) { + return string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }; -/** - * @param {string} [message] - * @constructor - */ -Resurrect.prototype.Error = function ResurrectError(message) { - this.message = message || ''; - this.stack = new Error().stack; -}; -Resurrect.prototype.Error.prototype = Object.create(Error.prototype); -Resurrect.prototype.Error.prototype.name = 'ResurrectError'; + /* Helper Objects */ -/** - * Resolves prototypes through the properties on an object and - * constructor names. - * @param {Object} scope - * @constructor - */ -Resurrect.NamespaceResolver = function(scope) { - this.scope = scope; -}; + /** + * @param {string} [message] + * @constructor + */ + Resurrect.prototype.Error = function ResurrectError(message) { + this.message = message || ''; + this.stack = new Error().stack; + }; + Resurrect.prototype.Error.prototype = Object.create(Error.prototype); + Resurrect.prototype.Error.prototype.name = 'ResurrectError'; -/** - * Gets the prototype of the given property name from an object. If - * not found, it throws an error. - * @param {string} name - * @returns {Object} - * @method - */ -Resurrect.NamespaceResolver.prototype.getPrototype = function(name) { - var constructor = this.scope[name]; - if (constructor) { - return constructor.prototype; - } else { - throw new Resurrect.prototype.Error('Unknown constructor: ' + name); - } -}; + /** + * Resolves prototypes through the properties on an object and + * constructor names. + * @param {Object} scope + * @constructor + */ + Resurrect.NamespaceResolver = function(scope) { + this.scope = scope; + }; -/** - * Get the prototype name for an object, to be fetched later with getPrototype. - * @param {Object} object - * @returns {?string} Null if the constructor is Object. - * @method - */ -Resurrect.NamespaceResolver.prototype.getName = function(object) { - var constructor = object.constructor.name; - if (constructor == null) { // IE - var funcPattern = /^\s*function\s*([A-Za-z0-9_$]*)/; - constructor = funcPattern.exec(object.constructor)[1]; - } + /** + * Gets the prototype of the given property name from an object. If + * not found, it throws an error. + * @param {string} name + * @returns {Object} + * @method + */ + Resurrect.NamespaceResolver.prototype.getPrototype = function(name) { + var constructor = this.scope[name]; + if (constructor) { + return constructor.prototype; + } else { + throw new Resurrect.prototype.Error('Unknown constructor: ' + name); + } + }; - if (constructor === '') { - var msg = "Can't serialize objects with anonymous constructors."; - throw new Resurrect.prototype.Error(msg); - } else if (constructor === 'Object' || constructor === 'Array') { - return null; - } else { - return constructor; - } -}; + /** + * Get the prototype name for an object, to be fetched later with getPrototype. + * @param {Object} object + * @returns {?string} Null if the constructor is Object. + * @method + */ + Resurrect.NamespaceResolver.prototype.getName = function(object) { + var constructor = object.constructor.name; + if (constructor == null) { // IE + var funcPattern = /^\s*function\s*([A-Za-z0-9_$]*)/; + constructor = funcPattern.exec(object.constructor)[1]; + } -/* Set the default resolver searches the global object. */ -Resurrect.prototype.resolver = - new Resurrect.NamespaceResolver(Resurrect.GLOBAL); + if (constructor === '') { + var msg = "Can't serialize objects with anonymous constructors."; + throw new Resurrect.prototype.Error(msg); + } else if (constructor === 'Object' || constructor === 'Array') { + return null; + } else { + return constructor; + } + }; -/** - * Create a DOM node from HTML source; behaves like a constructor. - * @param {string} html - * @constructor - */ -Resurrect.Node = function(html) { - var div = document.createElement('div'); - div.innerHTML = html; - return div.firstChild; -}; + /* Set the default resolver searches the global object. */ + Resurrect.prototype.resolver = + new Resurrect.NamespaceResolver(Resurrect.GLOBAL); -/* Type Tests */ + /** + * Create a DOM node from HTML source; behaves like a constructor. + * @param {string} html + * @constructor + */ + Resurrect.Node = function(html) { + var div = document.createElement('div'); + div.innerHTML = html; + return div.firstChild; + }; -/** - * @param {string} type - * @returns {Function} A function that tests for type. - */ -Resurrect.is = function(type) { - var string = '[object ' + type + ']'; - return function(object) { - return Object.prototype.toString.call(object) === string; + /* Type Tests */ + + /** + * @param {string} type + * @returns {Function} A function that tests for type. + */ + Resurrect.is = function(type) { + var string = '[object ' + type + ']'; + return function(object) { + return Object.prototype.toString.call(object) === string; + }; }; -}; -Resurrect.isArray = Resurrect.is('Array'); -Resurrect.isString = Resurrect.is('String'); -Resurrect.isBoolean = Resurrect.is('Boolean'); -Resurrect.isNumber = Resurrect.is('Number'); -Resurrect.isFunction = Resurrect.is('Function'); -Resurrect.isDate = Resurrect.is('Date'); -Resurrect.isRegExp = Resurrect.is('RegExp'); -Resurrect.isObject = Resurrect.is('Object'); + Resurrect.isArray = Resurrect.is('Array'); + Resurrect.isString = Resurrect.is('String'); + Resurrect.isBoolean = Resurrect.is('Boolean'); + Resurrect.isNumber = Resurrect.is('Number'); + Resurrect.isFunction = Resurrect.is('Function'); + Resurrect.isDate = Resurrect.is('Date'); + Resurrect.isRegExp = Resurrect.is('RegExp'); + Resurrect.isObject = Resurrect.is('Object'); -Resurrect.isAtom = function(object) { - return !Resurrect.isObject(object) && !Resurrect.isArray(object); -}; + Resurrect.isAtom = function(object) { + return !Resurrect.isObject(object) && !Resurrect.isArray(object); + }; -/** - * @param {*} object - * @returns {boolean} True if object is a primitive or a primitive wrapper. - */ -Resurrect.isPrimitive = function(object) { - return object == null || - Resurrect.isNumber(object) || - Resurrect.isString(object) || - Resurrect.isBoolean(object); -}; + /** + * @param {*} object + * @returns {boolean} True if object is a primitive or a primitive wrapper. + */ + Resurrect.isPrimitive = function(object) { + return object == null || + Resurrect.isNumber(object) || + Resurrect.isString(object) || + Resurrect.isBoolean(object); + }; -/* Methods */ + /* Methods */ -/** - * Create a reference (encoding) to an object. - * @param {(Object|undefined)} object - * @returns {Object} - * @method - */ -Resurrect.prototype.ref = function(object) { - var ref = {}; - if (object === undefined) { - ref[this.prefix] = -1; - } else { - ref[this.prefix] = object[this.refcode]; - } - return ref; -}; + /** + * Create a reference (encoding) to an object. + * @param {(Object|undefined)} object + * @returns {Object} + * @method + */ + Resurrect.prototype.ref = function(object) { + var ref = {}; + if (object === undefined) { + ref[this.prefix] = -1; + } else { + ref[this.prefix] = object[this.refcode]; + } + return ref; + }; -/** - * Lookup an object in the table by reference object. - * @param {Object} ref - * @returns {(Object|undefined)} - * @method - */ -Resurrect.prototype.deref = function(ref) { - return this.table[ref[this.prefix]]; -}; + /** + * Lookup an object in the table by reference object. + * @param {Object} ref + * @returns {(Object|undefined)} + * @method + */ + Resurrect.prototype.deref = function(ref) { + return this.table[ref[this.prefix]]; + }; -/** - * Put a temporary identifier on an object and store it in the table. - * @param {Object} object - * @returns {number} The unique identifier number. - * @method - */ -Resurrect.prototype.tag = function(object) { - if (this.revive) { - var constructor = this.resolver.getName(object); - if (constructor) { - var proto = Object.getPrototypeOf(object); - if (this.resolver.getPrototype(constructor) !== proto) { - throw new this.Error('Constructor mismatch!'); - } else { - object[this.prefix] = constructor; + /** + * Put a temporary identifier on an object and store it in the table. + * @param {Object} object + * @returns {number} The unique identifier number. + * @method + */ + Resurrect.prototype.tag = function(object) { + if (this.revive) { + var constructor = this.resolver.getName(object); + if (constructor) { + var proto = Object.getPrototypeOf(object); + if (this.resolver.getPrototype(constructor) !== proto) { + throw new this.Error('Constructor mismatch!'); + } else { + object[this.prefix] = constructor; + } } } - } - object[this.refcode] = this.table.length; - this.table.push(object); - return object[this.refcode]; -}; + object[this.refcode] = this.table.length; + this.table.push(object); + return object[this.refcode]; + }; -/** - * Create a builder object (encoding) for serialization. - * @param {string} name The name of the constructor. - * @param value The value to pass to the constructor. - * @returns {Object} - * @method - */ -Resurrect.prototype.builder = function(name, value) { - var builder = {}; - builder[this.buildcode] = name; - builder[this.valuecode] = value; - return builder; -}; + /** + * Create a builder object (encoding) for serialization. + * @param {string} name The name of the constructor. + * @param value The value to pass to the constructor. + * @returns {Object} + * @method + */ + Resurrect.prototype.builder = function(name, value) { + var builder = {}; + builder[this.buildcode] = name; + builder[this.valuecode] = value; + return builder; + }; -/** - * Build a value from a deserialized builder. - * @param {Object} ref - * @returns {Object} - * @method - * @see http://stackoverflow.com/a/14378462 - * @see http://nullprogram.com/blog/2013/03/24/ - */ -Resurrect.prototype.build = function(ref) { - var type = ref[this.buildcode].split(/\./).reduce(function(object, name) { - return object[name]; - }, Resurrect.GLOBAL); - /* Brilliant hack by kybernetikos: */ - var args = [null].concat(ref[this.valuecode]); - var factory = type.bind.apply(type, args); - var result = new factory(); - if (Resurrect.isPrimitive(result)) { - return result.valueOf(); // unwrap - } else { - return result; - } -}; + /** + * Build a value from a deserialized builder. + * @param {Object} ref + * @returns {Object} + * @method + * @see http://stackoverflow.com/a/14378462 + * @see http://nullprogram.com/blog/2013/03/24/ + */ + Resurrect.prototype.build = function(ref) { + var type = ref[this.buildcode].split(/\./).reduce(function(object, name) { + return object[name]; + }, Resurrect.GLOBAL); + var valuecode = ref[this.valuecode]; + if (type.name === 'Function') { + return eval('(' + valuecode + ')'); + } + /* Brilliant hack by kybernetikos: */ + var args = [null].concat(valuecode); + var factory = type.bind.apply(type, args); + var result = new factory(); + if (Resurrect.isPrimitive(result)) { + return result.valueOf(); // unwrap + } else { + return result; + } + }; -/** - * Dereference or build an object or value from an encoding. - * @param {Object} ref - * @returns {(Object|undefined)} - * @method - */ -Resurrect.prototype.decode = function(ref) { - if (this.prefix in ref) { - return this.deref(ref); - } else if (this.buildcode in ref) { - return this.build(ref); - } else { - throw new this.Error('Unknown encoding.'); - } -}; + /** + * Dereference or build an object or value from an encoding. + * @param {Object} ref + * @returns {(Object|undefined)} + * @method + */ + Resurrect.prototype.decode = function(ref) { + if (this.prefix in ref) { + return this.deref(ref); + } else if (this.buildcode in ref) { + return this.build(ref); + } else { + throw new this.Error('Unknown encoding.'); + } + }; -/** - * @param {Object} object - * @returns {boolean} True if the provided object is tagged for serialization. - * @method - */ -Resurrect.prototype.isTagged = function(object) { - return (this.refcode in object) && (object[this.refcode] != null); -}; + /** + * @param {Object} object + * @returns {boolean} True if the provided object is tagged for serialization. + * @method + */ + Resurrect.prototype.isTagged = function(object) { + return (this.refcode in object) && (object[this.refcode] != null); + }; -/** - * Visit root and all its ancestors, visiting atoms with f. - * @param {*} root - * @param {Function} f - * @param {Function} replacer - * @returns {*} A fresh copy of root to be serialized. - * @method - */ -Resurrect.prototype.visit = function(root, f, replacer) { - if (Resurrect.isAtom(root)) { - return f(root); - } else if (!this.isTagged(root)) { - var copy = null; - if (Resurrect.isArray(root)) { - copy = []; - root[this.refcode] = this.tag(copy); - for (var i = 0; i < root.length; i++) { - copy.push(this.visit(root[i], f, replacer)); - } - } else { /* Object */ - copy = Object.create(Object.getPrototypeOf(root)); - root[this.refcode] = this.tag(copy); - for (var key in root) { - var value = root[key]; - if (root.hasOwnProperty(key)) { - if (replacer && value !== undefined) { - // Call replacer like JSON.stringify's replacer - value = replacer.call(root, key, root[key]); - if (value === undefined) { - continue; // Omit from result + /** + * Visit root and all its ancestors, visiting atoms with f. + * @param {*} root + * @param {Function} f + * @param {Function} replacer + * @returns {*} A fresh copy of root to be serialized. + * @method + */ + Resurrect.prototype.visit = function(root, f, replacer) { + if (Resurrect.isAtom(root)) { + return f(root); + } else if (!this.isTagged(root)) { + var copy = null; + if (Resurrect.isArray(root)) { + copy = []; + root[this.refcode] = this.tag(copy); + for (var i = 0; i < root.length; i++) { + copy.push(this.visit(root[i], f, replacer)); + } + } else { /* Object */ + copy = Object.create(Object.getPrototypeOf(root)); + root[this.refcode] = this.tag(copy); + for (var key in root) { + var value = root[key]; + if (root.hasOwnProperty(key)) { + if (replacer && value !== undefined) { + // Call replacer like JSON.stringify's replacer + value = replacer.call(root, key, root[key]); + if (value === undefined) { + continue; // Omit from result + } } + copy[key] = this.visit(value, f, replacer); } - copy[key] = this.visit(value, f, replacer); } } + copy[this.origcode] = root; + return this.ref(copy); + } else { + return this.ref(root); } - copy[this.origcode] = root; - return this.ref(copy); - } else { - return this.ref(root); - } -}; - -/** - * Manage special atom values, possibly returning an encoding. - * @param {*} atom - * @returns {*} - * @method - */ -Resurrect.prototype.handleAtom = function(atom) { - var Node = Resurrect.GLOBAL.Node || function() {}; - if (Resurrect.isFunction(atom)) { - throw new this.Error("Can't serialize functions."); - } else if (atom instanceof Node) { - var xmls = new XMLSerializer(); - return this.builder('Resurrect.Node', [xmls.serializeToString(atom)]); - } else if (Resurrect.isDate(atom)) { - return this.builder('Date', [atom.toISOString()]); - } else if (Resurrect.isRegExp(atom)) { - var args = atom.toString().match(/\/(.+)\/([gimy]*)/).slice(1); - return this.builder('RegExp', args); - } else if (atom === undefined) { - return this.ref(undefined); - } else if (Resurrect.isNumber(atom) && (isNaN(atom) || !isFinite(atom))) { - return this.builder('Number', [atom.toString()]); - } else { - return atom; - } -}; + }; -/** - * Hides intrusive keys from a user-supplied replacer. - * @param {Function} replacer function of two arguments (key, value) - * @returns {Function} A function that skips the replacer for intrusive keys. - * @method - */ -Resurrect.prototype.replacerWrapper = function(replacer) { - var skip = new RegExp('^' + Resurrect.escapeRegExp(this.prefix)); - return function(k, v) { - if (skip.test(k)) { - return v; + /** + * Manage special atom values, possibly returning an encoding. + * @param {*} atom + * @returns {*} + * @method + */ + Resurrect.prototype.handleAtom = function(atom) { + var Node = Resurrect.GLOBAL.Node || function () { + }; + if (Resurrect.isFunction(atom)) { + //throw new this.Error("Can't serialize functions.") + var funcStr = atom.toString(); + if (Resurrect.ISNATIVEFUNC.test(funcStr)) { + throw new Error('Can\'t serialize a object with a native function property. Use serialize(obj, true) to ignore the error.'); + } + return this.builder('Function', funcStr) + } else if (atom instanceof Node) { + var xmls = new XMLSerializer(); + return this.builder('Resurrect.Node', [xmls.serializeToString(atom)]); + } else if (Resurrect.isDate(atom)) { + return this.builder('Date', [atom.toISOString()]); + } else if (Resurrect.isRegExp(atom)) { + var args = atom.toString().match(/\/(.+)\/([gimy]*)/).slice(1); + return this.builder('RegExp', args); + } else if (atom === undefined) { + return this.ref(undefined); + } else if (Resurrect.isNumber(atom) && (isNaN(atom) || !isFinite(atom))) { + return this.builder('Number', [atom.toString()]); } else { - return replacer(k, v); + return atom; } }; -}; -/** - * Serialize an arbitrary JavaScript object, carefully preserving it. - * @param {*} object - * @param {(Function|Array)} replacer - * @param {string} space - * @method - */ -Resurrect.prototype.stringify = function(object, replacer, space) { - if (Resurrect.isFunction(replacer)) { - replacer = this.replacerWrapper(replacer); - } else if (Resurrect.isArray(replacer)) { - var acceptKeys = replacer; - replacer = function(k, v) { - return acceptKeys.indexOf(k) >= 0 ? v : undefined; - }; - } - if (Resurrect.isAtom(object)) { - return JSON.stringify(this.handleAtom(object), replacer, space); - } else { - this.table = []; - this.visit(object, this.handleAtom.bind(this), replacer); - for (var i = 0; i < this.table.length; i++) { - if (this.cleanup) { - delete this.table[i][this.origcode][this.refcode]; + /** + * Hides intrusive keys from a user-supplied replacer. + * @param {Function} replacer function of two arguments (key, value) + * @returns {Function} A function that skips the replacer for intrusive keys. + * @method + */ + Resurrect.prototype.replacerWrapper = function(replacer) { + var skip = new RegExp('^' + Resurrect.escapeRegExp(this.prefix)); + return function(k, v) { + if (skip.test(k)) { + return v; } else { - this.table[i][this.origcode][this.refcode] = null; + return replacer(k, v); } - delete this.table[i][this.refcode]; - delete this.table[i][this.origcode]; - } - var table = this.table; - this.table = null; - return JSON.stringify(table, null, space); - } -}; + }; + }; -/** - * Restore the __proto__ of the given object to the proper value. - * @param {Object} object - * @returns {Object} Its argument, or a copy, with the prototype restored. - * @method - */ -Resurrect.prototype.fixPrototype = function(object) { - if (this.prefix in object) { - var name = object[this.prefix]; - var prototype = this.resolver.getPrototype(name); - if ('__proto__' in object) { - object.__proto__ = prototype; - if (this.cleanup) { - delete object[this.prefix]; - } - return object; - } else { // IE - var copy = Object.create(prototype); - for (var key in object) { - if (object.hasOwnProperty(key) && key !== this.prefix) { - copy[key] = object[key]; + /** + * Serialize an arbitrary JavaScript object, carefully preserving it. + * @param {*} object + * @param {(Function|Array)} replacer + * @param {string} space + * @method + */ + Resurrect.prototype.stringify = function(object, replacer, space) { + if (Resurrect.isFunction(replacer)) { + replacer = this.replacerWrapper(replacer); + } else if (Resurrect.isArray(replacer)) { + var acceptKeys = replacer; + replacer = function(k, v) { + return acceptKeys.indexOf(k) >= 0 ? v : undefined; + }; + } + if (Resurrect.isAtom(object)) { + return JSON.stringify(this.handleAtom(object), replacer, space); + } else { + this.table = []; + this.visit(object, this.handleAtom.bind(this), replacer); + for (var i = 0; i < this.table.length; i++) { + if (this.cleanup) { + delete this.table[i][this.origcode][this.refcode]; + } else { + this.table[i][this.origcode][this.refcode] = null; } + delete this.table[i][this.refcode]; + delete this.table[i][this.origcode]; } - return copy; + var table = this.table; + this.table = null; + return JSON.stringify(table, null, space); } - } else { - return object; - } -}; + }; -/** - * Deserialize an encoded object, restoring circularity and behavior. - * @param {string} string - * @returns {*} The decoded object or value. - * @method - */ -Resurrect.prototype.resurrect = function(string) { - var result = null; - var data = JSON.parse(string); - if (Resurrect.isArray(data)) { - this.table = data; - /* Restore __proto__. */ - if (this.revive) { - for (var i = 0; i < this.table.length; i++) { - this.table[i] = this.fixPrototype(this.table[i]); + /** + * Restore the __proto__ of the given object to the proper value. + * @param {Object} object + * @returns {Object} Its argument, or a copy, with the prototype restored. + * @method + */ + Resurrect.prototype.fixPrototype = function(object) { + if (this.prefix in object) { + var name = object[this.prefix]; + var prototype = this.resolver.getPrototype(name); + if ('__proto__' in object) { + object.__proto__ = prototype; + if (this.cleanup) { + delete object[this.prefix]; + } + return object; + } else { // IE + var copy = Object.create(prototype); + for (var key in object) { + if (object.hasOwnProperty(key) && key !== this.prefix) { + copy[key] = object[key]; + } + } + return copy; } + } else { + return object; } - /* Re-establish object references and construct atoms. */ - for (i = 0; i < this.table.length; i++) { - var object = this.table[i]; - for (var key in object) { - if (object.hasOwnProperty(key)) { - if (!(Resurrect.isAtom(object[key]))) { - object[key] = this.decode(object[key]); + }; + + /** + * Deserialize an encoded object, restoring circularity and behavior. + * @param {string} string + * @returns {*} The decoded object or value. + * @method + */ + Resurrect.prototype.resurrect = function(string) { + var result = null; + var data = JSON.parse(string); + if (Resurrect.isArray(data)) { + this.table = data; + /* Restore __proto__. */ + if (this.revive) { + for (var i = 0; i < this.table.length; i++) { + this.table[i] = this.fixPrototype(this.table[i]); + } + } + /* Re-establish object references and construct atoms. */ + for (i = 0; i < this.table.length; i++) { + var object = this.table[i]; + for (var key in object) { + if (object.hasOwnProperty(key)) { + if (!(Resurrect.isAtom(object[key]))) { + object[key] = this.decode(object[key]); + } } } } + result = this.table[0]; + } else if (Resurrect.isObject(data)) { + this.table = []; + result = this.decode(data); + } else { + result = data; } - result = this.table[0]; - } else if (Resurrect.isObject(data)) { - this.table = []; - result = this.decode(data); - } else { - result = data; - } - this.table = null; - return result; -}; + this.table = null; + return result; + }; + return Resurrect; +}); \ No newline at end of file diff --git a/test.js b/test.js new file mode 100644 index 0000000..fb54ac1 --- /dev/null +++ b/test.js @@ -0,0 +1,16 @@ +var Resurrect = require('./resurrect.js') +function foo() { + return 'hello' +} +var necromancer = new Resurrect(); + +// References to the same object are preserved: +var json = necromancer.stringify([foo, foo]); +var array = necromancer.resurrect(json); +console.log(array) +console.log(array[0]() === array[1]()) + +// Dates are restored properly +json = necromancer.stringify(new Date()); +var date = necromancer.resurrect(json); +console.log(Object.prototype.toString.call(date)); // => "[object Date]" From c71560c4d091a2d8b3ad7e4fcf0d956eee7e20fe Mon Sep 17 00:00:00 2001 From: "wenli.lw" Date: Mon, 3 Apr 2017 15:55:35 +0800 Subject: [PATCH 2/6] publish to npm --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 62ee0f6..cae3a08 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ also properly resurrect these types of values: * DOM objects * `undefined` * NaN, Infinity, -Infinity + * Function, Class Supported Browsers: From 7d4e6d51a9893d3e66d83c5458287a764719a24e Mon Sep 17 00:00:00 2001 From: "wenli.lw" Date: Mon, 3 Apr 2017 15:55:43 +0800 Subject: [PATCH 3/6] publish to npm --- package.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 0000000..9869a92 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "resurrect.js", + "version": "1.0.3", + "description": "ResurrectJS preserves object behavior (prototypes) and reference circularity with a special JSON encoding.", + "main": "resurrect.js", + "scripts": { + "test": "node test.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/lwdgit/resurrect-js.git" + }, + "keywords": [ + "json", + "resurect" + ], + "author": "", + "license": "MIT", + "bugs": { + "url": "https://github.com/lwdgit/resurrect-js/issues" + }, + "homepage": "https://github.com/lwdgit/resurrect-js#readme" +} From d80866137eebac393489d94261219d9e07fb3ce8 Mon Sep 17 00:00:00 2001 From: "wenli.lw" Date: Mon, 3 Apr 2017 20:26:23 +0800 Subject: [PATCH 4/6] link to npm --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cae3a08..fa71593 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ResurrectJS +# [ResurrectJS](https://www.npmjs.com/package/resurrect.js) ResurrectJS preserves object behavior (prototypes) and reference circularity with a special JSON encoding. Unlike flat JSON, it can From 1096071c180f429fd8a34b5be15e950099260429 Mon Sep 17 00:00:00 2001 From: wen Date: Mon, 3 Apr 2017 20:31:53 +0800 Subject: [PATCH 5/6] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fa71593..b18c517 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,15 @@ Read about [how it works](http://nullprogram.com/blog/2013/03/28/). ## Examples ```javascript -function foo() {} +function foo() { + return 1; +} var necromancer = new Resurrect(); // References to the same object are preserved: json = necromancer.stringify([foo, foo]); var array = necromancer.resurrect(json); -array[0] === array[1]; // => true -array[1].greet(); // => "hello" +array[0]() === array[1](); // => true // Dates are restored properly json = necromancer.stringify(new Date()); From cfac8375095a5a003e1374c191edb7f8e3908027 Mon Sep 17 00:00:00 2001 From: wen Date: Mon, 3 Apr 2017 20:33:19 +0800 Subject: [PATCH 6/6] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9869a92..73f657e 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "keywords": [ "json", - "resurect" + "resurrect" ], "author": "", "license": "MIT",