Reseting a Javascript Class to its Default values - javascript

Pretty silly question, but I was thinking about this randomly earlier and I've never tried doing it... let's say I have a class like so:
function Drug () {
this.name = '';
this.type = '';
}
Later on in the page I set some values to this Drug:
var drug = new Drug();
drug.name = 'Testing';
drug.type = 'TestType';
Is there a simple way to let's say restart this instance of Drug to its defaults without just doing something like:
var drug2 = new Drug();
Like some sort of reset function I can create inside of Drug to just do drug.reset(); ?
I tried: this.clear = function () { return new Drug(); } but that doesn't do the trick...

You're overthinking it just a little. Instead of directly initializing those properties in the constructor, create an init() method which initializes them, called by the constructor. You can then call it to reinitialize the object's properties.
function Drug () {
this.init();
}
Drug.prototype.init = function() {
this.name = '';
this.type = '';
}
// Instantiate:
var d = new Drug();
d.name = 'Testing';
console.log(d);
// Drug {name: "Testing", type: ""}
// Re-initialize it
d.init();
console.log(d);
// Drug {name: "", type: ""}
This certainly isn't the only way you could handle it, but the first that comes to mind.

With this way you can redifine later your default values or add/remove them
function Drug() {
this.init();
}
// initializer
(function(obj) {
obj.prototype.init = function() {
for ( var k in obj.prototype ) {
this[ k ] = obj.prototype[ k ];
}
}
}(Drug));
// define here all default properties
Drug.prototype.name = '';
Drug.prototype.type = '';
var drug = new Drug();
drug.name = 'Testing';
drug.type = 'TestType';
console.log( drug );
drug.init();
console.log( drug );
try it yourself here: http://jsfiddle.net/bukfixart/vzsFw/

A rather general form of providing publicly-accessible properties in a class.
From here, you could build in a config object (in the constructor), to run overtop of the defaults.
var Drug = function (cfg) {
var defaults = {
key : "val"
};
this.init = function () {
var key, val;
for (key in defaults) {
if (defaults.hasOwnProperty (key)) {
val = defaults[key];
this[key] = val;
}
}
if (cfg) {
for (key in cfg) {
if (cfg.hasOwnProperty(key)) {
val = cfg[key];
this[key] = val;
}
}
}
};
this.reset = this.init;
this.init();
};
Here .reset() is just sugar on top of .init().
If you wanted, though, you could have the application of defaults and the application of config separated, so that .reset() ignored the config and just used defaults...
There are a billion ways of doing this, but here's one that would let you keep private state of this particular created object, while resetting the publicly-exposed properties.

Related

JavaScript Class Getter/Setter

is there a way to listen for a property call on a JavaScript Class
for example when i go something like this:
myForm = new Form();
myForm.name = 'Name';
-> when i set the name i dont only want to set the property but i also want to update my Vuex store.
Same thing with get i would like to read from Vuex store.
I knoew there are thins like Proxy but for this i need to wrap my Class with a Proxy object. Not so sure if i like this.
module.exports = new Proxy(new Form({}), {
get (receiver, name) {
console.log('getting property from Vuex Store');
}
});
What i need is something like this:
module.exports = class Form {
//this should be triggered when form.something
get(property) {
return this[property];
}
//this should be triggered when from.something = 'something'
set(property, value) {
return this[property] = value;
}
};
it there a best practice for this?
Javascript supports getters and setters
class Form{
set foo(val){
console.log("setting foo")
this.fooValue = val;
}
get foo(){
console.log("getting foo");
return this.fooValue;
}
}
let frm = new Form();
frm.foo = "bar";
console.log(frm.foo);
You could make this more dynamic by writing a withGetterSetter method which wraps each property of an object with a getter/setter.
var form = {
a: "aValue",
b: "bValue"
}
function withGetterSetter(obj){
var keys = Object.keys(obj);
var result = {};
for(var i=0;i<keys.length;i++){
var key = keys[i];
result[key+"_internal"] = obj[key];
(function(k){
Object.defineProperty(result,k, {
get:function() {
console.log("getting property:",k);
return this[k + "_internal"];
},
set: function(x) {
console.log("setting property:",k);
this[k + "_internal"] = x
}
});
})(key)
}
return result;
}
var setterObj = withGetterSetter(form);
console.log(setterObj.a);
setterObj.a = "updated";
console.log(setterObj.a);
It works by copying each property p to a new object with p_internal and creating a dynamic get/set for the original property name.

