Javascript class setter on each attribute - javascript

I am trying to create a class that has a number of objects assigned as attributes. I am stuck on implementing setters on these attributes.
Failing example below. When I do record1.foo = 'bar'; // record.foo is now a string I overwrite the attribute with a string, rather than set the value inside the Element class.
Hope this makes sense, I would like a setter to write the value into the Element rather than replace it.
'use strict';
const assert = require('chai').assert;
class Element {
constructor(name) {
this.name = name;
this.value = null;
}
getValue() {
return this.value;
}
setValue(value) {
this.value = value;
}
getName() {
return this.name;
}
toString() {
return this.getValue();
}
}
class Record {
constructor() {
this.fields = ['name', 'age', 'foo'];
this.fields.forEach((field) => {
this[field] = new Element(field);
});
}
setValue(field, value) {
this[field].setValue(value);
}
getValue(field) {
return this[field].getValue();
}
}
let record1 = new Record();
record1.name.setValue('Bob');
record1.setValue('age', 42);
assert.equal(record1.getValue('name'), 'Bob');
assert.equal(record1.age, 42);
console.log('so far so good');
record1.foo = 'bar'; // record.foo is now a string
assert.equal(record1.getValue('foo'), bar);

As a dynamic way to do it the way you want, try this:
'use strict';
class Element {
constructor(name) {
this.name = name;
this.value = null;
}
getValue() {
return this.value;
}
setValue(value) {
this.value = value;
}
getName() {
return this.name;
}
toString() {
return this.getValue();
}
}
class Record {
constructor() {
this.fields = ['name', 'age', 'foo'];
this.fields.forEach((field) => {
let element = new Element(field);
Object.defineProperty(this, field, {
set: function(value) {
element.setValue(value);
},
get: function() {
return element;
}
});
});
}
setValue(field, value) {
this[field].setValue(value);
}
getValue(field) {
return this[field].getValue();
}
}
let record1 = new Record();
record1.name.setValue('Bob');
record1.setValue('age', 42);
console.log(record1.getValue('name') === 'Bob');
console.log(record1.age === 42);
record1.foo = 'bar';
console.log(record1.getValue('foo') === 'bar');

As far as I can tell, this is what you want:
class Delegator {
set foo (a) {
this._foo.value = a;
}
get foo () {
return this._foo.value;
}
constructor () {
let fields = ['foo', 'value', 'name'];
fields.forEach(fld => this[`_${fld}`] = new Element(fld));
}
}
let instance = new Delegator();
instance.foo; // returns Element's value of foo
instance.foo = 'bar'; // sets Element's value to bar

Related

Lebab cannot convert if there are fields

I'm trying to convert es5 to es6 using lebab.
lebab es5.js -o es6.js --transform class
if I convert simple code everything is fine. For example:
var className = function(values) { this.__values = values; };
className.func = function() { return "test"; };
className.func2 = function() { return "test2"; };
className.prototype = {
_values: null,
put: function() { return 0; },
get_length: function() { return this._values.length; }
};
To
class com_cie_json_JsonArray {
constructor(values) {
this._values = values;
}
static func1() {
return "test1";
}
static func2() {
return "test2";
}
put() {
return 0;
}
get_length() {
return this._values.length;
}
}
But if I insert more fields then the Lebab either partially or not translates these pieces at all. This example not translated code.
var className = function(values) {
this.__values = values;
};
className.func = function() {
return "test";
};
className.func2 = function() {
return "test2";
};
$classes[2] = $classes["name.to.className"] = className;
className.__name__ = ["name","to","className"];
className.prototype = {
_values: null,
put: function(value) {
return 0;
},
get_length: function() {
return this._values.length;
},
__class__: className,
__properties__: {get_length:"get_length"}
};
The question is how this type of code will look in ec6. And if it’s possible how to tell the Lebab to convert it.
var className = function() {
};
$classes[2] = $classes["name.to.className"] = className;
className.__name__ = ["name","to","className"];
className.prototype = {
_values: null,
__class__: className,
__properties__: {get_length:"get_length"}
};
$classes[2] = $classes["name.to.className"] = className;
className.__name__ = ["name","to","className"];

can't use filter function on array

