How can you programmatically add a function to a nested namespace? - javascript

Say i have this function that dynamically creates my namespace for me when I just pass it a string, (I'm pretty sure basically what YUI JS library does):
MyObj.namespace('fn.method.name');
would result in
MyObj.fn.method.name = {}
being created - all three levels being equivalent to an empty object.
Now, what I want to do, though, is make the last level, in this case name, set to a function, but without having to redeclare the newly created object.
So instead of doing this:
function fnName() { /* some code here */ }
MyObj.namespace('fn.method.name');
MyObj.fn.method.name = new fnName();
i want to call something like:
MyObj.add('fn.method.name', fnName);
And internally, the add method would programmatically instantiate the passed in function:
MyObj.fn.method.name = new fnName()
In the way I have it implemented, I can create the namespace object and set it to an empty object, however, when I try to instantiate a passed in function and associate that namespace with the passed in function, it never gets added to the namespace. Instead, an empty object is always returned. Any ideas?
edit: Here is the namespace method. this is attached to the base object as a JSON object, so please ignore the formatting:
namespace: function (ns) {
var _ns = ns.split('.'),
i = 0, nsLen = _ns.length,
root = this;
if (_ns[0] === gNS) {
_ns.shift();
nsLen = _ns.length;
}
for (i = 0; i < nsLen; i++) {
// create a property if it doesn't exist
var newNs = _ns[i];
if (typeof root[newNs] === "undefined") {
root[newNs] = {};
}
root = root[newNs];
}
return root;
}
edit2 - removed the passed in fn argument

Were you looking for something like this:
var root = {};
function create(ns, fn) {
var nsArray = ns.split(/\./);
var currentNode = root;
while(nsArray.length > 1) {
var newNS = nsArray.shift();
if(typeof currentNode[newNS] === "undefined") {
currentNode[newNS] = {};
}
currentNode = currentNode[newNS];
}
if(fn) {
currentNode[nsArray.shift()] = fn;
}
else {
currentNode[nsArray.shift()] = {};
}
}
Then:
create("a.b.c");
console.log(root.a);
console.log(root.a.b);
console.log(root.a.b.c);
Gives:
Object { b={...}}
Object { c={...}}
Object {}
And:
create("d.e.f", function() { console.log("o hai"); });
console.log(root.d);
console.log(root.d.e);
console.log(root.d.e.f);
Gives:
Object { e={...}}
Object {}
function()
Calling the function you defined:
root.d.e.f();
Gives:
o hai

Well you haven't given the namespace function but your add function could look something like this:
MyObj.add = function (namespace, value) {
var names = namespace.split('.'), current = this, name;
while (names.length > 1) {
name = names.shift();
current[name] = {};
current = current[name];
}
current[names[0]] = value;
};
This code assigns the value given to the last part of the namespace. You could modify it to current[names[0] = new value(); if you want the object constructed by the passed in function (and you are assuming the constructor function takes no arguments).

function ns() {
var root = window;
for (var i = 0; i < arguments.length; i++) {
var arr = arguments[i].split(/\./);
for (var j = 0; j < arr.length; j++) {
var item = arr[j];
if (typeof item !== 'string') {
root = item;
}
else {
if (!root[item]) {
root[item] = {};
}
root = root[item];
}
}
root = window;
}
}
then you can create using
ns('fn.method.name');
or
ns('fn.method.name','fn.method.secondName');
and call using
fn.method.name
this function creates your namespace on 'window' so alternatively you can use
window.fn.method.name

Related

Change variable with string instead of just accessing it

I can access a variable easily:
function accessVariable(accessFunction, variable) {
var namespaces = accessFunction.split(".");
for (var b = 0; b < namespaces.length; b++) {
if (namespaces[b] != "") {
try {
variable = variable[namespaces[b]];
} catch {
variable = undefined;
};
};
return variable;
};
But updating this gotten variable is something that I don't know how to do.
It's easiest if you use a separate function to update. This can take the new value as another argument.
Then you stop the iteration before the last item in namespace, and use that as the index to assign to.
function updateVariable(accessFunction, variable, value) {
var namespaces = accessFunction.split(".");
for (var b = 0; b < namespaces.length - 1; b++) {
if (namespaces[b] != "") {
try {
variable = variable[namespaces[b]];
} catch {
variable = undefined;
}
}
}
if (variable) {
variable[namespaces.pop()] = value;
}
}
let obj = {a: {b:[{c: 10}]}};
updateVariable('a.b.0.c', obj, 20);
console.log(obj);
Define a single global-scoped variable, and put your variables there.
var Glob = {}; // globally scoped object
function changeVar(){
Glob.variable1 = 'value1';
}
function SeeVar(){
alert(Glob.variable1); // shows 'value1'
}

custom prototype function for nested json to check a key exists like Prototype.hasOwnproperty()

I am currently doing this by this method. Need a better implementation for this Scenario:
Here is the following:
var testjson = {
"key1":"val1",
"key2":"val2",
"key3":{
"k2":"v2",
"k3":{
"k4":"v4",
"k5":"v5"
}
},
"haskey": function (base, path) {
var current = base;
var components = path.split(".");
for (var i = 0; i < components.length; i++) {
if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
return false;
}
current = current[components[i]];
}
return true;
}
}
console.log( testjson.haskey(testjson,"key3.k3.k4"));
First of all Sorry for asking misleading question.I just need a method for checking a nested object Property exists in a JavaScript object or not. There is nothing to do with prototype with this question. I just created a custom method with two argument one for object and another for property to check.This works fine, I came to a conclusion after looking at this ans->Checking if a key exists in a JavaScript object?
Here is my Method to check the property exist or not in an Object
var testobject = {
"key1": "value",
"key2": {
"key3": "value"
}
}
function checkproperty(object, path) {
var current = object;
var components = path.split(".");
for (var i = 0; i < components.length; i++) {
if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
return false;
}
current = current[components[i]];
}
return true;
}
console.log(checkproperty(testobject, "key2.key3"))

Easy way to set javascript object multilevel property?

I am trying to create a javascript object like
var allUserExpiry={};
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
But I am getting an error like allUserExpiry[aData.userId] undefined.
Is there a way, whereby I can set multi-level JS-Object keys? or is it important that I should go by doing allUserExpiry[aData.userId]={}, then allUserExpiry[aData.userId][aData.courseId]={} ?
Please let me know if there are any utility functions available for the same.
No, there is no way to set "multilevel keys". You need to initialize each object before trying to add properties to it.
var allUserExpiry = {};
allUserExpiry[aData.userId] = {}
allUserExpiry[aData.userId][aData.courseId] = {}
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
Using Computed property names from ES6, it is possible to do:
var allUserExpiry = {
[aData.userId] = {
[aData.courseId]: {
[aData.uscId]: aData
}
}
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
Simply use loadash,
let object = {};
let property = "a.b.c";
let value = 1;
_.set(object, property, value); // sets property based on path
let value = _.get(object, property, default); // gets property based on path
Or you can do it:
function setByPath(obj, path, value) {
var parts = path.split('.');
var o = obj;
if (parts.length > 1) {
for (var i = 0; i < parts.length - 1; i++) {
if (!o[parts[i]])
o[parts[i]] = {};
o = o[parts[i]];
}
}
o[parts[parts.length - 1]] = value;
}
And use:
setByPath(obj, 'path.path2.path', someValue);
This approach has many weak places, but for fun... :)
Why not just do this?
var allUserExpiry={};
allUserExpiry[aData.userId] = {aData.courseId: {aData.uscId: aData}};
I have a pretty hacky but short way of doing it in IE9+ as well as real browsers.
Given var path = 'aaa.bbb.ccc.ddd.eee'; where path is what your intending to make into an object and var result = {}; will will create the object {aaa: {bbb: {ccc: {ddd: {eee: {}}}}}
result = {}
path.split('.').reduce(function(prev, e) {
var newObj = {};
prev[e] = newObj;
return newObj;
}, result);
will store the object in result.
How it works:
split('.') converts the input into ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
reduce(function (...) {...}, result) runs through the array created by split, and for each entry will pass along a returned value to the next one. In our case we pass the new object through after adding the new object to the old one. This creates a chain of objects. reduce returns the last object you return inside of it, so we have to defined result beforehand.
This relies on using references so it won't be immediately clear how it works if you're expecting your code to be maintained by anyone else and should probably be avoided to be honest, but it works at least.
You can also use the following to create the initial structure:
var x = function(obj, keys) {
if (!obj) return;
var i, t;
for (i = 0; i < keys.length; i++) {
if (!t) {
t = obj[keys[i]] = {};
} else {
t[keys[i]] = {};
t = t[keys[i]];
}
}
};
var a = {};
x(a, ['A', 'B', 'C', 'D', 'E', 'F']);
Another approach without strings or array as argument.
function fillObject() {
var o = arguments[0];
for(var i = 1; i < arguments.length-1; i++) {
if(!o.hasOwnProperty(arguments[i])) {
o[arguments[i]] = {};
}
if(i < arguments.length-2) {
o = o[arguments[i]];
}else {
o[arguments[i]] = arguments[i+1]
}
}
}
var myObj = {"foo":{}};
fillObject(myObj,"back","to","the","future",2);
console.log(JSON.stringify(myObj));
// {"foo":{},"back":{"to":{"the":{"future":2}}}}
But I wouldn't use it :-) It's just for fun.
Because I don't like too much intelligent algorithm. (If it was in this category)
Using lodash you can do this easily (node exists and empty check for that node)..
var lodash = require('lodash-contrib');
function invalidateRequest(obj, param) {
var valid = true;
param.forEach(function(val) {
if(!lodash.hasPath(obj, val)) {
valid = false;
} else {
if(lodash.getPath(obj, val) == null || lodash.getPath(obj, val) == undefined || lodash.getPath(obj, val) == '') {
valid = false;
}
}
});
return valid;
}
Usage:
leaveDetails = {
"startDay": 1414998000000,
"endDay": 1415084400000,
"test": { "test1" : 1234 }
};
var validate;
validate = invalidateRequest(leaveDetails, ['startDay', 'endDay', 'test.test1']);
it will return boolean.
Another solution using reduce function (thanks Brian K).
Here we created a get/set to general proposes. The first function return the value in any level. The key is splited considering the separator. the function return the value refered from last index in the key's array
The second function will set the new value considering the last index of the splited key
the code:
function getObjectMultiLevelValue(_array,key,separator){
key = key.split(separator || '.');
var _value = JSON.parse(JSON.stringify(_array));
for(var ki in key){
_value = _value[key[ki]];
}
return _value;
}
function setObjectMultiLevelValue(_array,key,value,forcemode,separator){
key.split(separator || '.').reduce(function(prev, currKey, currIndex,keysArr) {
var newObj = {};
if(prev[currKey] && !forcemode){
newObj = prev[currKey];
}
if(keysArr[keysArr.length-1] == currKey){
newObj = value;
prev[currKey] = newObj;
}
prev[currKey] = newObj;
return newObj;
}, _array);
return _array;
}
//testing the function
//creating an array
var _someArray = {a:'a',b:'b',c:{c1:'c1',c2:{c21:'nothing here...'}}};
//a multilevel key to test
var _key = 'a,a1,a21';
//any value
var _value = 'new foo in a21 key forcing replace old path';
//here the new value will be inserted even if the path exists (forcemode=true). Using comma separator
setObjectMultiLevelValue(_someArray,_key,_value,true,',');
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//inserting another value in another key... using default separator
_key = 'c.c2.c21';
_value = 'new foo in c21 key';
setObjectMultiLevelValue(_someArray,_key,_value);
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//recovering the saved value with different separators
_key = 'a,a1,a21';
console.log(getObjectMultiLevelValue(_someArray,_key,','));
_key = 'c.c2.c21';
console.log(getObjectMultiLevelValue(_someArray,_key));
Let assume our object is
const data = {
//some other data
userInfo: {},
};
First, define a new property of that object
data.userInfo.vehicle = {};
then simply
data.userInfo.vehicle.vehicleType = state.userInfo.vehicleType;

How to deep-copy closure objects (private objects) while Deep Copying

Board = function()
{
var cells = [8];
/**
* Initializing every cell using numeric format.
* */
for (var i=0 ; i<8; i++){
cells[i] = [8];
for (var j=0 ; j<8; j++)
cells[i][j] = new Cell(new Position(i,j));
}
....
}
In Another code GameManager.js,
var duplicateBoard = Copy.deepCopy(board);
board.moveCell(1,2)
And for Deepcopying I am using,
Ref : http://jsperf.com/deep-copy-vs-json-stringify-json-parse
function deepCopy(o) {
var copy = o,k;
if (o && typeof o === 'object') {
copy = Object.prototype.toString.call(o) === '[object Array]' ? [] : {};
for (k in o) {
copy[k] = deepCopy(o[k]);
}
}
return copy;
}
My need :
I want cells (private member of constructor ) in Board to be deep-copied.
Problem :
But, When I debugged with firebug, I saw, deepCopy function does not deep copying private objects of constructor.
My Case :
board.moveCell(1,2), Here cell[1][2] is moved in duplicateBoard too.
That is,
No deep-copying of cell has taken place
Both the board and duplicateBoard has same reference to cell[1][2].
What I have traced ?
The deep-copy function, treats the constructor to be a function, hence it ignores deep-copying the functions, since it will fail in typeof o === 'object. But removing this condition is not useful, because by doing so, duplicateBoard has no functions rather all the functions to be object{} type.
This cannot be done as the "private" variable is local to the function(constructor). With the way that JS works, even by cloning the functions you will still get a pointer from the original object (http://jsfiddle.net/kBzQP/),
function deepCopy(o) {
if(o == null || typeof(o) != 'object') {
return o;
}
var newObj = new o.constructor();
for(var key in o) {
newObj[key] = deepCopy(o[key]);
}
return newObj;
}
if you do not clone functions then you get a brand new set of private variables with all the public variables cloned (http://jsfiddle.net/kBzQP/3/).
function deepCopy(o) {
if(o == null || typeof(o) != 'object') {
return o;
}
var newObj = new o.constructor();
for(var key in o) {
if(typeof(o) != 'function') continue;
newObj[key] = deepCopy(o[key]);
}
return newObj;
}
The best way to handle this is to make your private variables publicly accessible but give them a different naming convention such as "_myPrivateVariable". This way the variables will be cloned and anyone else using your class will know that this is a private variable.
So in your case it would be:
Board = function()
{
this._cells = [8];
/**
* Initializing every cell using numeric format.
* */
for (var i=0 ; i<8; i++){
this._cells[i] = [8];
for (var j=0 ; j<8; j++)
this._cells[i][j] = new Cell(new Position(i,j));
}
....
}
For reference sake check here: Copy javascript object with private member
It's not a good solution as all functions accessing your "private" cells variable have to declared as this.someFunction instead of Board.prototype so each Board instance will have their own funcions instead of sharing them.
Here is some sample code that would break prototype (c instanceof b is not true) but since you can't use prototype because you need to access closure variables in your functions that would not matter.
function Test(privates) {
var msg = [];
if(privates!==undefined){
msg=deepCopy(privates.msg,[]);
}
this.Message = function(newMsg) {
if (newMsg) {
msg.push(newMsg);
} else {
return msg;
}
}
this.clone=function(){
var orgMsg=msg
var ret = function(){
Test.call(this,{msg:orgMsg});
}
return deepCopy(this,new ret());
}
}
// this does not set prototype correctly
function deepCopy(from,to) {
if(from == null || typeof(from) != 'object') {
return from;
}
for(var key in from) {
// this.Message has closure ref to msg
// you can't copy it because we've set a new
// closure ref
if(typeof from[key]!=="function"){
to[key] = deepCopy(from[key]);
}
}
return to;
}
var b = new Test();
b.Message("Before cloning");
console.log("b message before cloning:",b.Message());
var c = b.clone();
console.log("c message after cloning:",c.Message());
b.Message("From BB after Clone");
console.log("c message after pushing new item in b:",c.Message());
c.Message("From CC after Clone");
console.log("b message after pushing new item in c:",b.Message());
console.log("c message after pushing new item in b:",c.Message());
[UPDATE]
Why this is a bad desing is because you can't declare your object methods as prototype:
Test.prototype.Message(){
//here the msg variable doesn't exist
}
This forces you to declare all your methods in the Test body with "this.someFunction" syntax. If you create multiple Test instances than each instance has it's own set of methods doing the exact same thing. To save resources you should use prototype but then you can't access closure varibales in these funcitons so you can't. Please read this on prototype basics: Prototypical inheritance - writing up
Maybe if you only have a couple of instances it wouldn't matter but technically you can't clone these objects. A real clone of b in the above code would be typeof Test but in the code above cloned instance of "b" called "c" is not typeof Test and there is no way I can see setting it without breaking the newly set closure variable called "msg".
Use $.extend():
var testObj = function() {
var rand = Math.random(0, 1);
this.r = function() {
return rand;
};
this.changeRand = function() {
rand = Math.random(0, 1);
};
};
var obj1 = new testObj();
alert(obj1.r());
obj1.changeRand();
alert(obj1.r());
var obj2 = $.extend(true, {}, obj1);
alert(obj2.r());
alert(obj1.r() === obj2.r()); // true
JSFiddle
In the same way you should use it for your board:
var Board = function() {
var cells = [8];
/**
* Initializing every cell using numeric format.
* */
for (var i=0 ; i<8; i++){
cells[i] = [8];
for (var j=0 ; j<8; j++)
cells[i][j] = new Cell(new Position(i,j));
}
}
var board = new Board(),
copy = $.extend(true, {}, board);
Normally I try to avoid using jQuery, but in this case it seems perfect...

Reference to current object in constructor

I'm trying to define a class that, in its constructor, instantiates other objects and passes them a reference to itself:
var Child = function(m) {
var mother = m;
return {
mother: mother
}
}
var Mother = function() {
var children = makeChildren();
return {
children: children
}
function makeChildren() {
var children = [];
for (var i = 0; i < 10; i++) {
var c = new Child(this); // <--- 'this' is an empty object here
children.push(c)
}
return children;
}
}
This doesn't work, and the Child instances end up with an empty object in their mother property. What is the proper way to do this?
Javascript's this is not lexical. This means that makeChildren gets its own this instead of getting the Mother's this you want.
Set a normal variable to this and use it instead.
var that = this;
function makeChildren(){
blabla = that;
}
I don't think doing this is just enough though. By returning an object from the constructor you ignore the this. Set things into it:
this.children = children;
instead of returning a new object.
You could try passing a reference to the mother object when you call makeChildren() from within the mother object, something like this maybe:
var Mother = function() {
var children = makeChildren(this);
}
The makeChildren() function can then accept as an argument the reference, which you can use:
function makeChildren(ref)
var c = new Child(ref);
No idea whether or not that will work, but it might be worth a try.
A nested function does not inherit this from its parent, so the this within makeChildren() is not the same as the this within the Mother constructor unless you explicitly set it when calling makeChildren():
var children = makeChildren.call(this);
That should work without any further changes to your code. Have a look at MDN for more detail about .call().
Alternatively you can save a reference to this and pass that into the function:
var Mother = function() {
var self = this; // <-- new variable
var children = makeChildren();
return {
children: children
}
function makeChildren() {
var children = [];
for (var i = 0; i < 10; i++) {
var c = new Child(self); // <--- change 'this' to 'self'
children.push(c)
}
return children;
}
}
Local variables within a function are accessible to nested functions.
var Child = function(m) {
var mother = m;
return {
mother: mother
}
};
var Mother = function() {
if (!(this instanceof Mother)) {
return new Mother();
}
var that = this;
var makeChildren = function() {
var children = [];
for (var i = 0; i < 10; i++) {
var c = new Child(that); // <--- 'that' is the reference to Mother
children.push(c)
}
return children;
};
var children = makeChildren();
return {
children: children
}
};
then do:
var m = Mother();
The first three lines in the Mother object ensure that the value of that is the Mother instance and not the global object. Without them, you would always have to write:
var m = new Mother();

Categories