How to access form data from a setter without using object literal values?

I need to add setter to the below JavaScript Module:
In the below code I am simply returning the form data to the module object.
I need to add setter functionality so that I can do minimal check on the user input.
var Mod = (function(){
var module = {};
var element = document.forms.[0];
Object.defineProperty(module, 'Country', {
get: function () {
return element.txtCountry.value;
}
});
Object.defineProperty(module, 'City', {
get: function () {
return element.txtCity.value;
}
});
return module;
})();
However, all of the examples I have come across, including those on MDN shows an object with literal values:
Like this one:
var module = {
Country: "United States",
get function() {
return this.Country;
},
set function(x) {
this.Country = x + ' ' + somethingElse;
}
};
How do I add the setter to return data to the object without literal object members?
Finally I am calling the module like this:
var btn = document.getElementById( 'btnDataEntry' );
var result = document.getElementById('result');
btn.addEventListener('click', function(e) {
var t = document.createTextNode(Mod.Country + ',' + Mod.City);
result.appendChild(t);
e.preventDefault();
}, false);
Update (Additional Info):
In the most simplest form I want to perform checks in the setter, something like this:
var Mod = (function(){
var module = {};
var element = document.forms.dataEntry;
Object.defineProperty(module, 'Country', {
get: function () {
return Country;
},
set: function(val) {
if( val == 'A') {
val = element.txtCountry.value;
}
}
});
return module;
})();
Update: (Solution).
So as simple as this may seem, it can become confusing because JavaScript is more abstract when it comes to how one can accomplish certain task.
The problem is, when using setter in an Object.defineProperty() method, you have to pass the value using a dot notation to the object, and by also using a variable within the scope of the function to emulate a private member.
If you look at my previous code, you will see that I was passing the form data directly within the getter, this defeats the entire purpose of having a getter/setter.
Here is a complete working code: Based on readings and example from the following book: The Principles of Object-Oriented JavaScript: By Nicholas C. Zakas.
Code:
var LocationData = (function(){
var location = {};
//Private member to eliminate global scope
var _country;
Object.defineProperty(location, "Country", {
get: function() {
return this._country;
},
set: function(value) {
if(value === 'A') {
this._country = value;
} else {
this._country = 'X';
}
}
});
return location;
})();
var btn = document.getElementById( 'btnDataEntry' );
var result = document.getElementById('result');
btn.addEventListener('click', function(e) {
var element = document.forms[0];
//Pass the value to the method
LocationData.Country = element.txtCountry.value;
var t = document.createTextNode(LocationData.Country);
result.appendChild(t);
e.preventDefault();
}, false);
Define the setter in the same defineProperty call where you define the getter:
Object.defineProperty(module, 'City', {
get: function () {
return element.txtCity.value;
},
set: function (value) {
// do minimal check
element.txtCity.value = value;
}
});

Why doesn't JavaScript ES6 support multi-constructor classes?

