So, I'm trying to create an object like:
var a = {
b: "object_id",
b: function(){ return c(this.b); }
}
var c = {
"object_id": {
foo: "bar"
}
}
But it only registers the last value for the key 'b'. But I think I've seen something like this used before and it would really help me if I could call a.b().foo or if I just want ID a.b
Is there any way to make this happen or will I have to rename value and/or method?
You're looking for the Object Getter/Setter notation.
var o = {
_b: 7,
get b() {
return this._b + 1;
},
}
That being the case, you CAN'T use the same object id, and should store the internal variable for b in a closure, if you need it to be totally private.
Related
I once again stumbled over a behavior in Javascript that I don't understand. I need to update properties of an object as soon as a variable outside of the object is changed. The external variable is referenced in the object properties so I thought all I had to do is to change the variable externally and automatically have the property values changed.
Here's a simplified version of what the code looks like:
var serverPath = "123/";
var GetCurrentProductionApiConfig = {
URL: {
GetStart: serverPath + 'GetCurrentProduction?returnValue=start&',
GetEnd: serverPath + 'GetCurrentProduction?returnValue=end&',
Get: serverPath + 'GetCurrentProduction?returnValue=start&'
}
};
serverPath = "456/";
console.log(GetCurrentProductionApiConfig.URL.GetStart);
This will result in:
123/GetCurrentProduction?returnValue=start&
Is it because the variable has been copied (passed by value) rather than having a pointer on it (passed by reference)? And which way would be the correct one to update the properties?
Everything in JavaScript is pass by value however, it happens that the value of an object is its reference. However, the important thing here is that for primitives, will not get changes when a referenced variable changes:
var a = "world";
var obj = {
b: "hello" + a //evaluated once
}
a = "universe"; //does not modify obj.b which references a
console.log(obj.b); //helloworld
In order to have a dynamically evaluated string, you need to call a function or a method:
var a = "world";
var obj = {
b: function() {
return "hello" + a //evaluated every time the function is executed
}
}
console.log(obj.b()); //helloworld
a = "universe"; //will influence obj.b
console.log(obj.b()); //hellouniverse
However, that looks a bit "dirty" since it forces the caller to know to evaluate the property every time. It can also introduce inconsistency if some properties are plain strings, others functions and it's especially annoying if a property has to change from one to the other - you need to modify every place that calls this code to change, say, obj.c to obj.c().
Instead, using ES6+ you can define a getter for a property that will do the same as before but will hide the function call, so any time you read a property you actually evaluate code to return the result:
var a = "world";
var obj = {
c: "plain property"
}
Object.defineProperty(obj, 'b', {
get: function() {
return "hello" + a //evaluated every time the property is read
}
});
console.log(obj.b); //helloworld
a = "universe"; //will influence obj.b
console.log(obj.b); //hellouniverse
console.log(obj.c); //plain property
I have 2 files:
testrequire.js
let a = {};
function foo() {
a = 'test';
};
module.exports.foo = foo;
module.exports.a = a;
test.js
let a = require('./testrequire');
a.foo();
console.log(a);
When I run test.js, this is the result:
{ foo: [Function: foo], a: {} }
But I expect it to be like this:
{ foo: [Fuction: foo], a: 'test' }
However, when I change testrequire.js like this:
let a = {};
function foo() {
a.b = 'test';
};
module.exports.foo = foo;
module.exports.a = a;
The result is:
{ foo: [Function: foo], a: { b: 'test' } }
And it is perfectly like what I expected.
The question here is: Why function foo() can modify a's properties while it cannot modify a?
P/S: I did try var instead of let and the result is still the same. So it is definitely not ES6 let fault.
It's a pointer thing. It's the same in C/C++, Java etc. We've gotten so used to closures that we've sort of expect regular pointers to work the same. But pointers/references are simple indirections.
Let's walk through your code:
let a = {};
Create an object ({}) and point the variable a to that object.
function foo() {
a = 'test';
};
Declare a function foo() that overwrites the value of a with a string. Now, if you remember your C/assembly then you'd remember that the value of a pointer is the address of the thing it points to. So the original value of a is not {} but the address to that object. When you overwrite a with a string that object still exist and can be garbage collected unless something else points to it.
module.exports.foo = foo;
module.exports.a = a;
Export two properties, 1. foo which points to a function and 2. a which points to the same object that a is pointing to. Remember, just like in C/Java this does not mean that module.exports.a points to a but that it points to {}. Now you have two variables pointing to the same object {}.
Now, when you do:
a.foo();
All you're doing is changing the enclosed variable a to point to a string instead of the original object. You haven't done anything to a.a at all. It's still pointing to {}.
Workarounds
There are two ways to get what you want. First, the OO way. Don't create a closure for a, make it a regular object property:
function foo() {
this.a = 'test';
};
module.exports.foo = foo;
module.exports.a = {};
This will work as expected because modules in node.js are proper singletons so they can be treated as regular objects.
The second way to do this to use a getter to get the enclosed a. Remember that closures only work with functions, not objects. So just assigning the variable to a property like you did results in a regular pointer operation not a closure. The workaround is this:
let a = {};
function foo() {
a = 'test';
};
function getA() {
return a; // this works because we've created a closure
}
module.exports.foo = foo;
module.exports.getA = getA;
Now you can do:
a.foo();
a.getA(); // should return 'test'
foo can modify the variable a to point to something else.
But this has no effect on the object exported. When the require is done, the calling module receives whatever a pointed to at the time. After that, it does not care about what happens to (the variable) a.
In your second example, you are not assigning a to a new object, but you are modifying the existing object (by adding a new field). That will of course be visible to anyone who got hold of that object before.
This is (very roughly) analogous to
function callerDoesNotSeeThis(a){ a = 1 }
function callerSeesThis(a){ a.foo = 1 }
let us say i have a object in which there is a function and inside the function i have declared some key values.
if every function is an function object in javascript then it is totally legal to declare key value pair in it.
var a ={
a: "shiv",
b: "shiv1",
c: function(){
L:"shiv1",
console.log(L);
}
}
how can i access these keys. in what scenarios i need to add key value pairs to a function. what does this signify. For Example in this function, how can i access L key.
Edit 1:
Console.log line will through err.
let say we have only this code
var a ={
a: "shiv",
b: "shiv1",
c: function(){
L:"shiv1",
}
}
what does declaring a key in function signify. if i need to access L key how can i
You every function is also an object, meaning that you can indeed set properties to it. In fact every function has length property, which denotes the number of formal arguments this function accepts.
However the syntax you are using to set function properties is not how you should do it. Even though you are using perfectly valid syntax, this is label statement, not property definition syntax. If you wanted to set a property to a function object in your case you would do it like this:
function c() {
console.log(c.L) // => "I'm a property of c function"
}
c.L = "I'm a property of c function";
var a = {
a: "shiv",
b: "shiv1",
c: c
}
When it can be useful? Like I said there is a limited use for it, like length property of the function, but in many cases you would not need setting properties on a function level directly.
This has to be throwing errors:
L:"shiv1",
console.log(l);
You probably want:
c: function(){
return {L:"shiv1"};
}
And then access it as:
a.c().L;
Full example here:
var a ={
a: "shiv",
b: "shiv1",
c: function(){
return {L:"shiv1"}
}
}
alert(a.c().L);
You can't declare keys like you did inside the function.
This doesn't work:
c: function(){
L:"shiv1", //Declaration don't work in this context
console.log(l); //Don't work
}
Try this (Please, remove the "function" statement):
c: {
L:"shiv1"
}
Also, you can do this:
c: function() {
var test = "shiv1";
return test;
}
If you want to work with parameters:
c: function(a, b) {
var test = a + " shiv1 " + b;
return test;
}
Or:
c: function(a, b) {
var total = a + b;
return total;
}
I have the following situation:
var a = {
b: function() {
alert('hi');
},
c: [{m: this.b}]
};
alert(typeof a.c[0].m);
The output is "undefined". What is the reason for this?
Because you are using this keyword inside an object. In that case this.b is referring to something undefined which should be a property of window.
Read this article, it is very useful to understand scopes.
In this case, you should declare your variable like this:
b = 't'; //note there is not keywork var, it is a window global variable
var a = {
c: [{
b: 'a',
m: this.b //is 't'
}],
b: function() {
alert('hi');
}
};
alert(a.c[0].m); //will display 't'
Because (assuming you are executing this in the context of a browser) this is window and you haven't defined window.b.
The value of this is determined by the way you execute the current function, not by an object literal.
MDN has further reading about this.
When the value for a.c[0].m is run, this doesn't refer to a, it refers to the entire scope.
If you want the behaviour you are after, you need to change it to:
var a = { b: function() { alert('hi'); }, c: [{}] };
a.c[0].m = a.b;
var namespaced = {
A: function(){
function r(){
//do some stuff
return something;
}
var someProperty = 5;
function j(){
//do some more stuff
return something;
}
},
B: function(){
//can I call A and C?
A.r();
C.d();
},
C: function(){
function d() {
//do stuff we like
}
}
}
Then I could do...
namespaced.A.j();
namespaced.C.d();
something = namespaced.A.someProperty;
right?
Would I need to do this too?
var something = new namespaced.A()?
If so does A() have a constructor? I'm really confused here :{
I'm trying to encapsulate my javascript so it's easy to maintain
Then I could do...
namespaced.A.j();
namespaced.C.d();
something = namespaced.A.someProperty;
No you couldn't. The function j and someProperty are only local to A and are not propagated to the outside. If you want to access them from the outside, you have to make them a property of the function, using this:
var namespaced = {
A: function(){
this.r = function(){
//do some stuff
return something;
};
this.someProperty = 5;
this.j = function(){
//do some more stuff
return something;
};
}
}
But you would still need to call var a = new namespaced.A() in order to access the functions.
If you want to call namespaced.A.j() directly, you would have to declare A as object, not as function:
var namespaced = {
A: {
r: function(){
//do some stuff
return something;
},
someProperty: 5,
j: function(){
//do some more stuff
return something;
}
}
}
So it depends on what you want to achieve eventually... to get a better insight into these methods, I recommend JavaScript Patterns.
This is what you need to understand about JavaScript:
When you write
var obj = { A: a, B: b, C: c };
you are creating (and assigning to obj) an object with properties called A, B and C mapping to values a, b and c respectively. These values may very well be functions, so when you have
var obj = { A: function(){...} };
you are creating an object with a property called "A" which is a function. You can refer to it with obj.A and call with obj.A().
When you call obj.A(), the keyword this inside the body of function A will refer to obj. You can use it to assign new properties to obj:
var obj = {
A: function() { this.prop = "Hello!"; }
};
obj.A();
alert( obj.prop ); // alerts "Hello!"
So, inside namespaced.A.j() the this keyword will point to namespace.A (it's what is to the left of the last dot).
You can apply a function to an object like so: func.apply(obj) or like so: func.call(obj). In this case, the this keyword will refer to obj instead. This isn't relevant to your case, but if func takes parameters (let's say param1 and param2), you can apply the function like so: func.apply(obj, [val1, val2]) or like so: func.call(obj, val1, val2).
All variables declared inside a function live only inside that function. They are not visible outside. And when you write function doStuff(){} it's (I'm simplifying here) as good as if you wrote var doStuff = function(){}; So nested functions live and can be used only inside the surrounding function; that is, unless you assign them to something accessible from outside.
When you call something like new Cons() what happens is the creation of a new empty object followed by the application of Cons() on that object. In other words, it's the same as
var obj = {};
Cons.apply(obj);
or if you prefer:
var obj = {};
obj.Cons = Cons;
obj.Cons();
// obj's Cons property then mysteriously disappears
// unless it was explicitly set inside Cons() (oh my, how confusing! :)
So you can have this:
function Duck(name){
this.myName = name;
this.quack = function(){
alert(this.myName + " quacks!");
}
};
donald = new Duck('Donald');
donald.quack();
With all the preceding in mind, a way to write namespaced code is like this:
// The following syntax, confusing to someone who hasn't seen it before,
// is defining a new anonymous function and immediately using it
// as a constructor applied to a new empty object.
//
// Alternatively, you can use this syntax:
// var namespaced = {};
// (function(){
// ....
// }).apply(namespaced);
//
var namespaced = new (function(){
// This creates a new variable named "namespaced"
// which is visible only inside this anonymous function.
// This variable points to the still-empty object created by
// 'new'. This object will, once we're done with this anonymous function,
// be assigned to a variable, outside, which by "coincidence" is
// also named "namespaced".
var namespaced = this;
// You could alternatively not create the variable "namespaced"
// and use 'this' directly inside this anonymous function. But,
// the 'this' keyword may point to different objects inside the
// nested functions that follow, so we create it to avoid confusion.
// This assigns a new object to variable 'A', which isn't visible outside.
// Use a constructor function defined inline.
var A = new (function(){
var A = this; // 'this' now refers to the empty object created just above
this.someProperty = 5; // Two different ways of
A.anotherProperty = 7; // doing mostly the same thing
this.j = function(){
//do some more stuff
// 'this' will point to j, here
return something;
}
// Function r isn't visible outside of A's constructor like this!
function r(){
//do some stuff
return something;
}
// Make 'r' visible outside by assigning it to a property of 'A'.
// Look, it's also called "r". What fun!
A.r = r;
})();
// Make the object in variable 'A' visible outside of
// 'namespaced's constructor, by making it a property of 'namespaced'
namespaced.A = A;
// Create a new object as before.
// This time we won't make it visible outside
// of "namespaced"'s constructor.
var C = new (function(){
this.d = function (){
//do stuff we like
}
})();
// Give "namespaced" a property 'B'.
// This time it's a function instead of a nested object.
namespaced.B = function(){
// It's cool to make these function calls here, because
// (a) nested functions can see the variables ('A' & 'C')
// of surrounding functions, even if they terminate in the meantime;
// and (b) 'r' & 'd' are properties of 'A' and 'C'.
A.r();
C.d();
};
// You could return 'this' or 'namespaced' from this constructor,
// but the 'new' keyword will make sure the "namespaced" variable
// outside will get the no-longer-empty object it created,
// so you can just not return anything.
})();
// Now you can do
five = namespaced.A.someProperty;
seven = namespaced.A.anotherProperty;
something = namespaced.A.j();
namespaced.B(); // Calls A.r() and C.d()
// But you can't do
namespaced.C.d(); // WRONG: "namespaced" doesn't have a property named "C"
I hope this helps more than it confuses.