managing object construction parameters in javascript - javascript

I am posting this in hopes that someone might have dealt with a similar problem.
I am using a javascript object that encapsulates paramaters to intialize greater objects in my code, like so :
function MyObject(setup)
{
this.mysetup = setup;
if(typeof this.mysetup == "undefined") { this.mysetup = {} }
if(typeof this.mysetup.stringParameter == "undefined")
{
this.mysetup.stringParameter="string default value"
}
if(typeof this.mysetup.objParameter == "undefined")
{
this.mysetup.objParameter == {}
}
else
{
if(typeof this.mysetup.objParameter.member1 == "undefined")
{
this.mysetup.objParameter.member1 = "member1 default value"
}
}
// ...and so on
}
This way I can make sure not every parameter needs to be in setup, and still MyObject can resort to default values for what is missing.
However, this is a tedious thing to write and quite error prone. So I thought I'd try for a solution that checks the setup against a setupPrototype:
function MyObject(setup)
{
this.setupPrototype = {
stringParameter : "string default value",
objectParameter : { member1 : "member default value"}
}
}
and try to compare the setup against this.setupPrototype.
The function I'm putting together for this purpose looks like
parseSetup = function (obj, objPrototype)
{
var returnedObj = {};
var hasMembers = false;
if(typeof obj=="undefined")
{
returnedObj = objPrototype;
return returnedObj;
}
for(member in objPrototype)
{
hasMembers = true;
//if prototype member is not part of initialization object
if (typeof obj[member]=="undefined")
{
returnedObj[member] = objPrototype[member];
}
else
{
if(objPrototype[member] instanceof Object)
{
if(objPrototype[member] instanceof Array)
{
returnedObj[member]=[];
for(var i=0; i<objPrototype[member].length; i++)
{
returnedObj[member].push(parseSetup(obj[member][i], objPrototype[member][i]))
}
}
else{
returnedObj[member] = parseSetup(obj[member], objPrototype[member])
}
}
else
returnedObj[member] = obj[member];
}
}
if(!hasMembers)
{
if (typeof obj == "undefined")
{
returnedObj = objPrototype;
}
else
returnedObj = obj;
}
return returnedObj;
}
This however is still not up to par.
An additional issue, which I'm debating is whether the original 'setup' should retain any of its own initial properties, or just have whatever is in the prototype. Also, it would be pointless to require that the prototype itself be aware of every possible value the setup might contain, especially for deep nested objects.
So my question is, are you aware of any proper way to solve this problem and end up with a setup object that, where its parameters are missing, can get default values from the prototype, but also not lose its own where they somehow need to be kept?
Many thanks

I would recommend using jQuery and then taking advantage of the $.extend() function, as described on the jQuery plugins page. Basically, you define your default parameters as an object within the constructor method, and then use $.extend() to overwrite only the properties that are supplied in the parameter to your function.
So you might end up with something like:
var MyObject = function (options) {
this.defaultOptions = {
stringParameter: "string default value",
objParameter: {}
};
this.options = $.extend(true, this.defaultOptions, options);
};
To instantiate with the default parameters:
var obj1 = new MyObject({});
To instantiate with an overridden stringParameter:
var obj2 = new MyObject({stringParameter: 'overridden value'});
You can see a demo of this in action here.

Related

Is there any way to convert Ember Object into plain javascript object?