I want to write my Javascript class like below.
class Option {
constructor() {
this.autoLoad = false;
}
constructor(key, value) {
this[key] = value;
}
constructor(key, value, autoLoad) {
this[key] = value;
this.autoLoad = autoLoad || false;
}
}
I think it would be nice if we can write out class in this way.
Expect to happen:
var option1 = new Option(); // option1 = {autoLoad: false}
var option2 = new Option('foo', 'bar',); // option2 = {foo: 'bar'}
var option3 = new Option('foo', 'bar', false); // option3 = {foo: 'bar', autoLoad: false}
I want to write my Javascript class like below
You can't, in the same way you can't overload standard functions like that. What you can do is use the arguments object to query the number of arguments passed:
class Option {
constructor(key, value, autoLoad) {
// new Option()
if(!arguments.length) {
this.autoLoad = false;
}
// new Option(a, [b, [c]])
else {
this[key] = value;
this.autoLoad = autoLoad || false;
}
}
}
Babel REPL Example
Of course (with your updated example), you could take the approach that you don't care about the number of arguments, rather whether each individual value was passed, in which case you could so something like:
class Option {
constructor(key, value, autoLoad) {
if(!key) { // Could change this to a strict undefined check
this.autoLoad = false;
return;
}
this[key] = value;
this.autoLoad = autoLoad || false;
}
}
What you want is called constructor overloading. This, and the more general case of function overloading, is not supported in ECMAScript.
ECMAScript does not handle missing arguments in the same way as more strict languages. The value of missing arguments is left as undefined instead of raising a error. In this paradigm, it is difficult/impossible to detect which overloaded function you are aiming for.
The idiomatic solution is to have one function and have it handle all the combinations of arguments that you need. For the original example, you can just test for the presence of key and value like this:
class Option {
constructor(key, value, autoLoad = false) {
if (typeof key !== 'undefined') {
this[key] = value;
}
this.autoLoad = autoLoad;
}
}
Another option would be to allow your constructor to take an object that is bound to your class properties:
class Option {
// Assign default values in the constructor object
constructor({key = 'foo', value, autoLoad = true} = {}) {
this.key = key;
// Or on the property with default (not recommended)
this.value = value || 'bar';
this.autoLoad = autoLoad;
console.log('Result:', this);
}
}
var option1 = new Option();
// Logs: {key: "foo", value: "bar", autoLoad: true}
var option2 = new Option({value: 'hello'});
// Logs: {key: "foo", value: "hello", autoLoad: true}
This is even more useful with Typescript as you can ensure type safety with the values passed in (i.e. key could only be a string, autoLoad a boolean etc).
Guessing from your sample code, all you need is to use default values for your parameters:
class Option {
constructor(key = 'foo', value = 'bar', autoLoad = false) {
this[key] = value;
this.autoLoad = autoLoad;
}
}
Having said that, another alternative to constructor overloading is to use static factories. Suppose you would like to be able to instantiate an object from plain parameters, from a hash containing those same parameters or even from a JSON string:
class Thing {
constructor(a, b) {
this.a = a;
this.b = b;
}
static fromHash(hash) {
return new this(hash.a, hash.b);
}
static fromJson(string) {
return this.fromHash(JSON.parse(string));
}
}
let thing = new Thing(1, 2);
// ...
thing = Thing.fromHash({a: 1, b: 2});
// ...
thing = Thing.fromJson('{"a": 1, "b": 2}');
Here's a hack for overloading based on arity (number of arguments). The idea is to create a function from a number of functions with different arities (determined by looking at fn.length).
function overloaded(...inputs) {
var fns = [];
inputs.forEach(f => fns[f.length] = f);
return function() {
return fns[arguments.length].apply(this, arguments);
};
}
var F = overloaded(
function(a) { console.log("function with one argument"); },
function(a, b) { console.log("function with two arguments"); }
);
F(1);
F(2, 3);
Of course this needs a lot of bullet-proofing and cleaning up, but you get the idea. However, I don't think you'll have much luck applying this to ES6 class constructors, because they are a horse of a different color.
you can use static methods,look at my answer to same question
class MyClass {
constructor(a,b,c,d){
this.a = a
this.b = b
this.c = c
this.d = d
}
static BAndCInstance(b,c){
return new MyClass(null,b,c)
}
}
//a Instance that has b and c params
MyClass.BAndCInstance(b,c)
Use object.assigne with arguments with this
This={...this,...arguments}
Its not the overload I wanted, but this is a basic version of how I faked my way through creating an obj1 with some different initialization behavior. I realize I could have expanded the arguments as stated above, but I already had a nasty set of arguments and relatively different data sources to deconstruct that would have really distorted my objectives; this just made it cleaner for my situation...
class obj1{
constructor(v1, v2){
this.a = v1;
this.b = v2;
}
}
class obj1Alt{
constructor(v1, v2){
return new obj1(v1*2,v2*2);
}
}
new obj1(2,4) // returns an obj1
new obj1Alt(2,4) // also returns an obj1
Disclaimer: I've been programming for a long time, but I am fairly new to JS; probably not a best practice.

How to properly derive object with private vars using javascript (prototypal) inheritance

