I have for some time wondered if there is any quicker way to instantiate a chain of objects in in javascript.
Lets say that we for instance have the following "chain of objects"
window.myobject1.myobject2.myobject3 = {}
This will ofcourse not work.. since object1 and object2 are not instantiated..
however.. to get this to work.. I would have to do something like:
window.myobject1 = {}
window.myobject1.myobject2 = {}
window.myobject1.myobject2.myobject3 = {}
Which simply just seems silly.. in a more realistic case.. lets say we have
window.server.responses.currentuser.id
where server,responses and currentuser simply act as "namespaces"/empty enities..
Would there be any way where I can tell javascript to instantiate the whole chain as objects?.. so I don't need to instantiate each part of the chain on a new line?
This might be a bad example but I guess you get it..
for instance I might also have:
window.server.responses.currentuser.id
window.server.responses.theotherusers.personone.id
window.server.responses.labels.personname
etc..
Thanks in advance!
I've answered this question before but can't seem to find it so can't mark this as duplicate (hehe). Anyway, you can basically do it with a function:
function deep_set (obj,path,value) {
var name = path.shift();
if (path.length) {
if (typeof obj[name] == undefined) {
obj[name] = {};
}
if (typeof obj[name] == 'string' || typeof obj[name] == 'number') {
throw new Error('attempted to access string or number as object');
}
deep_set(obj[name],path,value);
}
else {
obj[name] = value;
}
}
So you can now do:
deep_set(window,['server','responses','currentuser','id'],3);
without having to manually test typeof == undefined at each step.
With a little change you can have the API look any way you want:
window.deep_set(['server','responses','currentuser','id'],3);
// or
deep_set(window,'server','responses','currentuser','id',3);
// or
deep_set(window,'server.responses.currentuser.id',3);
Related
This is a purely theoretical question here (though one I think is an interesting thought exercise). I was just working on a JavaScript object (documentation-related) and the somewhat-unusual thought crosses my mind: is there a way to make a key/value pair entry within said object capable of reading its own key as part of its value? That is to say:
Assuming I have a JavaScript object used for the purposes of serializing data:
{
"someKey":()=>"M-me? MY key is '" + voodoo(this) + "'! Thanks so much for taking an interest!"
}
...is there a way I can get "M-me? MY key is 'someKey'! Thanks so much for taking an interest!" as an (albeit: rather asinine) output when addressing the key? I totally don't care what the structure would look like, nor what the type of the Value of the portion of the KVP would be, NOR what arguments would need passed it (if any? I'm just assuming it would have to be a function, after all).
I mean, of course it's possible; it's code. It's ALL possible (I've seen a quine that can ascertain its own SHA-512 hash, for heaven sake). But I find it to be an interesting thought experiment, and wanted to see if anyone already had some Code Kung Fu/Source Santeria (even at the abstract/pseudo-code level) and/or someone that might have some ideas.
I've tinkered with going so far as to actually parse the JavaScript source file line-by-line and test for the remainder of the output string to place it (worked, but lame... What if it's a constructed object?), then thought of stringifying it and RegEx-ing it out (worked, but still pretty weak... Relies too much on advance knowledge of what would have to be an unchanging structure).
I'm now fiddling with attempting to filter the object on and by itself to try and isolate the key making the request, which I expect will work (-ish), but still leaves me feeling kind of like the bull in a china shop. I can extend the Object prototype (I know, I know. Theoretical, remember?) so the self-reference doesn't pose a problem, but I'm stumped as to providing a means for the KVP to identify itself uniquely without having to search for some set portion of string.
Anyone have any thoughts? No holds barred: this will probably never see the light of a production environment - just an interesting puzzle - so feel free to muck with prototypes, include libraries, fail to indent... whatever*. Frankly, it doesn't really even have to be in JavaScript; that's just what I'M using. It's 2:30am here, and I'm just noodling on if it's DOABLE.
* (Please don't fail to indent. Twitch-twitch (ಥ∻.⊙) It seems I lied about that part.)
Reflexively lookup the key on call
This is probably the most surefire way to do it. When obj.foo() is called, then foo is executed with obj set as the value of this. This means that we can lookup the key from this. We can examine the object easily the hardest thing is to find which key contains the function we just executed. We can try to do string matching but it might fail for:
const obj = {
foo: function() { /* magic */ },
bar: function() { /* magic */ },
}
Because the contents of the functions will be the same but the keys are different, so it's not easy to differentiate between obj.foo() and obj.bar() by doing string matching.
However, there is a better option - naming the function:
const obj = {
foo: function lookUpMyOwnKey() { /* magic */ }
}
Normally, there is pretty much no effect whether you give the function a name or not. However, the thing that we can leverage is that the function can now refer to itself by the name. This gives us a fairly straightforward solution using Object.entries:
"use strict";
const fn = function lookUpMyOwnName() {
if (typeof this !== "object" || this === null) { //in case the context is removed
return "Sorry, I don't know";
}
const pair = Object.entries(this)
.find(([, value]) => value === lookUpMyOwnName);
if (!pair) {
return "I can't seem to find out";
}
return `My name is: ${pair[0]}`
}
const obj = {
foo: fn
}
console.log(obj.foo());
console.log(obj.foo.call(null));
console.log(obj.foo.call("some string"));
console.log(obj.foo.call({
other: "object"
}));
This is pretty close to the perfect solution. As we can see, it even works if the function is not defined as part of the object but added later. So, it's completely divorced from what object it's part of. The problem is that it's still one function and adding it multiple times will not get the correct result:
"use strict";
const fn = function lookUpMyOwnName() {
if (typeof this !== "object" || this === null) { //in case the context is removed
return "Sorry, I don't know";
}
const pair = Object.entries(this)
.find(([, value]) => value === lookUpMyOwnName);
if (!pair) {
return "I can't seem to find out";
}
return `My name is: ${pair[0]}`
}
const obj = {
foo: fn,
bar: fn
}
console.log(obj.foo()); // foo
console.log(obj.bar()); // foo...oops
Luckily, that's easily solvable by having a higher order function and creating lookUpMyOwnName on the fly. This way different instances are not going to recognise each other:
"use strict";
const makeFn = () => function lookUpMyOwnName() {
// ^^^^^^ ^^^^^
if (typeof this !== "object" || this === null) { //in case the context is removed
return "Sorry, I don't know";
}
const pair = Object.entries(this)
.find(([, value]) => value === lookUpMyOwnName);
if (!pair) {
return "I can't seem to find out";
}
return `My name is: ${pair[0]}`
}
const obj = {
foo: makeFn(),
bar: makeFn()
}
console.log(obj.foo()); // foo
console.log(obj.bar()); // bar
Making really sure we find the key
There are still ways this could fail
If the call comes from the prototype chain
If the property is non-enumerable
Example:
"use strict";
const makeFn = () => function lookUpMyOwnName() {
// ^^^^^^ ^^^^^
if (typeof this !== "object" || this === null) { //in case the context is removed
return "Sorry, I don't know";
}
const pair = Object.entries(this)
.find(([, value]) => value === lookUpMyOwnName);
if (!pair) {
return "I can't seem to find out";
}
return `My name is: ${pair[0]}`
}
const obj = {
foo: makeFn()
}
const obj2 = Object.create(obj);
console.log(obj.foo()); // foo
console.log(obj2.foo()); // unknown
const obj3 = Object.defineProperties({}, {
foo: {
value: makeFn(),
enumerable: true
},
bar: {
value: makeFn(),
enumerable: false
}
})
console.log(obj3.foo()); // foo
console.log(obj3.bar()); // unknown
Is it worth making an overengineered solution that solves a non-existing problem just to find everything here?
Well, I don't know the answer to that. I'll make it anyway - here is a function that thoroughly checks its host object and its prototype chain via Object.getOwnPropertyDescriptors to find where exactly it was called from:
"use strict";
const makeFn = () => function lookUpMyOwnName() {
if (typeof this !== "object" || this === null) {
return "Sorry, I don't know";
}
const pair = Object.entries(Object.getOwnPropertyDescriptors(this))
.find(([propName]) => this[propName] === lookUpMyOwnName);
if (!pair) {//we must go DEEPER!
return lookUpMyOwnName.call(Object.getPrototypeOf(this));
}
return `My name is: ${pair[0]}`;
}
const obj = {
foo: makeFn()
}
const obj2 = Object.create(obj);
console.log(obj.foo()); // foo
console.log(obj2.foo()); // foo
const obj3 = Object.defineProperties({}, {
foo: {
value: makeFn(),
enumerable: true
},
bar: {
value: makeFn(),
enumerable: false
},
baz: {
get: (value => () => value)(makeFn()) //make a getter from an IIFE
}
})
console.log(obj3.foo()); // foo
console.log(obj3.bar()); // bar
console.log(obj3.baz()); // baz
Use a proxy (slight cheating)
This is an alternative. Define a Proxy that intercepts all calls to the object and this can directly tell you what was called. It's a bit of a cheat, as the function doesn't really lookup itself but from the outside it might look like this.
Still probably worth listing, as it has the advantage of being extremely powerful with a low overhead cost. No need to recursively walk the prototype chain and all possible properties to find the one:
"use strict";
//make a symbol to avoid looking up the function by its name in the proxy
//and to serve as the placement for the name
const tellMe = Symbol("Hey, Proxy, tell me my key!");
const fn = function ItrustTheProxyWillTellMe() {
return `My name is: ${ItrustTheProxyWillTellMe[tellMe]}`;
}
fn[tellMe] = true;
const proxyHandler = {
get: function(target, prop) { ///intercept any `get` calls
const val = Reflect.get(...arguments);
//if the target is a function that wants to know its key
if (val && typeof val === "function" && tellMe in val) {
//attach the key as ##tellMe on the function
val[tellMe] = prop;
}
return val;
}
};
//all properties share the same function
const protoObj = Object.defineProperties({}, {
foo: {
value: fn,
enumerable: true
},
bar: {
value: fn,
enumerable: false
},
baz: {
get() { return fn; }
}
});
const derivedObj = Object.create(protoObj);
const obj = new Proxy(derivedObj, proxyHandler);
console.log(obj.foo()); // foo
console.log(obj.bar()); // bar
console.log(obj.baz()); // baz
Take a peek at the call stack
This is sloppy and unreliable but still an option. It will be very dependant on the environment where this code, so I will avoid making an implementation, as it would need to be tied to the StackSnippet sandbox.
However, the crux of the entire thing is to examine the stack trace of where the function is called from. This will have different formatting in different places. The practice is extremely dodgy and brittle but it does reveal more context about a call than what you can normally get. It might be weirdly useful in specific circumstances.
The technique is shown in this article by David Walsh and here is the short of it - we can create an Error object which will automatically collect the stacktrace. Presumably so we can throw it and examine it later. Instead we can just examine it now and continue:
// The magic
console.log(new Error().stack);
/* SAMPLE:
Error
at Object.module.exports.request (/home/vagrant/src/kumascript/lib/kumascript/caching.js:366:17)
at attempt (/home/vagrant/src/kumascript/lib/kumascript/loaders.js:180:24)
at ks_utils.Class.get (/home/vagrant/src/kumascript/lib/kumascript/loaders.js:194:9)
at /home/vagrant/src/kumascript/lib/kumascript/macros.js:282:24
at /home/vagrant/src/kumascript/node_modules/async/lib/async.js:118:13
at Array.forEach (native)
at _each (/home/vagrant/src/kumascript/node_modules/async/lib/async.js:39:24)
at Object.async.each (/home/vagrant/src/kumascript/node_modules/async/lib/async.js:117:9)
at ks_utils.Class.reloadTemplates (/home/vagrant/src/kumascript/lib/kumascript/macros.js:281:19)
at ks_utils.Class.process (/home/vagrant/src/kumascript/lib/kumascript/macros.js:217:15)
*/
The question:
How can a I implement a function in JavaScript which would behave exactly the same as read property,
but will log the values
I.e.
function f(obj, prop) {
console.log(obj, prop, obj[prop]);
return obj[prop];
}
But with respect to preserving this value and other pitfalls.
Question context: I'm developing some instrumetation tool for Javascript.
I'm using Babel to parse the code, than I manipulate AST tree to replace reads from object properties.
For source code
a.b[5].c(1,2,3);
I produce instrumented code like this one:
const LOG_READ = (z)=> {console.log('LOGGED', z); return z;}
LOG_READ(
LOG_READ(
LOG_READ(
a
, "b")
, 5)
, "c")(1,2,3);
I had a problem with:
let x = [1,2,3];
x.map(it => console.log(it));
Instrumented to:
let x = [1,2,3];
LOG_READ(x, "map")(it => console.log(it));
It failed to run when instrumented, because "map" expects "this" to be equal to "x" which isn't.
When I fixed LOG_READ to
const LOG_READ = (obj, prop)=> {console.log('LOGGED', z); return (typeof z === "function" ? obj[prop].bind(obj) : z}
It failed for
class c1 extends LOG_READ(module, "func_name") {...}
Because bind should not be called for this case, so it became:
const LOG_READ = (obj, prop)=> {console.log('LOGGED', obj, prop); return (typeof obj[prop] === "function" && !obj[prop].prototype ? obj[prop].bind(obj) : obj[prop]}
As you might guess it failed again in some else place.
I've also tried making a proxy or an expression instead of function - they also don't work.
I.e. (LOG_WRITE_PROXY.w = a.map)(it => console.log(it));
So here is why I'm asking this question.
Someone linked Proxy in a comment, however a bit later the comment disappeared. I thought there would an answer come, but apparently it has not.
However Proxy seems to be related and interesting, see this test:
var something={
a:1,
b:[2,3,4],
c:{
d:"Hello",
e:[5,6,7]
}
};
var handler = {
get: function(obj, prop) {
console.log(prop.toString()+" requested: "+obj[prop]);
if(prop in obj){
var p=obj[prop];
if(p instanceof Object || p instanceof Array)
return new Proxy(p,handler);
return p;
}
return undefined;
}
};
var proxd=new Proxy(something,handler);
console.log(proxd.a+proxd.b.reduce((a,i)=>a+i)+proxd.c.e[2]);
So it does something with this simple throwaway recursion already, like cutting through the this issue appearing in the comments (reduce just works fine).
I am not sure it is possible but you guys can say it:
var tricky = function(obj){
var ret = new Object();
ret.porp1 = 'undefined';
ret.prop2 = 'undefined';
for (porp in ret){
if (obj.hasOwnProperty(prop) == false){
obj.???????? = prop;
}
return obj;
}
tricky({prop1:'ok_prop'});
===>>{prop1:'ok_prop',prop2:'undefined'}
I did not put the question in words because I find it hard to define properly. My problem is as you can see: I have an object and sometimes I set some properties in that object sometimes do not. I want a function which will check if my object has a range of properties and if not then set it explicitly to undefined. It does not work if I leave it implicitly undefined, since I use it to send it to a jade template which might have a property set to a value but the system has changed state and I want it to be a undefined.
What should I put after the obj.[what I put here] to be set at first to porp1 then prop2.
I am assuming you want to add members of the "template" object ret to obj if they do not exist & that porp is a typo.
You say that if they do not exist you want to set it explicitly to undefined however there is no need as a non-existent member of an object is already undefined:
var x = new Object();
alert (typeof x.wibble === 'undefined') //true
Your ret.porp1 = 'undefined'; is assigning a string object with the textual content 'undefined' to the member, this has nothing to do with the state of being "undefined", if this is your intention:
var tricky = function(obj) {
var ret = {
prop1: 'undefined',
prop2: 'undefined'
}
for (requiredMember in ret) {
if (obj.hasOwnProperty(requiredMember) === false) {
obj[requiredMember] = ret[requiredMember];
}
}
return obj;
}
var o = {prop1:'ok_prop'};
o = tricky(o);
console.dir(o);
Object
prop1: "ok_prop"
prop2: "undefined"
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();
}
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.