diff --git a/jsesc.js b/jsesc.js index 56241b4..0553a64 100644 --- a/jsesc.js +++ b/jsesc.js @@ -58,6 +58,9 @@ return typeof value == 'string' || toString.call(value) == '[object String]'; }; + var isFunction = function(value) { + return typeof value == 'function'; + }; /*--------------------------------------------------------------------------*/ @@ -108,6 +111,11 @@ var result; var isEmpty = true; + // convert argument to toJSON if needed. + if (json && argument && isObject(argument) && isFunction(argument.toJSON)) { + argument = argument.toJSON(); + } + if (!isString(argument)) { if (isArray(argument)) { result = []; diff --git a/src/jsesc.js b/src/jsesc.js index 2239e44..9b5c4a0 100644 --- a/src/jsesc.js +++ b/src/jsesc.js @@ -51,13 +51,20 @@ return toString.call(value) == '[object Array]'; }; var isObject = function(value) { - // simple, but good enough for what we need + // This is a very simple check, but it’s good enough for what we need. return toString.call(value) == '[object Object]'; }; var isString = function(value) { return typeof value == 'string' || toString.call(value) == '[object String]'; }; + var isFunction = function(value) { + // In a perfect world, the `typeof` check would be sufficient. However, + // in Chrome 1–12, `typeof /x/ == 'object'`, and in IE 6–8 + // `typeof alert == 'object'` and similar for other host objects. + return typeof value == 'function' || + toString.call(value) == '[object Function]'; + }; /*--------------------------------------------------------------------------*/ @@ -71,7 +78,7 @@ '\n': '\\n', '\r': '\\r', '\t': '\\t' - // `\v` is omitted intentionally, because in IE < 9, '\v' == 'v' + // `\v` is omitted intentionally, because in IE < 9, '\v' == 'v'. // '\v': '\\x0B' }; var regexSingleEscape = /["'\\\b\f\n\r\t]/; @@ -108,6 +115,10 @@ var result; var isEmpty = true; + if (json && argument && isFunction(argument.toJSON)) { + argument = argument.toJSON(); + } + if (!isString(argument)) { if (isArray(argument)) { result = []; diff --git a/tests/tests.js b/tests/tests.js index b183038..887179b 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -263,6 +263,26 @@ '"foo\\u{1D306}bar\\u00A9baz"', 'Escaping as JSON with `es6: true`' ); + var tmp = { + 'shouldn\u2019t be here': 10, + 'toJSON': function() { + return { + 'hello': 'world', + '\uD83D\uDCA9': 'foo', + 'pile': '\uD83D\uDCA9' + }; + } + }; + equal( + jsesc(tmp, { 'json' : true }), + '{"hello":"world","\\uD83D\\uDCA9":"foo","pile":"\\uD83D\\uDCA9"}', + '`toJSON` methods are called when `json: true`' + ); + notEqual( + jsesc(tmp), + '{"hello":"world","\\uD83D\\uDCA9":"foo","pile":"\\uD83D\\uDCA9"}', + '`toJSON` methods are not called when `json: false`' + ); }); if (runExtendedTests) {