I am new to JavaScript's (prototypal) inheritance and I'm trying to learn more about it.
I am using a simple observer pattern as example, in which I want observable objects to be derived from the 'subject' object. This is what I WANT to do:
function subject()
{
var callbacks = {}
this.register = function(name, callback)
{
callbacks[name] = callback;
}
this.unregister = function(name)
{
delete callbacks[name];
}
var trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
}
}
list.prototype = new subject()
function list()
{
var items = {}
this.add = function(name, item)
{
items[name] = item;
trigger('add', name);
}
this.remove = function(name)
{
delete items[name];
trigger('remove', name);
}
}
Now when using the code above like below, I run into my first problem:
var l = new list()
l.register('observer1', function() { console.log(this, arguments) });
l.add('item1', 'value1'); // <-- ReferenceError: trigger is not defined, trigger('add', name);
To continue testing I made the trigger function 'public' using this.trigger instead. Running my example again I run into the next problem:
var l = new list()
l.register('observer1', function() { console.log(this, arguments) });
l.add('item1', 'value1'); // <-- output: subject, ["add", "item1"]
The this object is subject, I want it to be list. My third problem occurs when creating another list:
var l2 = new list();
//Don;t register any observers
l2.add('item1', 'value1'); // <-- output: subject, ["add", "item1"]
The callbacks list is shared between the 2 lists.
I've tried similar things with Object.create(new subject()) as well and run into similar problems.
My 3 questions in this are:
Can I have private methods that can be used in derived objects (and
should I even care about having them private or public)?
How can I have the this object I want (without needing to use function.call in the derived object, if possible)?
How can I keep the callbacks list in the base object without it being shared?
An interesting question. As for #1 and #2: let's say you have a function foo:
function foo() {
var _private = 'private var!';
this.access = function () {
return _private;
}
}
access is a so-called privileged method, it's a closure that can access the private variable private.
you can inherit the whole thing by making use of call, like so:
function bar() {
foo.call(this);
}
var b = new bar();
console.log(b.output()); // prints 'private var!'
With the methods apply, call and bind you can establish the context of a function, effectively tamper with the this object. (your #2 question, read here )
Naturally you cannot make use of a totally private method in a derived object. You'd need an accessor method which would defeat the purpose of the original method being private. Having said that, that's the way it works in strongly typed languages too (in java if you mark a method as private not even subclases will be able to access it, it would have to be protected).
As for #3, I cannot think of how to keep callbacks shared and private.
But you can make it a static property for all instances of a function (much like a static property in a lanaguage like java) by simply declaring a function like:
function foo() {
}
add your prototypes which will be assigned to each instance
foo.prototype.bar = // ...
and a static property
foo.callbacks = [];
All instances of foo will share the callbacks property.
You can’t have private methods, and that’s that. It will never work both properly and nicely at the same time, so don’t bother trying to emulate them in JavaScript.
Then all you have to do is call the parent’s constructor in the derived constructor.
function subject()
{
var callbacks = {};
this.register = function(name, callback)
{
callbacks[name] = callback;
};
this.unregister = function(name)
{
delete callbacks[name];
};
this.trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
};
}
list.prototype = Object.create(subject);
list.prototype.constructor = list;
function list()
{
subject.call(this);
var items = {};
this.add = function(name, item)
{
items[name] = item;
this.trigger('add', name);
};
this.remove = function(name)
{
delete items[name];
this.trigger('remove', name);
};
}
Incorporating Joe's suggestion, this is what I eventually ended up with:
function subject()
{
var callbacks = {}
this.register = function(name, callback)
{
callbacks[name] = callback;
}
this.unregister = function(name)
{
delete callbacks[name];
}
trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
}
}
//without the following line, 'this' in firefox is 'subject' instead of 'list' (in chrome it is)
list.prototype = new subject()
//without these, 'list' is not an instanceof 'subject'
list.constructor = subject;
list.prototype.constructor = list;
function list(n)
{
this.name = n;
subject.call(this); //as suggested by Joe
var items = {}
this.add = function(name, item)
{
items[name] = item;
trigger.call(this, 'add', name); //no way to do this without using call/apply
}
this.remove = function(name)
{
delete items[name];
trigger.call(this, 'remove', name); //no way to do this without using call/apply
}
this.getitems = function() { return items }
}
//without the following line, 'this' in firefox is 'subject' instead of 'queue'
queue.prototype = new subject()
//without these, 'queue' is not an instanceof 'subject'
queue.constructor = subject;
queue.prototype.constructor = queue;
function queue(n)
{
this.name = n;
subject.call(this); //as suggested by Joe
var items = [];
this.enqueue = function(item)
{
items.push(item);
trigger.call(this, 'enqueue', item); //no way to do this without using call/apply
}
this.dequeue = function()
{
var d = items.shift();
trigger.call(this, 'dequeue', d); //no way to do this without using call/apply
return d;
}
this.getitems = function() { return items }
}
var l1 = new list('l1')
l1.register('observer1', function() { console.log('l1', this, arguments) });
l1.add('item1', 'value1');
// ^ 'l1', list { name = 'l1' ... }, ['add', 'item1']
var l2 = new list('l2')
l2.register('observer2', function() { console.log('l2', this, arguments) });
l2.add('item2', 'value2');
// ^ 'l2', list { name = 'l2' ... }, ['add', 'item2']
var q1 = new queue('q1')
q1.register('observer3', function() { console.log('q1', this, arguments) });
q1.enqueue('item3');
// ^ 'q1', queue { name = 'q1' ... }, ['enqueue', 'item3']
console.log(l1 instanceof list, l1 instanceof subject, l1 instanceof queue);
// ^ true, true, false
console.log(q1 instanceof list, q1 instanceof subject, q1 instanceof queue);
// ^ false, true, true
This ticks all of my boxes (except for the use of call, but I can live with that).
Thanks for all the help,
Mattie
EDIT: appearantly this does not work as expected. creating a new object overwrites the other objects callbacks

