I have a simple class, and I'm trying to figure out how to set a default value in the constructor:
var Foo = function(bar = Foo.someEnum.VAL1) {
this.bar = bar;
someEnum = {VAL1 : 1, VAL2: 2};
}
and to use it like:
var myFoo = new Foo(Foo.someEnum.VAL1);
but this is apparently wrong. What's the correct way to set a default enum value, or do I need to set the default to null, and check for the null in the constructor and set accordingly?
To clarify, bar is an enum for the Foo class. There are other properties in the class that are not shown. Also, updated class code.
You can try this if you want to make bar an optional parameter:
function Foo(bar) {
this.bar = bar || Foo.enum.VAL1; //If bar is null, set to enum value.
}
//Define static enum on Foo.
Foo.enum = { VAL1: 1, VAL2: 2, VAL3: 3 };
console.log(new Foo().bar); //1
console.log(new Foo(Foo.enum.VAL3).bar); //3
Do you just want bar to be defined inside the function?
var Foo = function() {
var bar = {VAL1 : 1, VAL2: 2};
}
or for a blank starting object:
var Foo = function() {
var bar = {};
}
Or are you wanting it to be set from a parameter that's passed into the function?
var Foo = function(barIn) {
var bar = barIn;
}
Another option - create the object (enum) from values passed in:
var Foo = function(val1, val2) {
var bar = {VAL1 : val1, VAL2: val2};
}
The possibilities go on, but it's not entirely clear what you're trying to achieve.
I'm not entirely sure what you are trying to do but maybe it is this...
var Foo = function (bar = 1) {
this.bar = bar;
}
Foo.VAL1 = 1;
Foo.VAL2 = 2;
Now you can do:
foo1 = new Foo();
alert(foo1.bar); //alerts 1;
foo2 = new Foo(Foo.VAL2);
alert(foo1.bar); //alerts 2;
Related
If a value of the constant is a pure constant, than it is very easy to define it:
class Foo {
static get bar() { return 123; }
}
However, what if I need to define a constant that needs to be calculated in the constructor?
class Foo {
constructor(m) {
// ...
}
}
let test = new Foo(10);
console.log(test.bar); // OK
test.bar = 1; // ERROR
How can I define bar within the constructor and make this bar accessible as a constant?
The value of this constant is needed many times in other methods. The calculation of this constant value is not trivial, so it would be a waste of resources to calculate it each time when it is needed. On the other hand, I want to ensure that it can't be changed once it is calculated.
UPDATE
Per the discussion in the comments, this can be achieved even more cleanly by simply setting a value property in the config passed to Object.defineProperty() in lieu of a get property; because the writable and configurable properties default to false, this is sufficient in providing the desired behavior outlined in the question:
class Foo {
constructor(m) {
this.m = m;
const bar = m * 100;
Object.defineProperty(this, 'bar', { value: bar });
}
}
let test = new Foo(10);
console.log(test.bar);
test.bar = 300;
console.log(test.bar);
delete test.bar;
console.log(test.bar);
ORIGINAL ANSWER
One option would be to use Object.defineProperty() in the constructor to set a getter for bar, and a setter that prevented its being changed:
class Foo {
constructor(m) {
this.m = m;
const bar = m * 100;
Object.defineProperty(this, 'bar', {
get: () => bar,
set: () => console.warn('bar cannot be set'), // The setter is optional
});
}
}
let test = new Foo(10);
console.log(test.bar);
test.bar = 300;
console.log(test.bar);
How about this
class Foo {
constructor (m) {
this._bar = m + 10; // this is _bar , not bar
}
get bar() {
return this._bar; //_bar is the private methods, which is different from bar
}
}
const foo = new Foo(1);
console.log(foo.bar) // return 11
foo.bar = 0
console.log(foo.bar) // still return 11
You can check this.
class Foo {
constructor(value) {
this._privateValue = value;
}
get bar() {
return this._privateValue;
}
set bar(val) {
console.error("Sorry can't edit me ;) ")
}
}
const test = new Foo(10)
console.log(test.bar)
test.bar = 20;
console.log(test.bar)
Despite having read all the usual tutorials on the Javascript module pattern, there's still something about scoping which I clearly don't understand.
pushing to a module-wide list does what I'd expect it to do. Setting a module-wide list with = doesn't. What am I doing wrong here?
var MODULE = MODULE || {};
MODULE.Submodule = (function(){
var foo = [],
bar = [];
var init = function() {
foo = ['a','b','c']; // This doesn't set MODULE.Submodule.foo
bar.push('a'); // But this works as expected
console.log('foo: ' + MODULE.Submodule.foo); // foo:
console.log('bar: ' + MODULE.Submodule.bar); // bar: a
}
return {
init: init,
foo: foo,
bar: bar
}
}());
MODULE.Submodule.init();
This JSFiddle http://jsfiddle.net/SwBLk/1/ may help explain what's happening:
var MODULE = MODULE || {};
MODULE.Submodule = (function(){
var foo = [],
bar = [];
var init = function() {
console.log(MODULE.Submodule.foo === foo);
foo = ['a','b','c']; // This doesn't set MODULE.Submodule.foo
console.log(MODULE.Submodule.foo === foo);
bar.push('a'); // But this works as expected
console.log('foo: ' + MODULE.Submodule.foo); // foo:
console.log('bar: ' + MODULE.Submodule.bar); // bar: a
}
return {
init: init,
foo: foo,
bar: bar
}
}());
MODULE.Submodule.init();
The first boolean check return TRUE because both objects reference the same object.
The second boolean check returns FALSE because you have replaced foo with a new object and the references don't point to the same underlying object anymore.
You are replacing the foo array with a reference to a new array when you re-assign a new array to it.
When you execute the IIFE, you assign a reference to a very specific version of "foo" on the return statement. That is the reference you then access when you call MODULE.Submodule.foo. When you go in and replace foo = [ "a", ... ], you are replacing the object foo, but not replacing the reference to it in the MODULE.Submodule object.
EDIT: How do you get around this issue?
1) Ideally, you don't replace the entire array, but only clear & re-initialize it on init() call:
MODULE.Submodule = (function(){
var foo = [],
bar = [];
var init = function() {
// Clear our the array if it already contains data (in case your calling "init" more than once)
// This method is documented to be the fastest way to clear out an array, referenced here:
// http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript
while(foo.length > 0) {
foo.pop();
}
foo.push('a');
foo.push('b');
foo.push('c');
bar.push('a'); // But this works as expected
console.log('foo: ' + MODULE.Submodule.foo); // foo:
console.log('bar: ' + MODULE.Submodule.bar); // bar: a
}
2) Your second option is to use what I'd call "dynamic getters" (working fiddle: http://jsfiddle.net/6zVcP/):
var MODULE = MODULE || {};
MODULE.Submodule = (function(){
var foo = [],
bar = [];
var init = function() {
foo = ['a','b','c']; // This doesn't set MODULE.Submodule.foo
bar.push('a'); // But this works as expected
console.log('foo: ' + MODULE.Submodule.foo()); // foo:
console.log('bar: ' + MODULE.Submodule.bar()); // bar: a
}
var modify = function()
{
foo = [];
foo.push("test");
}
return {
init: init,
modifyArrays: modify,
foo: function() { return foo; },
bar: function() { return bar; }
}
}());
MODULE.Submodule.init();
MODULE.Submodule.modifyArrays();
console.log(MODULE.Submodule.foo());
This allows you to do whatever you want to the foo object while the getter will always return the latest reference.
I am trying to do the following to satisfy the requirements of a code builder (Sencha Cmd to be specific).
This is the essence I what I need to do. The critical factor is that the function body MUST end with a return of an object literal. I cant return a variable due to restrictions in the builder. So, how to add a property 'b' at the point of the pseudo code below if the parameter 'includeB' is true, but NOT add a property AT ALL if it is false. ie b==undefined or b==null is not allowed.
Perhaps it is not possible.
function create(includeB) {
// Can have code here but the final thing MUST be a return of the literal.
// ...
return {
a : 1
// pseudo code:
// if (includeB==true) then create a property called b
// and assign a value of 2 to it.
// Must be done right here within this object literal
}
}
var obj = create(false);
// obj must have property 'a' ONLY
var obj = create(true);
// obj must have properties 'a' and 'b'
Thanks for reading and considering,
Murray
If you can use ES6, use the spread properties.
function create(includeB) {
return {
a : 1,
...(includeB ? { b: 2 } : {}),
};
}
You've pretty much shown a use case for a constructor function instead of using an object literal:
function CustomObject(includeB) {
this.a = 1;
if (includeB) {
this.b = 2;
}
}
//has `a` only
var obj1 = new CustomObject(false);
//has `a` and `b`
var obj2 = new CustomObject(true);
After re-reading your question it appears that you've got limited access in modifying the function. If I'm understanding your question correctly you can only change a limited portion of the script:
function create(includeB) {
// modifications may be done here
// the rest may not change
return {
a : 1
}
}
var obj = create(false);
// obj must have property 'a' ONLY
var obj = create(true);
// obj must have properties 'a' and 'b'
If that's the case, then you could simply skip the later part of the function:
function create(includeB) {
if (includeB) {
return {
a: 1,
b: 2
};
}
return {
a: 1
};
}
You cannot put boolean logic inside a javascript literal definition. So, if your builder requires the the returned object can ONLY be defined as a javascript literal, then you cannot define properties conditionally that way.
If you can create an object inside your function, modify that object using logic and then return that object, then that's pretty easy.
function create(includeB) {
var x = {
a: 1
};
if (includeB) {
x.b = 2;
}
return x;
}
Your other option would be to wrap the create function and do it outside the create function.
function myCreate(includeB) {
var x = create(includeB)
if (includeB) {
x.b = 2;
}
return x;
}
Or, you could even wrap the create function transparently so callers still use create(), but it's behavior has been altered.
var oldCreate = create;
create = function(includeB) {
var x = oldCreate(includeB);
if (includeB) {
x.b = 2;
}
return x;
}
I recently had to do this, and found you could use a self-calling function within an object's definition (if using ES6). This is similar to the accepted answer, but might be useful for others who need to do this without first defining a constructor function.
For example:
let obj = (() => {
let props = { a: 1 };
if ( 1 ) props.b = 2;
return props;
})();
makes the object: { a: 1, b: 2 }
It's handy for more complicated objects, keeping the construction continuous:
let obj = {
a: 1,
b: (() => {
let props = { b1: 1 };
if ( 1 ) props.b2 = 2;
return props;
})(),
c: 3
}
makes the object:
{
a: 1,
b: {
b1: 1,
b2: 2
},
c: 3
}
You could define it later:
var hasA = create(); // has hasA.a
var hasBoth = create();
hasBoth.b = 2; //now has both
Alternatively, using your argument in create:
function create (includeB) {
var obj = {
a : 1
};
if (includeB) {
obj.b = 2;
}
return obj;
}
Below should work. I hope this help.
function create(includeB){
var object = {
a: 1
};
if (includeB)
object.b = 2;
return object;
}
How about this:
function create(includeB) {
return includeB && { a:1, b:2 } || { a:1 };
}
When includeB is true, the create function will return {a:1, b:2}. If includeB is false, it will return whatever is after the or - in this case, the {a:1} object.
create(true) returns { a:1, b:2 }.
create(false) returns { a:1 }
If you would like to use a declaration to satisfy the same requirement once without too much bloat, you can also simply do the following:
var created = function(includeB) {
var returnObj = { a : 1 };
if(includeB) { returnObj.b = 2; }
return returnObj;
}}(); //automatically runs and assigns returnObj to created
When trying to create a new object, one that is empty for manipulation, I can't get the old data out.
Here is an example of what I've tried:
function Foo() {
this.Bar = Bar;
// etc..
}
var Bar = {
__words : {},
addWord : function (word, amount) {
this.__words[word] = amount;
}
// etc..
}
Now, when I create a new object:
var foo = new Foo();
var bar = foo.Bar;
bar.addWord("hello",7);
bar.addWord("world",9);
var lorem = new Foo();
var words = lorem.Bar.__words; // This will display {hello:7,world:9} from the
// previous object
I also tried using Object.create() but it was still the same, showing the __words from the previous object.
The object referred to by Bar is shared between each Foo instance.
I don't really see the point of putting the logic into two objects, you can do this with just one constructor function:
function Foo() {
this.__words = {};
}
Foo.prototype.addWord = function(word, amount) {
this.__words[word] = amount;
}
var foo = new Foo();
foo.addWord("hello",7);
var bar = new Foo();
bar.addWord("world",9);
If you really have to separate the functionality, then make Bar a constructor function as well and create a new instance inside the Foo constructor method:
function Foo() {
this.bar = new Bar();
}
function Bar() {
this.__words = {};
}
Bar.prototype.addWord = function(word, amount) {
this.__words[word] = amount;
}
There are couple of problems in the code.
function Foo() {
this.Bar = Bar;
// etc..
}
var Bar = {
__words : {},
addWord : function (word, amount) {
this.__words[word] = amount;
}
// etc..
}
Bar should be defined before Foo but it might work without problems because variables are actually defined at function scope level which might not be a problem in this particular example.
That said, you're copying an object inside the Foo constructor. Objects are mutables so it won't work. One change to the object will change the other. What you might be able to do is this. Here's an other way to do that without new.
var Bar = {
create: function () {
var obj = Object.create(this);
obj.__words= {};
return obj;
},
addWord: function (word, amount) {
this.__words[word] = amount;
}
...other methods...
};
var Foo = function () {
this.Bar = Bar.create();
// etc..
};
But I have no idea how is supported Object.create in other browsers. This is syntax is the equivalent to what Felix wrote, you could clearly remove the need of two object.
The only big difference is that instead of writing new Bar(). You are actually creating a prototype object and creating an object from the prototype. The create is the constructor.
Foo could be written in the same way and you'd get something like this.
var Bar = {
create: function () {
var obj = Object.create(this);
obj.__words= {};
return obj;
},
addWord: function (word, amount) {
this.__words[word] = amount;
},
...other methods...
};
var Foo = {
create: function () {
var foo = Object.create(this);
foo.Bar = Bar.create();
return foo;
},
....
};
var foo1 = Foo.create();
var foo2 = Foo.create();
foo1.bar.addWord("allo", 3);
I guess it all depends on what you're trying to do but the create method as some advantages over the "new" operators. You could for example create async methods that after object creations execute a callback.
Over here for more informations. In the end it's pretty much the same thing.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
I can't get the old data out.
Well you are not clearing var Bar which is global actually - meaning you are creating a new Object with var lorem = new Foo(); but giving him the same Bar Object with Foos constructor.
this.Bar = Bar;
if you want a new Object you should give Foo its own Bar Object.
Or include:
__words : {},
addWord : function (word, amount) {
this.__words[word] = amount;
in Foo itself.
Anyways if you just need this once, you could simply empty the words array in Bar.
So let's say I want to pass in an object containing settings to my class in JavaScript, but also provide default options, how would I do that easily? For example:
myClass = function(options){
var defaults = {
foo: 'foo',
bar: 'bar'
};
};
customOptions = {
bar: 'foobar'
};
myInstance = new myClass(customOptions);
So in this case, I would like for myInstance() to use foo='foo' since it was not specified by the user, and bar='foobar' as that was set by the user.
Now, I'll be dealing with a bigger and more complex JSON object, obviously, and it seems inefficient and hard to maintain to check every property every time, so is there some way to combine these objects easily, overwriting as needed, with the user supplied properties always taking precedence?
You could do something like this:
var MyClass = function (options) {
var defaults = {foo: 1, bar: 2};
for (var option in defaults) {
this[option] = options[option] || defaults[option];
}
}
var customOptions = { bar: 5};
var c = new MyClass(customOptions);
Basically loop through the custom options passed in and add any missing options with their defaults. In this case c.foo = 1 and c.bar = 5. Fiddle available at http://jsfiddle.net/2Ve3M/.
You can check if the custom options object contains the properties you are looking for, and if not set default values.
myClass = function(options) {
this.foo = options.foo || 'foo';
this.bar = options.bar || 'bar';
};
customOptions = {
bar: 'foobar'
};
myInstance = new myClass(customOptions);
You could try something like this:
myClass = function(options){
var myOptions = {
foo: options.foo || 'foo',
bar: options.bar || 'bar'
};
};
If you are using jQuery you could also look at http://api.jquery.com/jQuery.extend/