I could not find any way to accomplish the task of such conversion as I could not find any means of getting Ember.js properties for the object. Ember.keys returns only the properties I set in create or with get and the properties declared in Ember.extend do not show up there. I use such properties to set up default values (e.g. [] for array properties)
Here is my dirty workaround
var newModel = JSON.parse(JSON.stringify(model));
I would do something similar to the person above, but I'd do it a little bit differently.
Mixin
App.NativeObject = Ember.Mixin.create({
toNative: function() {
var properties = [];
for (var key in this) {
if (jQuery.inArray(Ember.typeOf(object[key]), ['string', 'number', 'boolean']) !== -1) {
properties.push(key);
}
}
return this.getProperties(properties);
}
});
Object
Then you just need to implement the App.NativeObject mixin in your objects that you would like the toNative on:
var Object = Ember.Object.extend(App.NativeObject, {
name: 'Adam',
count: 4
});
We then have the toNative method on all the objects that implement our mixin.
Obligatory jsFiddle: http://jsfiddle.net/jumUx/
If your object is a subclass of ember-data model notice you can use the toJSON method otherwise you can use:
JSON.parse(JSON.stringify(emberObj))
To grab any values which support native json serialization (i.e. not functions/methods)
This worked for me:
myModel.toJSON({includeId: true})
I'm using Ember 3.
This is what I did and it works quite well. Note, this should be ready only, as any changes to an object or array in the copied object will affect the original object
App.BaseValidations = Ember.Object.create({
toObject: function() {
var destination = {}
for (var k in this) {
if (this.hasOwnProperty(k) && typeof(this[k]) !== 'function') {
destination[k] = this[k];
}
}
return destination;
}
})
something quite simple that worked properly enough for me is :
Ember.Object.reopen({
toJson: function() {
return JSON.parse(JSON.stringify(this));
}
});
at app loading time.
At the moment I solve it with the following snippet:
App.plainCopy = function (obj) {
if (Ember.isArray(obj)) {
return obj.map(App.plainCopy);
} else if (typeof(obj) === "object") {
if (App.Plainable.detect(obj)) {
return obj.plainCopy();
} else {
throw new Error(Ember.String.fmt("%# is not Plainable", [obj]));
}
} else {
return obj;
}
}
App.Plainable = Ember.Mixin.create({
plainCopy: function() {
var props = Ember.keys(this);
var proto = this.constructor.prototype;
for(p in proto) {
if (proto.hasOwnProperty(p) && typeof(this[p])!=="function") {
props.push(p);
}
}
var copy = {};
props.forEach(function(p) {
copy[p] = App.plainCopy(this.get(p));
}, this);
return copy;
}
});
It does not go up the class hierarchy and does not look into mixins (as I use for data objects which are quite simple form that point of view)
With modern (3.17) ember, I've used myEmberObject.getProperties('id', 'name', 'foo', 'bar')
It produces a plain object.
Another possible solution that may suit your needs while not being fully recursive for nested Ember objects:
// where myEmberObject is.. an ember object
var plainJavaScriptObject = myEmberObject.toJSON();
This will only include actual properties that you've defined and no Ember internals. Again, the drawback here is that any nested Ember objects will not, themselves, be converted but will appear as Strings in style of "".

Permission denied to access property 'toString'

I'm trying to find a generic way of getting the name of Constructors. My goal is to create a Convention over configuration framework for KnockoutJS
My idea is to iterate over all objects in the window and when I find the contructor i'm looking for then I can use the index to get the name of the contructor
The code sofar
(function() {
constructors = {};
window.findConstructorName = function(instance) {
var constructor = instance.constructor;
var name = constructors[constructor];
if(name !== undefined) {
return name;
}
var traversed = [];
var nestedFind = function(root) {
if(typeof root == "function" || traversed[root]) {
return
}
traversed[root] = true;
for(var index in root) {
if(root[index] == constructor) {
return index;
}
var found = nestedFind(root[index]);
if(found !== undefined) {
return found;
}
}
}
name = nestedFind(window);
constructors[constructor] = name;
return name;
}
})();
var MyApp = {};
MyApp.Foo = function() {
};
var instance = new MyApp.Foo();
console.log(findConstructorName(instance));
The problem is that I get a Permission denied to access property 'toString' Exception, and i cant even try catch so see which object is causing the problem
Fiddle http://jsfiddle.net/4ZwaV/
Final version in this fiddle
http://jsfiddle.net/2Uvd5/8/
Check here for the embryo of my Convention over configuration plugin
https://github.com/AndersMalmgren/Knockout.BindingConventions
Edit2:
JSFiddle
This solves everything except for one thing: var MyApp = {}; doesn't add it to the window-object. Changing that to window.MyApp = {}; makes it completely working (even within an IFrame).
Edit1:
JSFiddle
Adding to the array by setting the key name requires the key name to be a string so Javascript will automatically call. toString() on your suggested keyname which will fail for certain objects. Instead use .push() to add elements of any type to an array and then .indexOf() to check if it already exists.
Do note that the jsFiddle still breaks because of being placed in an iframe. Opening it in a new tab solves that.
My previous answer (which proved to be invalid when I tried to verify it in your jsFiddle):
You need to check if the constructor is an exact Object. If it is then calling .toString() on it will cause a security exception which I found to be kinda hard to debug. Here's a function I use to get the type of an object in a var-dumper I use.
function GetTypeOfObject(obj) {
if (obj.constructor === window.Object)
return '[object]';
else
return obj.constructor.toString();
}

Is it right to think of a Javascript Function Expression that uses the 'new' keyword as 'static'