Private members in Javascript. Can they be non-static?

I have this code here:
var Person = (function() {
var name;
var PersonConstructor = function(n) {
name = n;
};
PersonConstructor.prototype.getName = function() {
return name;
};
return PersonConstructor;
})();
var people = [];
var person1 = new Person("Foo");
var person2 = new Person("Bar");
alert(person1.getName()); // I want it to be Foo
people.push(person1);
people.push(person2);
I got the idea of emulating classes from here.. But of course, I neglected the fact that the private variable var name; is also a static variable. Since this is tripping my current efforts I would like to know if there is a way to keep the private behaviour in this example but avoid the static one?
Use this.
var Person = (function() {
var PersonConstructor = function(n) {
this.name = n;
};
PersonConstructor.prototype.getName = function() {
return this.name;
};
return PersonConstructor;
})();
Unfortunately, this won't preserve the private state.
It's just a scope issue.
var Person = (function(){
var PersonConstructor = function(n){
// ***************************************************************
// PRIVATE VARIABLES AND FUNCTIONS
// ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE
// ***************************************************************
var myName=n?n:"John Doe";
// ***************************************************************
// PRIVILEGED METHODS
// MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS
// MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS
// ***************************************************************
this.toString=this.getName=function(){ return myName }
}
return PersonConstructor;
})();
var person1 = new Person("foo");
var person2 = new Person("bar");
alert(person1.getName());
alert(person1.toString());
alert(person1.myName);
// alerts "foo", "foo", undefined
EDIT - Here is my original solution.
var Person = function(n){
// ***************************************************************
// PRIVATE VARIABLES AND FUNCTIONS
// ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE
// ***************************************************************
var myName=n?n:"John Doe";
// ***************************************************************
// PRIVILEGED METHODS
// MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS
// MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS
// ***************************************************************
this.toString=this.getName=function(){ return myName }
}
var person1 = new Person("foo");
var person2 = new Person("bar");
alert(person1.getName());
alert(person1.toString());
alert(person1.myName);
// alerts "foo", "foo", undefined
There is no "private" when working with prototypes.
It should be noted there is no value in private state, avoid it like the plague. Closures are ugly and expensive.
var o = {
name: value
}
However if you insist on being delusional and want private state badly then
You can store state in a closure
function nameHolder(name) {
return {
get name() {
return name
},
set name(n) {
name = n
}
}
}
Note this is highly inefficient and has little benefit.
Alternatively you can store state in a weakmap
function privates() {
var map = new WeakMap()
return function (key) {
var v = map.get(key)
if (!v) {
v = {}
map.set(key, v)
}
return v
}
}
var NameHolder = (function () {
var state = privates()
return {
constructor: function (name) {
state(this).name = name
},
getName: function () {
return state(this).name
}
}
}())
WeakMap browser support is non-existant so emulate it using pd.Name

Categories