Why can't I run a ForIn to merge the configs on a constructor?
I have got this tiny class that has a bunch of open configurations, so I would like to simply run a ForIn in order to shorten the code.
Basically turn this
var FormMessage = (function() {
...
function FormMessage(args) {
this.Conf = {
mobile:args.mobile || Configurations.mobile,
form: {
selector: args.form.selector || Configurations.formSelector
},
...
}
}
...
return FormMessage;
})();
var i = new FormMessage({mobile: true, form:{selector: '.the-first'}}),
e = new FormMessage({mobile: false, form:{selector: '.the-second'}});
into this
var FormMessage = (function() {
...
function FormMessage(args) {
for (var attr in args) { this.Conf[attr] = args[attr]; }
}
...
return FormMessage;
})();
var i = new FormMessage({mobile: true, form:{selector: '.the-first'}}),
e = new FormMessage({mobile: false, form:{selector: '.the-second'}});
But for if I do this then the second time I initialize the object it will override the first one.
Do you guys know how can I make this work?
Thanks for the help.
Perhaps along something like the code below? I also think that the IIFE is unnecessary.
function FormMessage(conf, args) {
Object.keys(args).forEach(e => conf[e] = args[e]);
return Object.assign({}, conf); // returns a fresh new object
}
// initial config object
var confObj = {mobile: 'wow'};
var i = new FormMessage(confObj, {mobile: true, form:{selector: '.the-first'}}),
e = new FormMessage(confObj, {mobile: false, form:{selector: '.the-second'}});
console.log(i, e);
Related
I have an array of objects. Each object has a method that should update a boolean property in the same object called 'found'.
When I call the function, the property does not update. I am not sure why.
I thought that the 'found' property would be accessible but it isn't??
I have created a minimal version of the problem here:
https://codepen.io/sspboyd/pen/XWYKMrv?editors=0011
const gen_s = function () { // generate and return the object
let found = false;
const change_found = function () {
found = true;
};
const update = function () {
change_found();
};
return {
change_found,
found,
update
};
};
const s_arr = []; // initialize an array
s_arr.push(gen_s()); // add a new s object to the array
console.log(s_arr[0].found); // returns 'false'
s_arr.forEach((s) => {
s.update();
});
console.log(s_arr[0].found);
When your change_found function changes the value of found, it's changing the value pointed to by your let found variable, but the object returned by your gen_s function still points to the old value.
You can fix your code using the 'holder' pattern, like this:
const gen_s = function () { // generate and return the object
let foundHolder = {value: false};
const change_found = function () {
foundHolder.value = true;
};
const update = function () {
change_found();
};
return {
change_found,
foundHolder,
update
};
};
const s_arr = []; // initialize an array
s_arr.push(gen_s()); // add a new s object to the array
console.log(s_arr[0].foundHolder.value); // returns 'false'
s_arr.forEach((s) => {
s.update();
});
console.log(s_arr[0].foundHolder.value);
Or even better, use a class:
class S {
constructor() { this.found = false; }
change_found() { this.found = true; }
update() { this.change_found(); }
}
const s_arr = [];
s_arr.push(new S());
console.log(s_arr[0].found);
s_arr.forEach(s => s.update());
console.log(s_arr[0].found);
Here is the pseudo-code in question: https://jsfiddle.net/yzps2gef/40/
I'm trying to understand why I cannot access an object's properties directly in one scenario (see ISSUE #1 in comments) but I can in another scenario (see ISSUE #2 in comments). I'm failing to see the difference between the two. Thanks!
Here's the fiddle code:
window.DataStore = function () {
var url = new Url(),
filters = new Filters(),
orderBy,
orderByDir,
setOrderBy = function (x, y) {
orderBy = x;
orderByDir = y;
},
getOrderBy = function () {
return orderBy;
},
getOrderByDir = function () {
return orderByDir;
};
return {
url: url,
filters: filters,
orderBy: orderBy,
orderByDir: orderByDir,
setOrderBy: setOrderBy,
getOrderBy: getOrderBy,
getOrderByDir: getOrderByDir
};
};
window.Url = function () {
var get = function (ds) {
var url = 'xyz.php';
console.log(ds);
// ISSUE #1: These do not work. It results in: xyz.php?orderby=undefined&orderbydir=undefined.
// Why can't I access them directly like I do below with the dataStore.filters.someFilterOption?
url = url + '?orderby=' + ds.orderBy;
url = url + '&orderbydir=' + ds.orderByDir;
// These work when I use the "get" functions.
// url = url + '?orderby=' + ds.getOrderBy();
// url = url + '&orderbydir=' + ds.getOrderByDir();
return url;
}
return {
get: get
};
};
window.Filters = function () {
var someFilterOption = 0;
return {
someFilterOption: someFilterOption
};
};
window.Grid = function () {
var dataStore = new DataStore(),
doSearch = function () {
console.log(dataStore.url.get(dataStore));
},
render = function () {
doSearch();
// ISSUE #2: Why can I access this one directly but not the order bys?
if (dataStore.filters.someFilterOption) {
console.log('Why was I able to read this one (dataStore.filters.someFilterOption) directly and not have to have a getSomeFilterOption() function to read it? But when it comes to the orderBy and orderByDir above I cannot read them directly.');
}
}
return {
dataStore: dataStore,
render: render
};
};
window.MyReUsableGrid = function () {
var grid = new Grid(),
showSomeFilterOption = function () {
grid.dataStore.filters.someFilterOption = 1;
},
render = function () {
grid.render();
};
grid.dataStore.setOrderBy(4, 'asc');
return {
showSomeFilterOption: showSomeFilterOption,
render: render
};
};
// The Screen
var myGridScreen = new MyReUsableGrid();
myGridScreen.showSomeFilterOption();
myGridScreen.render();
Because when your object gets returned from the function this line gets evaluated:
orderBy: orderBy,
And as the variable orderBy isnt set yet it is actually:
orderBy: undefined
Now later you call setOrderBy and set the internal variable orderBy to a value which you can expose through the getter, but that doesnt get reflected to the objects property.
IMO the whole thing should be restructured so that the methods work with their context:
window.DataStore = () => ({
url: new Url(),
filters: new Filters(),
applyOrder(order, dir) {
this.orderBy = order;
this.orderByDir = dir;
},
});
That way you dont need getters at all.
I learn to create my own library. This is new for me. I start learn with this library, if you see, I have similar clear function like that library, but my code didn't replace var history.
Init value: var history = [];
Show(): return []
Add ('asd'): history[0] = 'asd';
Show(): return [asd]
Clear: revert var history to empty var history = []
Show(): return [asd] what I expect is []
Here is my code:
;(function() {
'use strict';
var testLib = function() {
var index = -1,
history = [];
return {
add: function(data) {
history[history.length] = data;
},
show: function() {
return history;
},
clear: function() {
var index = -1,
history = [];
}
};
};
window.testLib = testLib;
}());
$(function() {
var mylib = new testLib();
mylib.add('asdasd');
console.log(mylib.show());
mylib.clear();
console.log(mylib.show()); //expect: [] empty array
});
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
Just remove the var in your clear method:
clear: function() {
index = -1;
history = [];
}
You are declaring new variables instead of modifying the ones in your closure. In modern ECMAScript, you might want to consider creating your library as a "class":
class History{
// ...
clear() {
this.history = [];
this.index = -1;
}
}
Just set your variables in the proper scope ;)
First in your sample, you redefine variables in your clear function, by using the keyword var.
Then, from my experience, it is simpler to set everything in the same object and not have private scopes inside your object. This way, you can work in a more flexible way!
So, do not use var, set your properties in the object you return, and use this and it will work fine!
(function() {
'use strict';
var testLib = function() {
return {
index: -1,
history: [],
add: function(data) {
this.history[this.history.length] = data;
},
show: function() {
return this.history;
},
clear: function() {
this.index = -1;
this.history = [];
}
};
};
window.testLib = testLib;
}());
$(function() {
var mylib = testLib();
mylib.add('asdasd');
console.log(mylib.show());
mylib.clear();
console.log(mylib.show()); //expect: [] empty array
});
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
here's a tricky one. I create a class...
App = function(o) {
var _app = this;
this.events = {
listeners : {
list : new Array(),
add : function(event, fn) {
if (! this.list[event]) this.list[event] = new Array();
if (!(fn in this.list[event]) && fn instanceof Function) this.list[event].push(fn);
if (_app.debug.get()) _app.events.dispatch('log.append','EVENTS:ADD:'+event);
},
remove : function(event, fn) {
if (! this.list[event]) return;
for (var i=0, l=this.list[event].length; i<l; i++) {
if (this.list[event][i] === fn) {
if (_app.debug.get()) _app.events.dispatch('log.append','EVENTS:REMOVE:'+event);
this.list[event].slice(i,1);
break;
}
}
}
},
dispatch : function(event, params) {
if (! this.listeners.list[event]) return;
for (var i=0, l=this.listeners.list[event].length; i<l; i++) {
if (_app.debug.get()) _app.events.dispatch('log.append','EVENTS:DISPATCH:'+event);
this.listeners.list[event][i].call(window, params);
}
}
};
};
and prototype more functionality later. Here's one;
App.prototype.connection = {
source : { 'default' : null },
types : new Array(),
pool : new Array(),
count : function() { return this.pool.length },
active : {
pool : new Array(),
count : function() { return this.pool.length },
add : function(o) { this.pool.push(o) },
remove : function(o) { this.pool.splice(this.pool.indexOf(o), 1); }
},
create : function(o) {
if (! o || ! o.exe) o.exe = this.source.default;
if (! o || ! o.type) o.type = 'xhr';
var c = new this.types[o.type];
App.events.dispatch('connection.created',c);
this.pool.push(c);
return c;
},
remove : function(o) {
App.events.dispatch('connection.removed',o);
this.pool.splice(this.pool.indexOf(o), 1);
},
abort : function(o) {
var i = this.pool.indexOf(o);
if (i===-1) return;
this.pool[i].abort();
}
};
then instantiate this into an object.
app = new App();
The problem is, I have a line called App.events.dispatch('connection.removed',o) which doesn't work. App needs to be the instantiation 'app' which ideally would be 'this', but this refers to App.prototype.connection. How do you get at the root in this case?
Thanks - Andrew
You cannot use the object literal approach to define the connection on the prototype, otherwise there's no way to access the App instance.
Note that when you are referencing App, you are referencing the constructor function, not the App instance. Also, this inside create for instance would not be working because this will point to the connection object, not the App instance either.
There are a few options:
function Connection(eventBus) {
this.eventBus = eventBus;
}
Connection.prototype = {
someFunction: function () {
this.eventBus.dispatch(...);
}
};
function App() {
// this.events = ...
//the instance could also be injected, but you would need to implement
//a setEventBus on the connection object, or simply do conn.eventBus = this;
this.connection = new Connection(this);
}
var App = new App();
Also, please note that all mutable values (e.g. objects) defined on the prototype will be shared across all instances. That's probably not what you want.
Also note that:
listeners : {
list : new Array()
Should be:
listeners : {
list : {}
An array is meant to have numeric indexes only, while a plain object is a better structure to use as a map.
In the following code, I want to be able to call bindClickEvents() like so:
App.Utils.Modal.bindClickEvents();
However, I don't understand the syntax necessary to do this.
Current code:
var App = new Object;
App.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Modal.bindClickEvents();
});
You can do it in one go:
var App = {
Modal : {
bindClickEvents : function () {/* ... */}
}
}
or if you want to break that up to separate steps:
var App = {};
App.Modal = {};
Modal.bindClickEvents = function () {/* ... */};
BTW, in reference to your original question title, this is not object chaining. This is object composition. Object chaining is being able to call methods in an object multiple times in a single statement.
Is this what you're trying to do?
var App = {};
App.Utils = {};
App.Utils.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Utils.Modal.bindClickEvents();
});
Prefer the object literal syntax to the Object constructor; some authors go so far as to call the latter an anti-pattern
Here's the simplest way to set up App.Utils.Modal.bindClickEvents();
var App = {
Utils: {
Modal: {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
}
}
};
Or you can piece it together one step at a time:
var App = {};
App.Utils = {};
App.Utils.Modal = {};
App.Utils.Modal.bindClickEvents = function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
};