I learn how to code in javascript. I have always error: "Cannot read property 'filter' of undefined". What am I doing wrong here and why?
I have to build A class with Singleton pattern and B class which will be observer of A class.
I have to add some instances of B class to A as subscribers (observers) and unsubscribe any of it when random value I from A class is bigger than random value P from B class.
var A = (function()
{
// Instance stores a reference to the Singleton
var instance;
function init() {
// Singleton
var i = 0;
let observers = new Array();
function CheckIfGreaterThanI(observer)
{
console.log("CHECKING");
return observer.getP() > this.getI();
}
return {
subscribe: function(observer)
{
console.log("DODAJĘ");
observers.push(observer);
},
unsubscribe: function(observerss)
{
console.log("USUWAM");
for(i=0;i<observerss.length;i++)
{
var index = this.observers.indexOf(observerss[i])
if (~index)
{
this.observers.splice(index, 1);
}
}
},
notify: function()
{
for(let observer of observers)
{
observer.update();
}
},
getI: function()
{
return this.i;
},
setI: function(value)
{
this.i = value;
this.notify();
///THAT'S THE PLACE WHERE ERROR RISES
var observersToUnsubscribe = this.observers.filter(this.CheckIfGreaterThanI);
this.unsubscribe(observersToUnsubscribe);
}
};
};
return
{
// Get the Singleton instance if one exists
// or create one if it doesn't
getInstance: function () {
if ( !instance ) {
instance = init();
}
return instance;
}
};
})();
function B (name,value,a) //observer
{
this.Name = name;
this.P = value;
this.A = a;
}
B.prototype =
{
constructor:B,
getName : function()
{
return this.Name;
},
getP : function()
{
return this.P;
},
update : function()
{
if(A.getInstance().getI()<this.P)
{
console.log("OK - " + this.Name);
}
}
};
for(i=0;i<10;i++)
{
var bObject = new B(i,Math.random(),A.getInstance());
A.getInstance().subscribe(bObject);
}
var ChangeIValue = function()
{
A.getInstance().setI(Math.random());
}
setTimeout(function run()
{
ChangeIValue();
setTimeout(run,1000);
}
, 1000);
OK, I resolved this problem alone and there were many mistakes behind it, so I added my solution for that:
var A = (function()
{
// Instance stores a reference to the Singleton
var instance;
function init() {
// Singleton
var i = 0;
var observers =[];
function CheckIfGreaterThanI(observer)
{
return observer.getP() > i;
}
return {
subscribe: function(observer)
{
observers.push(observer);
},
unsubscribe: function(observersToUnsubscribe)
{
for(let observer of observersToUnsubscribe)
{
var index = observers.indexOf(observer);
if(index!=-1)
{
observers.splice(index,1);
}
}
},
notify: function()
{
for(let observer of observers)
{
observer.update();
}
},
getI: function()
{
return i;
},
setI: function(value)
{
i = value;
this.notify();
var observersToUnsubscribe = observers.filter(CheckIfGreaterThanI);
this.unsubscribe(observersToUnsubscribe);
return;
}
};
};
return {
// Get the Singleton instance if one exists
// or create one if it doesn't
getInstance: function ()
{
if ( !instance )
{
instance = init();
}
return instance;
}
};
})();
function B (name,value,a) //observer
{
this.Name = name;
this.P = value;
this.A = a;
this.getName = function()
{
return this.Name;
};
this.getP = function()
{
return this.P;
};
this.update = function()
{
if(A.getInstance().getI()<this.P)
{
console.log("OK - " + this.Name);
}
};
};
for(j=0;j<10;j++)
{
var bObject = new B(j,Math.random(),A.getInstance());
A.getInstance().subscribe(bObject);
}
var ChangeIValue = function()
{
A.getInstance().setI(Math.random());
}
setTimeout(function run()
{
ChangeIValue();
setTimeout(run,1000);
}
, 1000);

Aliasing or otherwise merging two identical object prototypes with different names

I've got two object prototypes like this:
function Tag(name, description) {
this.name = name;
this.description = description || null;
}
function Category(name, description) {
this.name = name;
this.description = description || null;
}
Both of them are exactly the same, which seems awkward. Is it possible to merge them both into an object named 'Entity', and refer to them both by different names (the original 'Tag' and 'Category')?
This may be further complicated by the fact I need to refer to the current prototype name inside the prototype.
Tag.prototype.toJSON = function() {
return {
__type: 'Tag',
name: this.name,
description: this.description
};
};
How can I apply the same 'toJSON' extension to the 'Entity' object, but make sure it returns 'Tag' or 'Category' in the '__type' field, dependent on which object is being used?
I would do something like this:
Dummy = function () {};
Entity = function (name) {
this.name = name;
};
Entity.prototype.toString = function () {
return "My name is " + this.name + ".";
};
A = function () {
Entity.call(this, 'A');
};
Dummy.prototype = Entity.prototype;
Dummy.prototype.constructor = A;
A.prototype = new Dummy();
B = function () {
Entity.call(this, 'B');
};
Dummy.prototype = Entity.prototype;
Dummy.prototype.constructor = B;
B.prototype = new Dummy();
document.body.innerHTML = ""
+ (new A()) + "<br />"
+ (new B());
Here is a small function to make things cleaner (hopefully):
function Nothing () {};
function extend (Sup, proto) {
function Class () {
if (this.init) {
this.init.apply(this, arguments);
}
}
Nothing.prototype = Sup.prototype;
Nothing.prototype.constructor = Sup;
Class.prototype = new Nothing();
delete Nothing.prototype;
for (var k in proto) {
Class.prototype[k] = proto[k];
}
return Class;
}
Here is how to use it:
Entity = extend(Nothing, {
init: function (name) {
this.name = name;
},
toString: function () {
return "My name is " + this.name + ".";
}
});
A = extend(Entity, {
init: function () {
var sup = Entity.prototype;
sup.init.call(this, 'A');
}
});
B = extend(Entity, {
init: function () {
var sup = Entity.prototype;
sup.init.call(this, 'B');
}
});

setters and getters in javascript