I'm just trying to understand Javascript a little deeper.
I created a 'class' gameData that I only want ONE of, doesn't need a constructor, or instantiated.
So I created it like so...
var gameData = new function () {
//May need this later
this.init = function () {
};
this.storageAvailable = function () {
if (typeof (Storage) !== "undefined") {
return true;
}
else {
return false;
}
};
}
Realizing that the 'new' keyword doesn't allow it to be instantiated and makes it available LIKE a static class would be in C#.
Am I thinking of this correctly? As static?
No, it is not static because it still has a constructor property pointing to your "anonymous" function. In your example, you could use
var gameData2 = new (gameData.constructor)();
to reinstantiate a second object, so the "class" (instance actually) is not really "static". You are basically leaking the constructor, and possibly the data that is bound to it. Also, a useless prototype object (gameData.constructor.prototype) does get created and is inserted in the prototype chain of gameData, which is not what you want.
Instead, you might use
a single, simple object literal (as in Daff's answer). That means you don't have a constructor, no closure-scoped private variables (you have used none anyway) and no (custom) prototype.
the (revealing) module pattern (as in jAndy's answer). There you'd have an IIFE to create closure-scoped variables, and can return any kind of object.
an actual constructor ("class") that can be instantiated later (when needed), and yields the same singleton object always.
This is what the singleton pattern would look like:
function GameData() {
if (this.constructor.singleton)
return this.constructor.singleton;
else
this.constructor.singleton = this;
// init:
// * private vars
// * public properties
// ...
}
GameData.prototype.storageAvailable = function () {
if (typeof (Storage) !== "undefined") {
return true;
}
else {
return false;
}
};
var gameData = new GameData();
var gameData2 = new GameData();
gameData === gameData2 === GameData.singleton; // true
Yet, the prototype is quite useless because you have only one instance of GameData. It would only get interesting with inheritance.
There is no Class in ECMAscript, there is only Object.
When new is used to invoke a function, we call it a constructor function. This function somewhat auto returns a new object once it finished. Any data that is stored within that object using this (which references that newly created object) is returned as property of that object. Beside that, new sets a property called constructor to exactly this function.
In your case, you don't even really require the usage of new, you could easily re-write your code as follows:
var gameData = (function () {
var public = { },
private = { }; // any private data can get stored here
//May need this later
public.init = function () {
};
public.storageAvailable = function () {
if (typeof (Storage) !== "undefined") {
return true;
}
else {
return false;
}
};
return public;
}());
This is called the factory pattern, singleton pattern, module pattern, and there might be some other names.
I think what you are looking for is just a simple JavaScript object:
var gameData = {
//May need this later
init : function () {
},
storageAvailable : function () {
if (typeof (Storage) !== "undefined") {
return true;
}
else {
return false;
}
}
}
If you want to use private variables create a revealing module pattern style wrapper. This is basically what jAndy suggested:
var gameData = (function() {
var private = 'private variable';
return {
//May need this later
init : function () {
},
storageAvailable : function () {
if (typeof (Storage) !== "undefined") {
return true;
} else {
return false;
}
}
}
})();

How to stringify inherited objects to JSON?

json2.js seems to ignore members of the parent object when using JSON.stringify(). Example:
require('./json2.js');
function WorldObject(type) {
this.position = 4;
}
function Actor(val) {
this.someVal = 50;
}
Actor.prototype = new WorldObject();
var a = new Actor(2);
console.log(a.position);
console.log(JSON.stringify(a));
The output is:
4
{"someVal":50}
I would expect this output:
4
{"position":0, "someVal":50}
Well that's just the way it is, JSON.stringify does not preserve any of the not-owned properties of the object. You can have a look at an interesting discussion about other drawbacks and possible workarounds here.
Also note that the author has not only documented the problems, but also written a library called HydrateJS that might help you.
The problem is a little bit deeper than it seems at the first sight. Even if a would really stringify to {"position":0, "someVal":50}, then parsing it later would create an object that has the desired properties, but is neither an instance of Actor, nor has it a prototype link to the WorldObject (after all, the parse method doesn't have this info, so it can't possibly restore it that way).
To preserve the prototype chain, clever tricks are necessary (like those used in HydrateJS). If this is not what you are aiming for, maybe you just need to "flatten" the object before stringifying it. To do that, you could e.g. iterate all the properties of the object, regardless of whether they are own or not and re-assign them (this will ensure they get defined on the object itself instead of just inherited from the prototype).
function flatten(obj) {
var result = Object.create(obj);
for(var key in result) {
result[key] = result[key];
}
return result;
}
The way the function is written it doesn't mutate the original object. So using
console.log(JSON.stringify(flatten(a)));
you'll get the output you want and a will stay the same.
Another option would be to define a toJSON method in the object prototype you want to serialize:
function Test(){}
Test.prototype = {
someProperty: "some value",
toJSON: function() {
var tmp = {};
for(var key in this) {
if(typeof this[key] !== 'function')
tmp[key] = this[key];
}
return tmp;
}
};
var t = new Test;
JSON.stringify(t); // returns "{"someProperty" : "some value"}"
This works since JSON.stringify searches for a toJSON method in the object it receives, before trying the native serialization.
Check this fiddle: http://jsfiddle.net/AEGYG/
You can flat-stringify the object using this function:
function flatStringify(x) {
for(var i in x) {
if(!x.hasOwnProperty(i)) {
// weird as it might seem, this actually does the trick! - adds parent property to self
x[i] = x[i];
}
}
return JSON.stringify(x);
}
Here is a recursive version of the snippet #TomasVana included in his answer, in case there is inheritance in multiple levels of your object tree:
var flatten = function(obj) {
if (obj === null) {
return null;
}
if (Array.isArray(obj)) {
var newObj = [];
for (var i = 0; i < obj.length; i++) {
if (typeof obj[i] === 'object') {
newObj.push(flatten(obj[i]));
}
else {
newObj.push(obj[i]);
}
}
return newObj;
}
var result = Object.create(obj);
for(var key in result) {
if (typeof result[key] === 'object') {
result[key] = flatten(result[key]);
}
else {
result[key] = result[key];
}
}
return result;
}
And it keeps arrays as arrays. Call it the same way:
console.log(JSON.stringify(flatten(visualDataViews)));
While the flatten approach in general works, the snippets in other answers posted so far don't work for properties that are not modifiable, for example if the prototype has been frozen. To handle this case, you would need to create a new object and assign the properties to this new object. Since you're just stringifying the resulting object, object identity and other JavaScript internals probably don't matter, so it's perfectly fine to return a new object. This approach is also arguably more readable than reassigning an object's properties to itself, since it doesn't look like a no-op:
function flatten(obj) {
var ret = {};
for (var i in obj) {
ret[i] = obj[i];
}
return ret;
}
JSON.stringify takes three options
JSON.stringify(value[, replacer[, space]])
So, make use of the replacer, which is a function, that is called recursively for every key-value-pair.
Next Problem, to get really everything, you need to follow the prototpes and you must use getOwnPropertyNames to get all property names (more than you can catch with keysor for…in):
var getAllPropertyNames = () => {
const seen = new WeakSet();
return (obj) => {
let props = [];
do {
if (seen.has(obj)) return [];
seen.add(obj);
Object.getOwnPropertyNames(obj).forEach((prop) => {
if (props.indexOf(prop) === -1) props.push(prop);
});
} while ((obj = Object.getPrototypeOf(obj)));
return props;
};
};
var flatten = () => {
const seen = new WeakSet();
const getPropertyNames = getAllPropertyNames();
return (key, value) => {
if (value !== null && typeof value === "object") {
if (seen.has(value)) return;
seen.add(value);
let result = {};
getPropertyNames(value).forEach((k) => (result[k] = value[k]));
return result;
}
return value;
};
};
Then flatten the object to JSON:
JSON.stringify(myValue, flatten());
Notes:
I had a case where value was null, but typeof value was "object"
Circular references must bee detected, so it needs seen

Javascript - Reflecting encapsulated members

I created a javascript "class" as follows:
function MyClass(member1, member2) {
this.Member1 = member1;
this.Member2 = member2;
}
All members are Strings.
I want to take an instance of MyClass and "clean" the members by calling
function NoneBecomesNull(item) {
if (item === "[None]")
item = "";
return item;
}
for-each member of the class. Is there an effecient way to accomplish this task? (In the case where MyClass has 30 members).
I would like to avoid doing...
myClassInstance.Member1 = NoneBecomesNull(myClassInstance.Member1);
myClassInstance.Member2 = NoneBecomesNull(myClassInstance.Member2);
//...30+ times
Try the following
for (var name in theObject) {
if (theObject.hasOwnProperty(name) && theObject[name] === "[None]") {
theObject[name] = "";
}
}
I used hasOwnProperty to prevent the reseting of properties higher up in the prototype chain. Your example didn't show the use of a prototype chain here and hence it's likely not necessary for this example. But it's good practice.
Why not encapsulate this behaviour inside your object?
WORKING EXAMPLE
function MyClass(member1, member2) {
this.Member1 = member1;
this.Member2 = member2;
this.clean = function() {
for ( var member in this ) {
if (this.hasOwnProperty(member) && this[member] === "[None]") {
this[member] = "";
}
}
};
}
Then it only takes one line to accomplish...
var obj = new MyClass("[None]", "hello");
obj.clean();

Categories

Resources