I know the title is a bit confusing, but I have no idea to describe precisely what I mean.
I have a minimal demo below:
class A{
name: string;
age: number;
toString(){
return `
My name is: ${this.name},
My age is: ${this.age}
`
}
fromTextToData(text: string){
//Do something, regex or something?
this.name = ...;
this.age = ...;
}
}
main(){
let a = new A();
a.name = "Name 1";
a.age = 20;
let text = a.toString();
var revert = a.fromTextToData(text);
}
The best way I can think about it is using Regex expression, but if the data is large or something the structure is duplicated to its others, I think it's not really a good solution.
The second solution I can think of is using a data structure such as JSON or XML.
But I still want some suggestions from you.
That is possible, but I would first change a few things in your code:
Allow the constructor to take arguments for initialising its properties:
constructor(name, age) {
this.name = name;
this.age = age;
}
Make the reversing method a static method that returns a new instance of A instead of overwriting the properties of an existing instance. I would also just call it fromText
To better ensure that both toString and fromText rely on the same string format, define a kind of template as a static property of the A class. This template could take different forms. Here I will propose an array:
static template = ["\nMy name is: ", "\nMy age is: ", "\n"]
So the idea is that the dynamic parts get inserted between those strings. On the other hand, the reverse action can identify these strings and extract what is in between them with (.*?) as part of the regular expression.
class A {
static template = ["\nMy name is: ", "\nMy age is: ", "\n"]
constructor(name, age) {
this.name = name;
this.age = age;
}
toString(){
return [this.name, this.age]
.map((data, i) => A.template[i] + data)
.concat(A.template.slice(2))
.join("");
}
static fromText(text) {
const regex = RegExp(A.template.join("(.*?)"));
// Get the match, and extract with slice the captured groups
return new A(...text.match(regex).slice(1));
}
}
function main() {
const a = new A("Name 1", 20);
const text = a.toString();
const revert = A.fromText(text);
console.log(revert);
}
main();
Remark
If the goal is to serialise and deserialise instances, then it is better not to rely on such human-readable phrases as string representations, but use a format proven for that purpose, like JSON.
So then it would go like this:
class A {
constructor(name, age) {
this.name = name;
this.age = age;
}
toString() {
return `
My name is: ${this.name},
My age is: ${this.age}
`
}
static fromJSON(json) {
return Object.assign(Object.create(this.prototype), JSON.parse(json));
}
}
function main() {
const a = new A("Name 1", 20);
const json = JSON.stringify(a);
const revert = A.fromJSON(json);
console.log(revert);
}
main();
Related
How can I change the way String or Number casts a class?
For example, I have the class User:
class User {
constructor(name, age) {
this._name = name;
this._age = age;
}
}
How do I convert a class object to String and get the name attribute back when I convert it to String. And if I want to convert the object of the class to Number, it returns the age attribute.
Like this:
const user = new User('Kevin', 23);
console.log(String(user)) // Kevin
console.log(Number(user)) // 23
Thanks!
Use toString and valueOf methods.
class User {
constructor(name, age) {
this._name = name;
this._age = age;
}
toString() { return this._name; }
valueOf() { return this._age; }
}
const user = new User('Kevin', 23);
console.log(String(user)) // Kevin
console.log(Number(user)) // 23
I want to know why?
It doesn't look like this reduces complexity.
String(user);
Number(user);
user.string;
user.number;
user.name;
user.age;
In the example below I have two console.log's, and I'm getting different answers each time (despite my expecting to get the same answer).
class Dog {
constructor(name, gender) {
this.name = name;
this.gender = gender;
}
toString() {
return this.name;
}
}
let maxi = new Dog('john', 'male');
console.log(maxi) // Dog { name: 'john', gender: 'male' }
console.log(`${maxi}`); // john
In the first example, it seems like I'm logging the object's type and its properties. In the second example, however, it seems like my custom toString() method is being used to convert the object into a string.
Why the difference? And is there a place in the documentation that explains why string literals use the toString() method to convert objects into a string whereas console.log() doesn't. In addition, where does console.log() even pull this string representation of the object?
Thanks!
If you log objects in the last versions of chrome or firefox what you get logged is a reference to the object, and probably not the 'value' at the moment when you call the console.log(). but it's the value of the object at that time.
Reference : Mozilla
So what does fix this problem, convert your object into a string(your log 2), for example:
class Dog {
constructor(name, gender) {
this.name = name;
this.gender = gender;
}
toString() {
return this.name;
}
}
let maxi = new Dog('john', 'male');
console.log('Dog: ' + maxi) // Dog { name: 'john', gender: 'male' }
console.log(`${maxi}`); // {"name":"john","gender":"male"}
this works because I use the + operator instead of the , operator, so he will stick it into the string instead of making a reference to your object.
Summary
A variable does not store the value of the object, but a reference (the address in memory) for the value. So your copies that reference instead of the object.
As I think your requirement is to print object in string form so I have this solution below.
Oh I used arrow function here๐
๐
. you can use normal es5 function.
Thank you.
class Dog {
constructor(name, gender) {
this.name = name;
this.gender = gender;
}
toString() {
return JSON.stringify(this);
}
}
let maxi = new Dog('john', 'male');
console.log(`${maxi}`); // {"name":"john","gender":"male"}
I think that Console.log uses internaly a method toJSON in order to transport an object in to string. So you must write your own toJSON...
class Dog {
constructor(name, gender) {
this.name = name;
this.gender = gender;
}
toJSON() {
return this.name;
}
}
let maxi = new Dog('john', 'male');
console.log(maxi) // Dog { name: 'john', gender: 'male' }
console.log(`${maxi}`); // john
This really don't work... as Bergi tell me and he was right. I tried the code without remove the original: toString() { ... } and adding the toJSON() {...} and '>>>' like this: console.log('>>>'+maxi) and take output //>>>john. So, i thought that the toJSON() give the //>>>john, but it was the toString(). Thank you Bergi.
I'm sorry about the title I'm not sure what is the name of the issue I'm trying to solve.
Assume I have the following class with a function
{
printInfo(age){
console.log(name)
console.log(age)
}
}
From another page I want to call the class like this:
someClass.Mike.printInfo(21), and the function would print Mike and 21. Of course, the name Mike is variable so it can be anything.
Is there a way to accomplish that? Maybe something special in the constructor of the class? some JSON object keys manipulations?
Thank you.
Assuming you have stored the Names somewhere for example:
const names = ['Mike', 'Dave'];
you could access the function, if it is indeed stored in the regarding objects with dynamic properties like this:
names.forEach(name => {
someClass[name].printInfo(21);
})
If that is not what you need you should try to write your question a little bit more detailed.
The statement from another page is also a little bit confusing, if the class is not defined on the page than you cannot access it.
You'll need to pass any variables being used. You can do so by separating them:
printInfo(name, age)
To use it, you'd call the method like so:
SomeClass.printInfo("Mike", 21);
Create an instance for each person and pass the name in the constructor then call printInfo() from an instance to get the name and age :
class SomeClass {
constructor(name, age) {
this.name = name;
this.age = age;
}
printInfo(age){
console.log(this.name)
console.log(age || this.age)
}
}
const mike = new SomeClass('Mike');
mike.printInfo(21);
const jean = new SomeClass('Jean', 17);
jean.printInfo();
Or you can get your result using Objects:
const Mike = {
printInfo: (age) => { console.log('Mike'); console.log(age) }
};
const Jean = {
printInfo: (age) => { console.log('Jean'); console.log(age) }
};
const someClass = {
Mike,
Jean
};
someClass.Mike.printInfo(21);
someClass.Jean.printInfo(17);
// Or:
// let name = 'Mike';
// someClass[name].printInfo(21);
// name = 'Jean';
// someClass[name].printInfo(17);
Or even better:
const constructObject = (name) => ({
printInfo: (age) => { console.log(name); console.log(age) }
});
const someClass = {
Mike: constructObject('Mike'),
Jean: constructObject('Jean')
};
someClass.Mike.printInfo(21);
someClass.Jean.printInfo(17);
// Or:
// let name = 'Mike';
// someClass[name].printInfo(21);
// name = 'Jean';
// someClass[name].printInfo(17);
I want to put global property named "entities" in JS scope. Entity is basically Java class describing Person.
public class EntityJS extends ScriptableObject {
private String firstName;
private String lastName;
private Double salary;
private String email;
#Override
public String getClassName() {
return "Entity";
}
public EntityJS() {
}
public EntityJS(String firstName, String lastName, Double salary, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.salary = salary;
this.email = email;
}
public void jsConstructor() {
this.firstName = "";
this.lastName = "";
this.salary = 0.;
this.email = "";
}
public void jsSet_salary(Double value) {
this.salary = value;
}
public Double jsGet_salary() {
return this.salary;
}
public void jsSet_firstName(String value) {
this.firstName = value;
}
//the rest of getters & setters
}
Entity class is almost the same class like EntityJS, except it extends only java Object.
I want to allow javascript user modify global variable "entities". After executing user's script, I want to retrieve this object back to Java (and perform some operations later on).
I've commented interesting lines with results and expected returned values.
Here is my code of trying to execute user's code:
public String execute(String code, ObservableList<Entity> entities) {
Context context = Context.enter();
try {
Scriptable scope = context.initStandardObjects();
ScriptableObject.defineClass(scope, EntityJS.class, true, true);
EntityJS[] objects = new EntityJS[entities.size()];
for(int i = 0; i < entities.size(); ++i){
objects[i] = new EntityJS(entities.get(i).getFirstName(), entities.get(i).getLastName(), entities.get(i).getSalary(), entities.get(i).getEmail());
}
ScriptableObject.putProperty(scope, "e1", Context.javaToJS(objects, scope));
// typing "e1" (which is equal to "code" value) returns "[Lentity.EntityJS;#7959b389"
Object[] array = entities.toArray();
ScriptableObject.putProperty(scope, "e2", array);
// same for e1
Object wrappedOut = Context.javaToJS(entities, scope);
ScriptableObject.putProperty(scope, "e3", wrappedOut);
//this works quite nice, but it doesn't behave like JS object
//it returns, good-looking array:
//[Entity{firstName='Alwafaa', lastName='Abacki', salary=1000.0, email='zdzisiek#adad.com'},
//Entity{firstName='chero', lastName='Cabacki', salary=2000.0, email='bfadaw#dadaad.com'}]
//Unfortunately, if I want to get e.g. salary value I have to call
//e.get(0).getSalary() which returns string :(
//if I want to add number I have to call
//Number(e.get(0).getSalary()) to get Number
ScriptableObject.putProperty(scope, "e4", Context.javaToJS(objects[0], scope));
//this results in "TypeError: Cannot find default value for object."
Object result = context.evaluateString(scope, code, "<cmd>", 1, null);
return context.toString(result);
} catch (Exception ex) {
System.out.println(ex.getMessage());
return ex.getMessage();
} finally {
Context.exit();
}
}
I want to give user "entities" JS-like array, which could be modified e.g. this way:
entities.forEach(function(entity){entity.salary += 1000;})
I'd like salary property to be Number, of course.
Does someone know how can I approach this?
Thanks in advance
You can transfer Java Objects into JSON String on the server side and pass it to the client - javascript. When the client receives response from the server (JSON String containing your ojects), you can parse json.
You can convert Java Objects into JSON (on the server side), check this tutorials:
GSON https://www.mkyong.com/java/how-do-convert-java-object-to-from-json-format-gson-api/
Jackson https://www.mkyong.com/java/jackson-2-convert-java-object-to-from-json/
This is how to parse JSON object in js.
https://www.w3schools.com/js/js_json_parse.asp
I want to allow javascript user modify global variable "entities". After executing user's script, I want to retrieve this object back to Java (and perform some operations later on).
You can make an AJAX request to the server passing modified objects. Again, on the js side you convert these objects into JSON, then pass them to the server, and parse JSON String into a collection (array) of your objects.
This is how to convert javascript objects into JSON String
Convert JS object to JSON string
I have Java Application, which uses Rhino (as in tags). I have TextArea, where user types JS code. I need to give user the entities object.
So, pass the text entered by the user into your server, parse it. Make sure that you are passing valid JSON though.
Depends on your JS version, you can do the following:
ES5
function Person(firstName, lastName, salary, email) {
this.firstName = firstName;
this.lastName = lastName;
this.salary = salary;
this.email = email;
}
var john = new Person("john", "smith", 500, "john.smith#john.com");
var jane = new Person("jane", "doe", 650, "jane.doe#jane.net");
var people = [john, jane];
//mutating the original values
people.forEach(function(person) {person.salary += 500})
ES6
class Person {
constructor(firstName, lastName, salary, email) {
this.firstName = firstName;
this.lastName = lastName;
this.salary = salary;
this.email = email;
}
}
let john = new Person("john", "smith", 500, "john.smith#john.com");
let jane = new Person("jane", "doe", 650, "jane.doe#jane.net");
let people = [john, jane];
people.forEach(person => person.salary += 500);
Let's imagine that we have a JavaScript class:
var Person = (function () {
function Person(name, surname) {
this.name = name;
this.surname = surname;
}
Person.prototype.saySomething = function (something) {
return this.name + " " + this.surname + " says: " + something;
};
return Person;
})();
I want to iterate its methods and properties. I have no problem with the methods.
var proto = Person.prototype,
methods = Object.keys(proto);
// iterate class methods ["saySomething"]
for (var i = 0; i < methods.length; i++) {
// do something...
}
My problem comes when I want to iterate its properties:
var proto = Person.prototype,
targetInstance = new Person(), // this is my problem!
properties = Object.getOwnPropertyNames(targetInstance),
// iterate class properties ["name", "surname"]
for (var i = 0; i < properties.length; i++) {
// do something...
}
The only way that I have found is to create an instance and use Object.getOwnPropertyNames. I want to use this code as part of a framework so I will not have control over the classes defined by other developers. I want to avoid the need of creating an instance because if the constructor had some sort of validation like:
function Person(name, surname) {
if(typeof name === "undefined" || typeof surname === "undefined"){
throw new Error()
}
this.name = name;
this.surname = surname;
}
I wouldn't be able to use the code above. Do you know if it is possible to get the public properties of a class without creating an instance of it?
The properties don't exist until an object constructs them.
If your class looked like:
var Person = (function () {
Person.prototype.name = null;
Person.prototype.surname = null;
function Person(name, surname) {
this.name = name;
this.surname = surname;
}
Person.prototype.saySomething = function (something) {
return this.name + " " + this.surname + " says: " + something;
};
return Person;
})();
you would see name and surname too, but of course you can't count on the objects looking like that.
Do you know if it is possible to get the public properties of a class without creating an instance of it?
If you are talking about runtime them no, not without ugly hacks like toString (which gives you a string representation of the function body).
However you can get these at compile time using the TypeScript language service and then do code generation to assist the runtime (https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API).
Neither of these are trivial.