How add methods from a class to a JSON object - javascript

I have a class defined in Javascript with some members and methods. I also have a JSON object that has the same member variables as my class but, obviously, not the methods. What is the easiest way to convert my JSON object to an instance of the class?
Below some code that explains better my case. I've tried to use Object.assign without success. Can this be done in a one liner?
function Thing(a, b){
this.a = a;
this.b = b;
this.sum = function(){ return this.a + this.b; };
this.printSum = function(){ console.log (this.sum()); };
};
// test it works
z = new Thing(4,3);
z.printSum(); // shows 7
// attempt with Object.assign
y = JSON.parse('{"a": 5, "b": 4}'); // initialize y from JSON object
console.log(y);
Object.assign(y, new Thing()); // trying to copy methods of Thing into y
console.log(y); // shows both a and b undefined (assign overwrited also a and b)
y.printSum(); // shows NaN
// trying Object.assing the other way around
y = JSON.parse('{"a": 5, "b": 4}');
Object.assign(new Thing(), y); // trying the other way around
console.log(y); // methods from thing are not present now
y.printSum(); // gives error y.printSum is not a function (obvious, as it is not present)

You rather need to change the sum function to make it return sum of this.a and this.b.
Also, instead of Object.assign, you need to change the prototype of the variable y so that the methods are available to it.
function Thing(a, b){
this.a = a;
this.b = b;
this.sum = function(){ return this.a + this.b; };
this.printSum = function(){ console.log (this.sum()); };
};
// test it works
z = new Thing(4,3);
z.printSum(); // shows 7
// attempt with Object.assign
y = JSON.parse('{"a": 5, "b": 4}'); // initialize y from JSON object
console.log(y);
y.__proto__ = new Thing(); // trying to copy methods of Thing into y
console.log(y); // shows both a and b undefined (assign overwrited also a and b)
y.printSum();

Do you mind to make some changes? Lets change Thing input params to Object. And then you can easily pass parsed json into it.
Is it suitable for you?
function Thing(obj) {
this.a = obj.a;
this.b = obj.b;
this.sum = function(){ return this.a + this.b; };
this.printSum = function(){ console.log (this.sum()); };
};
y = JSON.parse('{"a": 5, "b": 4}');
t = new Thing(y);
t.printSum();
It possible to add object as optinal param:
function Thing(a, b, obj = null) {
if (!obj) {
this.a = a;
this.b = b;
} else {
this.a = obj.a;
this.b = obj.b;
}
this.sum = function(){ return this.a + this.b; };
this.printSum = function(){ console.log (this.sum()); };
};
y = JSON.parse('{"a": 5, "b": 4}');
t = new Thing(null, null, y);
t.printSum();
tt = new Thing(5, 4);
t.printSum();

Related

how to make properties of an object update correctly when used in a new object?

https://jsfiddle.net/ca4xvkzb/
function foo(a,b)
{
this.a = a
this.b = b
this.bar = a+b
}
var z = new foo();
z.a = 2
z.b = 3
console.log(z.bar)
what currently happens:
z.bar returns NaN, further testing implied that whilst z.a and z.b were having their values updated, this.bar of z was using the old undefined values for a and b, rather than the updated ones
what i need to happen:
z.bar should correctly return the value of a+b: 5, after they've been set after the objects definition, as i would like to use the foo object for more variables than just z
You can use it like that :
function foo(a,b)
{
this.a = a
this.b = b
this.bar = ()=> this.a + this.b
}
var z = new foo();
z.a = 2
z.b = 3
console.log(z.bar())
As commented by walidum you are creating the bar function as a ownProperty of the foo instance. I think it would make more sense to include bar as a function of the foo class per prototype:
function foo(a,b) {
this.a = a;
this.b = b;
}
foo.prototype.bar = function(){ return this.a + this.b; };
var z = new foo();
z.a = 2
z.b = 3
console.log(z.bar()); // = 5
console.log(z.hasOwnProperty('a')); // = true -- because it's something that belongs to THIS very instance of foo
console.log(z.hasOwnProperty('bar')); // = false -- because the function does not belong to the instance, but to the class (all instances of) foo.

Experiment: Can a JavaScript instance be a constructor itself & have its own set of instances?