I am trying to learn classes and setters and getters in JavaScript .. but my code isn't working .. it alerts undefined .. here is my code
function Person () {
name:"something"
Person.prototype = {
get Name (){
return name;
},
set Name (val){
this.name = val;
}
};
};
var person = new Person();
alert(person.name);
This is the right way to set getters and setters in your example:
function Person () {
this.name = "something";
}
Person.prototype = {
get Name() {
return this.name;
},
set Name(val) {
this.name = val;
}
};
var person = new Person();
person.Name = 'example';
alert(person.name);
JS is based on prototypes, then u can define propertys:
function Person(){
this._name = '';
}
Object.defineProperty(Person.prototype, "name", {
get: function(){
return this._name;
},
set: function(value){
this._name= value;
},
enumerable:true,
configurable: true
});
Then u can set or get property "name"
var p = new Person()
p.name = 'Stackoverflow'
alert(p.name) // Stackoverflow
In ES6 u can use the keyword class, example:
class Person {
constructor() {
this.name = '';
}
}

Write getters as a prototype

I'm working on making performance updates on my javascript code.
In Firefox I got this warning:
mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create
I wrote some scripts to prove this, and the results are great: without mutation a simple script runs 66% faster.
But I have trouble converting my code without mutation, I can't write the getters:
This is what I have now:
// Class
function FooBar(options) {
this.options = options;
}
// Prototype
FooBar.prototype = {
// Getters
get a() {
return this.options.a;
},
get b() {
return this.options.b;
},
get ab() {
return this.options.a + this.options.b;
},
// Methods
displayOptions: function() {
console.log(this.options);
}
};
// Code
var options = {
a: 'foo',
b: 'bar'
};
var fooBar = new FooBar(options);
console.log(fooBar.a);
console.log(fooBar.b);
console.log(fooBar.ab);
fooBar.displayOptions();
The getters as a prototype using the this keyword in their return are the problem.
If I use Object.defineProperty the this keyword is wrong, unless I do it inside the constructor, but it would recreate the property on each instance of the class and slow my code down even further.
This works (I just messed up the syntax in my previous attempt):
// Class
function FooBar (options) {
this.options = options;
}
//Prototype getters
Object.defineProperty(FooBar.prototype, 'a', {
get: function() {
return this.options.a;
}
});
Object.defineProperty(FooBar.prototype, 'b', {
get: function() {
return this.options.b;
}
});
Object.defineProperty(FooBar.prototype, 'ab', {
get: function() {
return this.options.a + this.options.b;
}
});
// Methods
FooBar.prototype.displayOptions = function() {
console.log(this.options);
};
// Code
var options = {
a:'foo',
b:'bar'
};
var fooBar = new FooBar (options);
console.log(fooBar.a);
console.log(fooBar.b);
console.log(fooBar.ab);
fooBar.displayOptions();
For those who are curious about the benefits of converting scripts like this to run faster: Run following code and look to your output in the console (Chrome - 66% faster, Firefox - no difference (curious, since I got the warning from Firefox)):
// WITHOUT PROTOTYPING
var Person1 = function() {
this.name = 'myName';
this.changeName = function(name) {
this.name = name;
};
this.changeName2 = function(name) {
this.name = name;
};
this.changeName3 = function(name) {
this.name = name;
};
this.changeName4 = function(name) {
this.name = name;
};
}
// WITH PROTOTYPING, WITH MUTATION
var Person2 = function() {
this.name = 'myName';
}
Person2.prototype = {
changeName: function(name) {
this.name = name;
},
changeName2: function(name) {
this.name = name;
},
changeName3: function(name) {
this.name = name;
},
changeName4: function(name) {
this.name = name;
}
};
// WITH PROTOTYPING, WITHOUT MUTATION
var Person3 = function() {
this.name = 'myName';
}
Person3.prototype.changeName = function(name) {
this.name = name;
};
Person3.prototype.changeName2 = function(name) {
this.name = name;
};
Person3.prototype.changeName3 = function(name) {
this.name = name;
};
Person3.prototype.changeName4 = function(name) {
this.name = name;
};
// DO THE TEST
var i=0, len=1000000;
// TEST1
window.performance.mark('mark_test_start');
for(i=0;i<len;i++) {
p = new Person1();
p.changeName('myName2');
}
window.performance.mark('mark_test_end');
window.performance.measure('no-prototyping', 'mark_test_start', 'mark_test_end');
// TEST2
window.performance.mark('mark_test2_start');
for(i=0;i<len;i++) {
p = new Person2();
p.changeName('myName2');
}
window.performance.mark('mark_test2_end');
window.performance.measure('prototyping-with-mutation', 'mark_test2_start', 'mark_test2_end');
// TEST3
window.performance.mark('mark_test3_start');
for(i=0;i<len;i++) {
p = new Person2();
p.changeName('myName2');
}
window.performance.mark('mark_test3_end');
window.performance.measure('prototyping-without-mutation', 'mark_test3_start', 'mark_test3_end');
// OUTPUT tests
var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
var req = items[i];
console.log(req.name + ': ' + req.duration.toFixed(2));
}

Categories