for example, if you are doing some kind of eval on a string generated elsewhere

(assumes your Object is JSON-serializable)

The Problem

there’s a lot that can go wrong with a naive approach:

  • if you simply inject the result of toString, you may have issues deserializing later
    • you may be tempted to pass a “simple” number between contexts, but does your parser correctly handle: NaN, Infinity, negatives, decimals?
    • you will probably end up with something like [object Object] for non-trivial types
  • if you simply inject the result of JSON.stringify, you will have issues with newlines, delimiters, quotes, escaping, etc.
  • if you simply inject the result of btoa, you will have issues with Unicode

The Solution

// on the backend
 
const object_for_frontend = { myCoolParams: true }
 
function bytesToBase64 (bytes) {
	return btoa(Array.from(bytes, function (byte) {return String.fromCodePoint(byte);}).join(""))
}
 
const b64params_for_frontend = bytesToBase64(new TextEncoder().encode(JSON.stringify(object_for_frontend)))
 
const str_for_eval = `object_from_backend = JSON.parse(new TextDecoder().decode(base64ToBytes("${b64params_for_frontend}")))`
// on the frontend
 
// (you could embed the below function in your "str_for_eval" above if you want)
function base64ToBytes (base64) {
	return Uint8Array.from(atob(base64), function (m) {return m.codePointAt(0);})
}
 
eval(str_for_eval)
 
// object_from_backend === { myCoolParams: true }