creating default parameters when making a object using object.create - javascript

Is there a way to optionally declare dots: {} from inside Object.create?
As you can see I have tried
standard default parameter assignment
tried using a nullish coalescing
Neither work.
Any guidance would be appreciated.
'use strict'
let Carousel = function(name, dots=true) {
this.name = name
this.dots = dots ?? true
}
Carousel.prototype.show = function() {
console.log(`${this.name} and ${this.dots}`)
}
window.addEventListener('DOMContentLoaded', () => {
let usps = Object.create(Carousel.prototype, {
name: { value: 'element name', writeable: false}
// dots: { value: true, writeable: false }
})
usps.show()
})

Not sure why you don't use new keyword and instead create object from prototype, but in either case, if you set default value to prototype of your object, you will get dots on it.
let Carousel = function(name, dots) {
this.name = name
this.dots = dots
}
Carousel.prototype.show = function() {
console.log(`${this.name} and ${this.dots}`)
}
Carousel.prototype.dots = true;
window.addEventListener('DOMContentLoaded', () => {
let usps = Object.create(Carousel.prototype, {
name: { value: 'element name', writeable: false},
})
usps.show() // will show that dots equals true
let usps2 = Object.create(Carousel.prototype, {
name: { value: 'element name', writeable: false},
dots: { value: false, writeable: false }
});
usps2.show(); // will show that dots equals false
})
It is important, that if you will want to use new operator, you will have to modify your constructor, so if there is no dots parameter set, you will not override one with undefined:
let Carousel = function (name, dots) {
this.name = name;
if (dots !== undefined) {
this.dots = dots;
}
};
Carousel.prototype.show = function () {
console.log(`${this.name} and ${this.dots}`);
};
Carousel.prototype.dots = true;
window.addEventListener("DOMContentLoaded", () => {
let usps = new Carousel("element name");
usps.show();
let usps2 = new Carousel("element name 2", false);
usps2.show();
});

Related

JavaScript override specific jso properties

I have a JS object that contains base options. If that object is initialized with a specific new option, I only want to change the one that was specified.
//Object :
var controller = function (){
var _id = id;
var _options = {
placeholder: "Search..."
value: "Some text",
width: 25,
...
};
// ==================================
// Public Functions
// ==================================
return {
init: function (id, options = null) {
...
}
}
}();
Example here i only change the placeholder property.
Call: controller.init('demo', options = { placeholder: 'Other value...' });
Expected change:
_options = {
placeholder: "Other value..."
value: "Some text",
width: 25,
...
};
You can use ... to merge the provided options into the default options.
var controller = function() {
var _id = id;
var _options = {
placeholder: "Search..."
value: "Some text",
width: 25,
// ...
};
// ==================================
// Public Functions
// ==================================
return {
init: function(id, options = {}) {
this.options = {..._options, ...options};
// ...
}
}
}();
You can use the below code.
_options = Object.assign(_options, options);
Here, _options will have default properties and values. options is input property with limited configuration. So, From this code, _options get only overuse with options properties.
Hope it's useful.
Object assign works too.
const thingy = (() => {
let myWords = {
favorite: "defenestrate",
leastFavorite: "whilst",
newest: "copacetic",
oldest: "dada",
};
return (words) => {
myWords = Object.assign(myWords, words);
console.log(JSON.stringify(myWords));
}
})();
// For the demo.
thingy({newest: "pyroclastic", fakest: "supercalifragilisticexpialidocious"});

Assigning functions to Objects

I want to add functions to JSON Objects, but I can't find function for assigning to all objects, only to one.
This code works with Arrays:
Object.defineProperty(Array.prototype, 'random', {
value: () => {
return this[Math.floor(Math.random() * this.length)];
},
});
I've also found this code:
const obj = {name: 'Bob'};
obj.fullName = function() { return this.name }
But that one only works for specific object, not all of them.
Is it event possible to write global functions for all JSON Objects, and if is, then how to do it?
You could add the function to Object.prototype. Note that this is not considered a very good practice because it could impact the rest of the code (like shown in the comments):
Object.prototype.fullName = function() { return this.name; };
const obj = { name: 'Bob' };
console.log(obj.fullName());
You should consider doing this instead:
const baseObject = { fullName: function() { return this.name; } };
const obj = Object.create(baseObject, { name: { value: 'Bob', writable: true } });
console.log(obj.fullName());
And if your target runtime (browser?) supports ECMAScript 6, you could also create a dedicated class for this:
class MyClass {
constructor(name) {
this.name = name;
}
fullName() { return this.name; }
}
const bob = new MyClass('Bob');
console.log(bob.fullName());
Finally, the class syntax for ECMAScript 5:
function MyClass(name) {
this.name = name;
}
MyClass.prototype.fullName = function() { return this.name; }
const bob = new MyClass('Bob');
console.log(bob.fullName());