let bar;
function foo(){
this.x=x;
this.y=y;
}
bar=new foo(1,2);
/*Since all functions in javascript are objects.
Why can't an instance an object be a constructor itself?*/
!function bar(a,b){
this.a=a;
this.b=b;
}(); // (1)
let bar1=new bar(3,4);
console.log(bar1.a);
(1): Using a named IIFE, can bar be an instance and a constructor at the same time?
It's just a fun trick to exploit the language's freedom to see if it is a possibility, any other ways of doing this?
It sounds like you are looking for a constructor-function-returning function:
function makeBar(x, y) {
function Bar(a, b) {
this.a = a;
this.b = y + b;
}
Bar.x = x;
return Bar;
}
var Bar1 = makeBar("bar1", 2);
console.log(Bar1.x); // "bar1"
var myBar1 = new Bar1(1, 2); // Bar { a: 1, b: 4 }
console.log(myBar1 instanceof Bar1); // true
var Bar2 = makeBar("bar2", 0);
var myBar2 = new Bar2(3, 3); // Bar { a: 3, b: 3 }
console.log(myBar2 instanceof Bar1); // false
To make makeBar not a factory function but a constructor with a working prototype, you'd have to use
function Foo(x, y) {
function Bar(a, b) {
this.a = a;
this.b = y + b;
}
Object.setPrototypeOf(Bar, Foo.prototype);
Bar.x = x;
return Bar;
}
Foo.prototype.log = function() {
console.log("I'm "+this.name);
};
var Bar = new Foo("bar", 2);
Bar.log();
console.log(Bar instanceof Foo); // true
var myBar = new Bar(1, 2);
console.log(myBar instanceof Bar); // true
Can a JavaScript instance be a constructor itself & have its own set of instances?
Sure, a function is an instance of Function and a constructor is a function.
So you can just have
const instance1 = new Function("a", "this.a = a;");
const instance2 = new instance1("foo");
console.log(instance2.a); // "foo"
If you need instance1 to be more than a raw Function, you can even extend this constructor:
class MyFunc extends Function {
doSomethingMore(){ console.log("I'm doing more"); }
}
const instance1 = new MyFunc("a", "this.a = a;");
instance1.doSomethingMore();
const instance2 = new instance1("foo");
console.log(instance2.a); // "foo"
But I can't see a clear reason why you'd ever want to do that...
(Note that dynamically creating a function via the Function constructor is generally less "performant" than at parse time through function expression or statement.)
In your code, since bar is declared with let, it can't be re-declared as a function declaration in the same scope.
If you wanted to do this, you could have foo explicitly return a function, which could then be called with new:
function foo(x, y) {
this.x = x;
this.y = y;
return function bar(a, b) {
this.a = a;
this.b = b;
};
}
const bar = new foo(1, 2);
const bar1 = new bar(3, 4);
console.log(bar1.a);

JS Class: smartest way to create a variable inside a class without being called by the constructor

I want to create a variable inside a javascript class without the necessity to call it by a variable with the constructor.
for example, we have a class A that takes in 2 inputs (a and b). a and b are summed and the result is appended to an array arr. Where´s the best place to declare the array?
class A {
constructor(a,b) {
this.a = a;
this.b = b;};
do_something(this.a, this.b) {
let c = this.a + this.b;
arr.append(c);
return arr;}
};
where should i initialize arr without having it to be called form the outside ?
At the beginning of the class definition, also you can not have this.a and this.b as parameters of the do_something.
class A {
arr = []
constructor(a,b) {
this.a = a
this.b = b
}
do_something() {
const c = this.a + this.b
this.arr.push(c)
return [this.arr, c]}
}
const obj = new A(1,2)
console.log(obj.do_something())

JS ES6: Get parameters as an object with destructuring

