Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to automatically stringify in JavaScript?

Tags:

javascript

I know I can create a toString() function on an object, so that every time it's printed or treated like a string it will first stringify the object with that function.

Is it possible to do that directly so I can use String object functions on the object?

var SomeObject = function(a, b){
    this.a = a;
    this.b = b
}

SomeObject.prototype.toString = function(){
    return [ this.a, this.b ].join(' ')
}

var objInstance = new SomeObject('this', 'that');

console.log(objInstance + '')                // This that
console.log(("" + objInstance).split(''))    // [ 't', 'h', 'i', 's', ' ', 't', 'h', 'a', 't' ]
console.log(objInstance.split())             // Error

Is it possible to do so that the object "behaves" like a string when a String function is called on it?

In other words, I'd like objInstance.split() to have the same result as ("" + objInstance).split(''), and also objInstance.length or objInstance.match(/something/), etc.

like image 254
simone Avatar asked Dec 17 '18 06:12

simone


People also ask

Can I Stringify a function in JavaScript?

The JSON.stringify() method converts a JavaScript value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.

Does JSON Stringify preserve functions?

To be clear, the output looks like JSON but in fact is just javascript. JSON. stringify works well in most cases, but "fails" with functions.

Which JavaScript method converts JSON to a JavaScript value?

parse() The JSON. parse() method parses a JSON string, constructing the JavaScript value or object described by the string.

When should I use JSON Stringify?

The JSON. stringify() method in Javascript is used to create a JSON string out of it. While developing an application using JavaScript, many times it is needed to serialize the data to strings for storing the data into a database or for sending the data to an API or web server.


3 Answers

You can let your objects inherit from String so that all string methods become available:

class SomeObject extends String {
  constructor(a, b) {
    super(a + " " + b);
    this.a = a;
    this.b = b;
  }
}

var obj = new SomeObject('this', 'that');
console.log(obj.split(""));

No need to use complicated Proxy solutions :-)

All the String.prototype methods (except for .toString, .valueOf and [Symbol.iterator]) are "intentionally generic; [they do] not require that its this value be a String object. Therefore, [they] can be transferred to other kinds of objects for use as a method." You can call them on any value, they will coerce it to a string (using .toString() or .valueOf as usual).

You don't even need to use ES6 class extends to inherit from the builtin (which also makes your string value immutable), it works in ES5 as well:

function SomeObject(a, b) {
  this.a = a;
  this.b = b;
}
SomeObject.prototype = Object.create(String.prototype);
SomeObject.prototype.constructor = SomeObject;
SomeObject.prototype.toString = function() {
    return this.a + " " + this.b;
};

var obj = new SomeObject('this', 'that');
console.log(obj.split(""));
like image 144
Bergi Avatar answered Oct 10 '22 14:10

Bergi


One option would be to return a Proxy that checks whether the property exists on String.prototype, and if it does, calls that property with the string that represents the object:

// Declare the proxy handler up front here
// to avoid unnecessary creation of duplicate handler objects
const handler = {
  get(obj, prop) {
    if (obj[prop] !== undefined) {
      return obj[prop];
    }
    const stringMethod = String.prototype[prop];
    if (stringMethod) {
      return stringMethod.bind(obj.a + ' ' + obj.b);
    }
  },
};

const SomeClass = function(a, b) {
  this.a = a;
  this.b = b
  return new Proxy(this, handler);
}

const instance = new SomeClass('this', 'that');

// String methods:
console.log(instance.trim());
console.log(instance.includes('this'));
console.log(instance.includes('somethingelse'));
console.log(instance.split(''));

// Can still assign and retrieve values directly on the object as normal:
instance.foo = 'foo';
console.log(instance.foo);
like image 25
CertainPerformance Avatar answered Oct 10 '22 14:10

CertainPerformance


One option to extend the SomeObject too, something like this.

var SomeObject = function(a, b){
    this.a = a;
    this.b = b
}

SomeObject.prototype.toString = function(){
    return [ this.a, this.b ].join(' ')
};

SomeObject.prototype.split = function() {
   return String.prototype.split.apply(this.toString(), arguments);
};

var objInstance = new SomeObject('this', 'that');

console.log(objInstance + '')                // this that
//console.log(("" + objInstance).split(''))    // [ 't', 'h', 'i', 's', ' ', 't', 'h', 'a', 't' ]
console.log(objInstance.split(''));

In a comment you've asked:

I was thinking about doing this programmatically by doing it for all functions - but is there a way to list all functions of an object?

Yes, you'd use getOwnPropertyNames on String.prototype and filter out the ones that aren't functions:

var SomeObject = function(a, b){
    this.a = a;
    this.b = b
}

SomeObject.prototype.toString = function(){
    return [ this.a, this.b ].join(' ')
};

Object.getOwnPropertyNames(String.prototype).forEach(function(name) {
    var fn = String.prototype[name];
    if (name !== "toString" && typeof fn === "function") {
        SomeObject.prototype[name] = function() {
            return fn.apply(this.toString(), arguments);
        };
    }
});

var objInstance = new SomeObject('this', 'that');

console.log(objInstance + '')                // this that
//console.log(("" + objInstance).split(''))    // [ 't', 'h', 'i', 's', ' ', 't', 'h', 'a', 't' ]
console.log(objInstance.split(''));
like image 2
Just code Avatar answered Oct 10 '22 15:10

Just code