Javascript created object property values undefined after calling another function with created object

I know this may be a silly question but I'm new to javascript so would appreciate some guidance.
I have the following notificationSocketEventHandler object created:
const notificationSocketEventHandler = Object.create(socketHandlerProto, {
validators: {
created: [],
destroyed: [],
loadedFromSocket: [],
updated: [],
addedto: {
relation: []
},
removedfrom: {
}
},
created: function (data) {
if (this.validateProfileData(data, validators.created)) {} else {}
},
destroyed: function (data) {},
updated: function (data) {},
loadedFromSocket: function (data) {
console.log('Loaded from socket')
console.log(data)
}
})
This event handler is being used to listen for notifications from a socket
so and is set like this
$.globals.socket.on('notifications',notificationSocketEventHandler);
the event prototype is defined as such:
$.globals.socket = {
events: {},
on: function (attr, func) {
if (!attr) return false
this.events[attr] = this.events[attr] || [];
this.events[attr].push(func)
return true
},
remove(attr, func) {
if (!events[attr]) return false
this.events[attr].forEach(function (f, indx) {
if (f === func) {
events[attr].slice(indx, 1)
}
})
},
trigger: function (attr, thisArg, paramArgs) {
if (Array.isArray(attr) && attr.length) {
var obj = this.events[attr[0]]
for (var i = 1; i < attr.length; i++) {
if (!obj) return
obj = obj[attr[i]]
}
if (typeof obj == 'function') obj.apply(thisArg, paramArgs)
if (Array.isArray(obj)) {
obj.forEach(function (c) {
if (typeof c == 'function') c.apply(thisArg, paramArgs)
})
}
return
}
if(this.events[attr]){
console.log(this.events[attr])
this.events[attr].forEach(function (f) {
if (typeof f === 'function')
f.apply(thisArg, paramArgs)
})
}
}
}
The problem I am having is that after the notificationSocketEventHandler object gets passed to the $.globals.socket.on function and ultimately pushed into the events object, the properties of the notificationSocketEventHandler such as created, destroyed,loadedFromSocket which are defined as functions before being passed to the $.globals.socket.on function, suddenly become `undefined' once within the events object, why is this?
Object.create is a little confusing - you can't use a normal object as the second parameter, it has to be an object of 'property descriptors'. To work properly your code would need to be formatted along these lines:
const notificationSocketEventHandler = Object.create(socketHandlerProto, {
'validators': {
value: {
'created': {
value: []
},
'destroyed': {
value: []
},
'loadedFromSocket': {
value: []
}
}
}
});
Unless you iterate over those properties it's going to be tedious. You would be much better off avoiding Object.create and just making an object normally:
const notificationSocketEventHandler = {
created: [],
destroyed: [],
etc...
}
or adding the properties to the constructor's prototype (as an aside, people usually make constructor names begin with an uppercase letter so it's immediately obvious they're constructors - saves you having to add 'Proto' at the end of the name for one thing. Anyway):
var SocketHandler = function {
this.created = [];
this.destroyed = []
etc...
}
const notificationSocketEventHandler = new SocketHandler
or
var SocketHandler = {};
SocketHandler.prototype.created = [];
SocketHandler.prototype.destroyed = [];
etc...

javascript: Alter the output of a function to the specified decimal place

I am not very good with my javascript but recently needed to work with a library to output an aggregated table. Was using fin-hypergrid.
There was a part where I need to insert a sum function (rollups.sum(11) in this example)to an object so that it can compute an aggregated value in a table like so:
aggregates = {Value: rollups.sum(11)}
I would like to change this value to return 2 decimal places and tried:
rollups.sum(11).toFixed(2)
However, it gives the error : "rollups.sum(...).toFixed is not a function"
If I try something like:
parseFloat(rollups.sum(11)).toFixed(2)
it throws the error: "can't assign to properties of (new String("NaN")): not an object"
so it has to be a function object.
May I know if there is a way to alter the function rollups.sum(11) to return a function object with 2 decimal places?
(side info: rollups.sum(11) comes from a module which gives:
sum: function(columnIndex) {
return sum.bind(this, columnIndex);
}
)
Sorry I could not post sample output here due to data confidentiality issues.
However, here is the code from the example I follow. I basically need to change rollups.whatever to give decimal places. The "11" in sum(11) here refers to a "column index".
window.onload = function() {
var Hypergrid = fin.Hypergrid;
var drillDown = Hypergrid.drillDown;
var TreeView = Hypergrid.TreeView;
var GroupView = Hypergrid.GroupView;
var AggView = Hypergrid.AggregationsView;
// List of properties to show as checkboxes in this demo's "dashboard"
var toggleProps = [{
label: 'Grouping',
ctrls: [
{ name: 'treeview', checked: false, setter: toggleTreeview },
{ name: 'aggregates', checked: false, setter: toggleAggregates },
{ name: 'grouping', checked: false, setter: toggleGrouping}
]
}
];
function derivedPeopleSchema(columns) {
// create a hierarchical schema organized by alias
var factory = new Hypergrid.ColumnSchemaFactory(columns);
factory.organize(/^(one|two|three|four|five|six|seven|eight)/i, { key: 'alias' });
var columnSchema = factory.lookup('last_name');
if (columnSchema) {
columnSchema.defaultOp = 'IN';
}
//factory.lookup('birthState').opMenu = ['>', '<'];
return factory.schema;
}
var customSchema = [
{ name: 'last_name', type: 'number', opMenu: ['=', '<', '>'], opMustBeInMenu: true },
{ name: 'total_number_of_pets_owned', type: 'number' },
{ name: 'height', type: 'number' },
'birthDate',
'birthState',
'employed',
{ name: 'income', type: 'number' },
{ name: 'travel', type: 'number' }
];
var peopleSchema = customSchema; // or try setting to derivedPeopleSchema
var gridOptions = {
data: people1,
schema: peopleSchema,
margin: { bottom: '17px' }
},
grid = window.g = new Hypergrid('div#json-example', gridOptions),
behavior = window.b = grid.behavior,
dataModel = window.m = behavior.dataModel,
idx = behavior.columnEnum;
console.log('Fields:'); console.dir(behavior.dataModel.getFields());
console.log('Headers:'); console.dir(behavior.dataModel.getHeaders());
console.log('Indexes:'); console.dir(idx);
var treeView, dataset;
function setData(data, options) {
options = options || {};
if (data === people1 || data === people2) {
options.schema = peopleSchema;
}
dataset = data;
behavior.setData(data, options);
idx = behavior.columnEnum;
}
// Preset a default dialog options object. Used by call to toggleDialog('ColumnPicker') from features/ColumnPicker.js and by toggleDialog() defined herein.
grid.setDialogOptions({
//container: document.getElementById('dialog-container'),
settings: false
});
// add a column filter subexpression containing a single condition purely for demo purposes
if (false) { // eslint-disable-line no-constant-condition
grid.getGlobalFilter().columnFilters.add({
children: [{
column: 'total_number_of_pets_owned',
operator: '=',
operand: '3'
}],
type: 'columnFilter'
});
}
window.vent = false;
//functions for showing the grouping/rollup capabilities
var rollups = window.fin.Hypergrid.analytics.util.aggregations,
aggregates = {
totalPets: rollups.sum(2),
averagePets: rollups.avg(2),
maxPets: rollups.max(2),
minPets: rollups.min(2),
firstPet: rollups.first(2),
lastPet: rollups.last(2),
stdDevPets: rollups.stddev(2)
},
groups = [idx.BIRTH_STATE, idx.LAST_NAME, idx.FIRST_NAME];
var aggView, aggViewOn = false, doAggregates = false;
function toggleAggregates() {
if (!aggView){
aggView = new AggView(grid, {});
aggView.setPipeline({ includeSorter: true, includeFilter: true });
}
if (this.checked) {
grid.setAggregateGroups(aggregates, groups);
aggViewOn = true;
} else {
grid.setAggregateGroups([], []);
aggViewOn = false;
}
}
function toggleTreeview() {
if (this.checked) {
treeView = new TreeView(grid, { treeColumn: 'State' });
treeView.setPipeline({ includeSorter: true, includeFilter: true });
treeView.setRelation(true, true);
} else {
treeView.setRelation(false);
treeView = undefined;
delete dataModel.pipeline; // restore original (shared) pipeline
behavior.setData(); // reset with original pipeline
}
}
var groupView, groupViewOn = false;
function toggleGrouping(){
if (!groupView){
groupView = new GroupView(grid, {});
groupView.setPipeline({ includeSorter: true, includeFilter: true });
}
if (this.checked){
grid.setGroups(groups);
groupViewOn = true;
} else {
grid.setGroups([]);
groupViewOn = false;
}
}
you may try:
(rollups.sum(11)).toFixed(2)
enclosing number in parentheses seems to make browser bypass the limit that identifier cannot start immediately after numeric literal
edited #2:
//all formatting and rendering per cell can be overridden in here
dataModel.getCell = function(config, rendererName) {
if(aggViewOn)
{
if(config.columnName == "total_pets")
{
if(typeof(config.value) == 'number')
{
config.value = config.value.toFixed(2);
}
else if(config.value && config.value.length == 3 && typeof(config.value[1]) == 'number')
{
config.value = config.value[1].toFixed(2);
}
}
}
return grid.cellRenderers.get(rendererName);
};

JavaScript object that saves/loads its state

I'd like to create a Javascript object that can save and load its state (to local storage).
This is the basic pattern I'm using:
var obj = function () {
// private members
//
return {
// public members
load: function () {
this.state = JSON.parse(localStorage.getItem('obj'));
if (this.state === null) {
this.state = {
name: 'foo'
};
}
},
save: function () {
localStorage.setItem('obj', JSON.stringify(this.state));
}
};
}();
// load state
obj.load();
console.log(obj.state.name);
// save state
obj.state.name = 'bar';
obj.save();
But there's one thing that annoys me about this pattern: I have to access the object's persistent properties through the 'state' property.
How can I rewrite this so I can use the object in a more natural way, like:
// load state
obj.load();
console.log(obj.name);
// save state
obj.name = 'bar';
obj.save();
This is a very simple 'state', but the solution has to work for a complex state object with nested objects, arrays etc., so simply adding a 'name' property to my object is not what I'm after.
If you don't care which properties get loaded/saved then you can simply copy all from state into self. For example, after reading into var state (instead of this.state since you don't want state to be a part of this anymore): for(x in state) this[x] = state[x];
similarly, you'd save out: var state = {}; for(x in this) state[x] = this[x]
However, if you want to have a pre-defined list, then I'd recommend: var fields = ['name', 'zip', 'age'];
And then use for(x in fields) this[x] = state[x] to load and for(x in fields) state[x] = this[x]; to save.
Sorry it's a bit pieced together, but I hope you can follow what I mean :)
EDIT: Added full example per OPs request.
An example of a full solution using this technique is as follows:
var obj = function () {
// private members
//
return {
// public members
load: function () {
var state = JSON.parse(localStorage.getItem('obj'));
if(state == null) state = { name: 'foo' };
for(x in state) this[x] = state[x];
},
save: function ()
{
var state = {};
// check if it's a function. This version taken from underscorejs
var isFunction = function(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
};
for(x in this)
{
if(isFunction(this[x])) continue; // skip functions
state[x] = this[x];
}
localStorage.setItem('obj', JSON.stringify(state));
}
};
};
You can also accomplish a direct save when a property changes,
by using ES5 getters/setters or by using Watch.js
Watch.js example:
var obj = (function () {
// private members
//
var self = {
// Some properties
name: '',
otherName: '',
// Try to load state or use "foo state"
state: JSON.parse(localStorage.getItem('obj')) || {
name: 'foo'
},
save: function () {
localStorage.setItem('obj', JSON.stringify(this.state));
}
};
// Watch the object and save it to local storage, when a property changes
// (Of course, you don't need to call the save method here...)
watch(self, function(property, value) {
console.log('saving state!');
self.state[property] = value;
self.save();
});
return self;
}());
// Update some properties and see that it is saved to local storage.
obj.name = "Some name";
obj.otherName = "Some other name";
console.log(JSON.parse(localStorage.getItem('obj')));
​
Example on JsFiddle.
You could make the state internal and surface getters and setters:
var obj = function () {
// private members
var state = {};
return {
// public members
load: function () {
var loadedState = JSON.parse(localStorage.getItem('obj'));
if (loadedState === null) {
state = {
name: 'foo'
};
} else {
state = loadedState;
}
},
save: function () {
localStorage.setItem('obj', JSON.stringify(state));
},
getState: function (key) {
return state[key];
},
setState: function (key, value) {
state[key] = value;
}
};
};
Using jQuery's extend():
var obj = (function () {
return {
load: function () {
var stored = localStorage.getItem("obj");
var state = stored ? JSON.parse(stored) : {
name: 'foo'
};
$.extend(this, state);
},
save: function () {
localStorage.setItem("obj", JSON.stringify(this));
}
};
})();
// load state
obj.load();
console.log(obj);
// save state
obj.name = 'bar';
obj.save();
jsfiddle
All credit to pimvdb.

Categories