Is it possible to get the function's arguments as an object (in order to iterate on it) using destructuring?
function({a=1, b=2, c=3}={}) {
// how to get {a:1, b:2, c:3}?
}
My goal here is to bind each parameter to this in a class constructor.
It is possible without destructuring:
class Test {
constructor(args) {
Object.assign(this, args);
}
}
But I don't know how to simplify that:
class Test {
constructor({a=1, b=2, c=3}={}) {
this.a = a;
this.b = b;
this.c = c;
}
}
let test = new Test();
// test.a = 1
// test.b = 2 etc.
You can do this, using the shorthand form of object creation:
class Test {
constructor({a=1, b=2, c=3}={}) {
Object.assign(this, {a, b, c});
}
}
Example:
class Test {
constructor({a=1, b=2, c=3}={}) {
Object.assign(this, {a, b, c});
}
}
const t1 = new Test();
console.log("t1:", t1.a, t1.b, t1.c);
const t2 = new Test({b: 42});
console.log("t2:", t2.a, t2.b, t2.c);
Alternately, don't use destructuring, and use multiple arguments to Object.assign:
class Test {
constructor(options = {}) {
Object.assign(this, Test.defaults, options);
}
}
Test.defaults = {a: 1, b: 2, c: 3};
// Usage:
const t1 = new Test();
console.log("t1:", t1.a, t1.b, t1.c);
const t2 = new Test({b: 42});
console.log("t2:", t2.a, t2.b, t2.c);
...and if you want any of those as discrete things you can reference by name, you can either just use this.a (and this.b and this.c) to do it, or you can do:
let {a, b, c} = this;
...afterward and use those. (Just beware that assigning to the resulting a, b, and c won't update the object.)

'this' context during object creation

I am trying to do something like this:
var test = {
a: 10,
b: 20,
c: (this.a+this.b)
};
but it doesn't work. How can I access the test.a from within test.c?
Is it possible?
It's not possible to reference "this" in an expression specifying an object literal. Either do it in a following line or use a constructor like this:
function myobj(a,b) {
this.a = a;
this.b = b;
this.c = this.a + this.b;
}
var test = new myobj(10,20);
In response to which method is faster, creation with the object constructor is faster. Here's a simple test case comparison. Run it yourself on JSBIN.
The results show that the object creation with a constructor vs an object literal is almost twice as fast:
0.450s : testObjectLiteral
0.506s : testObjectLiteralWithFunction
0.280s : testConstructor
Here's the test code inlined as well:
// timer function
function time(scope){
time.scope = time.scope || {};
if(time.scope[scope]) {
var duration = (new Date()).getTime()-time.scope[scope];
time.scope[scope] = null;
var results = document.getElementById("results");
results.innerHTML = results.innerHTML + '<p>'+(duration/1000).toFixed(3)+'s : '+scope+'</p>';
} else {
time.scope[scope] = (new Date()).getTime();
}
}
// object creation function with constructor
function myobj(a,b) {
this.a = a;
this.b = b;
this.c = this.a + this.b;
}
function testConstructor(iterations) {
var objs = new Array(iterations);
for(i=0;i<iterations;i++) {
objs[i] = new myobj(i,i+1);
}
return objs;
}
function testObjectLiteralWithFunction(iterations) {
var objs = new Array(iterations);
for(i=0;i<iterations;i++) {
objs[i] = {
a: i,
b: i+1,
c: function() {
return this.a + this.b;
}
};
}
return objs;
}
function testObjectLiteral(iterations) {
var objs = new Array(iterations);
for(i=0;i<iterations;i++) {
var item = {
a: i,
b: i+1
};
item.c = item.a + item.b;
objs[i] = item;
}
return objs;
}
var ITERATIONS = 1000000;
time("testObjectLiteral");
testObjectLiteral(ITERATIONS);
time("testObjectLiteral");
time("testObjectLiteralWithFunction");
testObjectLiteralWithFunction(ITERATIONS);
time("testObjectLiteralWithFunction");
time("testConstructor");
testConstructor(ITERATIONS);
time("testConstructor");
​
It's not possible within an object literal since this cannot be made to refer to an object that has not yet been created. Your best option is to assign the c property in a separate step:
var test = {
a: 10,
b: 20
};
test.c = test.a + test.b;
You simply can't do this when declaring an object literal, the closest you can do is:
var test = {
a: 10,
b: 20
};
test.c = test.a + test.b;
In your context this refers to whatever parent context you're in, not the test object...and even if it did, you can't declare members like that, for example this is also invalid:
var test = { a: 10, b: 20, test.c: test.a + test.b };
...because test, a and b aren't defined yet, since it's a single statement that hasn't completed.
Why not make c a function so that it always returns the current value of a+b?
var test = {
a: 5,
b: 1,
c: function() {
return this.a + this.b;
}
}

Categories

Resources