简体   繁体   中英

How to export JavaScript object as JavaScript?

I'm looking for most elegant way of "dumping" simple JavaScript object into JavaScript source-code generated on-fly.

Purpose : Assume we have node.js server generating HTML. We have an object x on server side. The object is simple - only strings/ints/arrays in it (so, it's JSON-able). I want to "embed" object x into HTML being generated, to be available for JavaScript code which will run on the browser. So the code:

console.log(x);

will dump exactly the same data on both server-side and browser-side. For example - imagine I'm going to pass some additional config/data to JavaScript running on browser.

Obvious solutions :

  1. Encoding as JSON and send as AJAX/Websocket is not a part of this question as we have to embed the object in the HTML. I don't want additional HTTP requests - everything should be passed in one go.
  2. Encoding as JSON and simply attach to variable sounds initially good, but involves some additional escaping steps.

  3. Using util.inspect() works for me, in this way:

    var toHtml = 'var x = ' + util.inspect(theXonServer, {depth:9}) + ';';

but I'm not sure if it's "elegant" (and secure and error-prone and...)

Any better suggestions ? Standard way of doing that ?

The Wrong Way to Pass Data

It's common to get advice to just stringify some JSON and dump it into a <script> tag. This is bad advice . Don't do it.

It's important to understand why this is a bad idea.

When you string-build JavaScript, you're opening yourself up to all sorts of quirks of the language that you'd absolutely be required to understand to make sure that there are no issues.

One such quirk is that within a <script> element, the first occurrence of </script> will close the <script> element. It doesn't matter that it's in a string, the script will be closed, and the rest of the contents after that point will be treated as HTML.

HTML escaping doesn't work because JS doesn't like HTML entities.

what might start as:

<script>
    window.xss = <%= JSON HERE %>
</script>

could turn into:

<script>
    window.xss = {"username":"Robert Hackerman</script><script src='nefarious.js'></script>"}
</script>

Don't risk it.

The Right Way to Pass Data...

...When the Page is Rendering

The much safer way that prevents any script execution is via [data-*] attributes. You must HTML-escape the contents, but that's OK in attributes. I'm using a <script> element because it's implied that the script will be using the data.

What would start as:

<script data-foo="<%= HTML ENCODED JSON HERE %>" src="yourscript.js"></script>

Would turn into:

<script data-foo="{&quot;username&quot;:&quot;Robert Hackerman&lt;/script&gt;&lt;script src=&apos;nefarious.js&apos;&gt;&lt;/script&gt;&quot;}" src="yourscript.js"></script>

And if you want access to that data, you can just access the attribute value, or use the dataset api (if your target browsers support it) :

var fooElement = document.querySelector('[data-foo]');
var rawData = fooElement.dataset.foo;
// or
var rawData = fooElement.getAttribute('data-foo');
var data = JSON.parse(rawData);
console.log(data);

...After the Page has Rendered

If the page has already loaded, and you want to access some data, just use an AJAX request. You'll be able to safely read in a JSON data source, which can be piped through JSON.parse to access the data object.

Util.inspect vs JSON.stringify

You only need util.inspect if your object is circular. If it's JSON encodable in 99.9% of cases you can just output it to the source with JSON.stringify .

Using JSON

There are edge cases to this - not only are JS objects more expressive than JSON (functions etc), JSON objects can do things JS objects can't (in edge cases of encoding). So make sure not only is your object serializable correctly, it's also deserializable correctly. I also assume you didn't do anything crazy like override the array constructor (which would make JS objects behave differently from JSON ones).

Security

As for security, unless your object can contain sensitive data (and it really shouldn't, whitelist it first) there should not be any related issues.

Overall option 2 is a standard approach that is quite commonly used - including on this very site.

  • It usually works for simple data which is most data you need to share (numbers and strings).
  • It saves the round trip.
  • It's used very often in big sites and in practice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM