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
Related
var myObj = {
key: "element",
key2: "element2"
}
function stuff(obj) {
var a = obj;
console.log(a);
}
stuff(myObj);
How do I make stuff(myObj) console.log myObj, which is the name of the object? If I were to run this code, it would print that the argument myObj passed into stuff is an object and console.log its keys and elements. I want var a to store and console.log the name of the object instead of its contents, which in this case, is myObj.
You can't. JavaScript passes by value, there is no connection to the variable that was used to pass the object.
Take this example:
var foo = {};
var bar = foo;
window.baz = bar;
stuff(foo);
function stuff(obj) {
var a = obj;
console.log(a);
}
You now have 5 variables / properties which are all "names" for the same object.
If you need an object to have some kind of identifying name, then give it as a property.
var foo = { name: "foo" };
Use for-in loop to iterate through object. a and b are keys of the object which are mapped to id of the elements..
Try this:
var obj = {
a: "A",
b: "B"
};
for (var i in obj) {
$('#' + i).hide();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="a">A</div>
<div id="b">B</div>
<div id="c">C</div>
<div id="d">D</div>
It is possible! You can do something like this:
function stuff(obj) {
var a = Object.keys(window);
a.forEach(function(item, i, a) {
if (window[item] == obj)
{
console.log((item);
}
});
}
There is no (good) way to do that. The myObj variable has a pointer to the object in the memory. When you send myObj as parameter to a function, it will be sent by reference, so the obj parameter of your function will also have the pointer to the object in memory.
At this point the global variable myObj and local variable obj are synonyms for the same object. Modifying the property of one will also affect the other variable.
Theoretically there could be hundreds of variables pointing to the same object, but the object would never have any link back to any of the variables that point to it, so there is no way to retrieve the name of an object. There is no correct or original variable: all variables are identical no matter in what order they were linked to this object.
This said, of course if myObj is a global variable, it would be possible to compare your variable with all properties of the window object and then know which variable it was originally, but that's an awful waste of processing power. And it will only work with global variables.
So this should do the trick for global variable, but I strongly advise against doing this (most of all if it's a big project with lots of global variables). Still, if used during coding for debugging only, which I assume this is all about, it might be of some help:
function getVariableName(x) {
for (var i in window) {
if (window[i] === x) return i;
}
return false;
}
Attention: if you got more than one global variable pointing to this object, there is no way to know which of those variable names you will get back ... javascript does not define a fix order of enumerating properties of an object. The first match will be reported back!
Say I have a class and some static helper methods like this:
function MyClass (myVar) {
this.myVar = myVar;
this.replaceMe = function (value) {
// this will fail
this = MyClass.staticHelper( value );
return this;
}
this.revealVar = function () {
alert( this.myVar );
}
}
MyClass.staticHelper = function (instance, value) {
return new MyClass( instance.myVar + value );
}
What I want to do is something like this:
var instance = new MyClass( 2 );
instance.revealVar(); // alerts 2
instance.replaceMe( 40 ).revealVar(); // alerts 42
The reason is that my class has a slightly more complicated structure and I don't want to assign all internal variables manually everytime, but rather replace the entire object. Is there a simple way to do so?
instance.replaceMe( 40 ).revealVar(); alerts 42
OK, for that return MyClass.staticHelper(this, value); would suffice. The question is only whether the next call to instance.revealVar() should now alert 2 or 42 - if you want instance to be changed to 42 it gets more complicated:
this = MyClass.staticHelper( value ); // this will fail
…because this is not a common variable, but a keyword and evaluates to the value of the ThisBinding of the current execution context which is set depending on how the function is entered - you cannot assign to it, you can only set it when invoking the function.
I don't want to assign all internal variables manually everytime, but rather replace the entire object.
Unfortunately you have to do so, without changing the properties of instance object (and the closure-hidden variables) you won't change the instance and revealVar() will stay 2.
Is there a simple way to do so?
Yes, it can be done programmatically. The simplest method would be to call the constructor (again) on the current instance, like it happens when invoked with the new keyword:
MyClass.call( instance, instance.myVar + value );
Yet you can't use this like the static function which creates a completely new instance. Either you put it in a static method and call that from replaceMe with this, or you just put it directly in replaceMe.
If you need a static method that at first returns a completely new instance, you could use that as well by copying the new properties on the old instance:
….replaceMe = function(val) {
var newInst = MyClass.staticHelper(this, val); // new MyClass(this.myVar+val);
for (var prop in newInst)
if (newInst.hasOwnProperty(prop))
this[prop] = newInst[prop];
return this;
};
That means overwriting the old attributes, and also the old closures can be garbage-collected now as nothing refers to them any more.
Btw, I'd recommend to put your methods on the prototype instead of assigning them in the constructor.
How about just returning the new instance:
function MyClass(myVar) {
// ...
this.replaceMe = function (value) {
return MyClass.staticHelper(this, value);
}
// ...
}
MyClass.staticHelper = function (instance, value) {
return new MyClass( instance.myVar += value );
}
There are two reasons why this is not going to work in Javascript.
First, despite that it looks like a variable, this is actually a function call* and therefore cannot be assigned to. this=foo is the same as bar()=baz. So it's not possible to have code like this:
a = 5
a.change(10)
alert(a == 10) // nope
Second, even if this=z were possible, that approach would fail anyways, because Javascript passes by value, therefore it's not possible to have a function that changes the value of its argument:
a = 5
change(a)
alert(a == 10) // nope
* "is" means "fully identical in every way"
I wanted to do something very similar a while back. Unfortunately there's no way to assign a value to this - the this pointer is a read only variable. However the next best thing is to use a getter and setter object to change the variable holding your instance itself.
Note that this only updates a single reference to the instance. You can read more about it here: Is there a better way to simulate pointers in JavaScript?
So this is how it works:
function MyClass(pointer, myVar) {
this.myVar = myVar;
this.replaceMe = function (value) {
pointer.value = MyClass.staticHelper(this, pointer, value);
return pointer.value;
};
this.revealVar = function () {
alert(this.myVar);
};
}
MyClass.staticHelper = function (instance, pointer, value) {
return new MyClass(pointer, instance.myVar + value);
};
This is how to create the pointer and use it:
var instance = new MyClass({
get value() { return instance; },
set value(newValue) { instance = newValue; }
}, 2);
instance.revealVar(); // alerts 2
instance.replaceMe(40).revealVar(); // alerts 42
It's not the most elegant solution but it gets the job done. You can see this code in action: http://jsfiddle.net/fpxXL/1/
i have an array of objects, and these objects all have an 'isvalid' attribute.
Is there a way with JQuery or plain javascript to bind code to the event that the value of that property changes?
So when i have an array with 10 objects, i want to execute a function when the 'isvalid' property of one of the object changes.
Is that possible?
Michel
It's possible with plain JavaScript by using a property setter function. Using ES5 syntax, that looks like this (live example -- works in Chrome and other browsers with reasonable ES5-compliance):
// Our constructor function
function Thing() {
}
// Define a "foo" property with a setter and getter
Object.defineProperty(Thing.prototype, "foo", {
set: function(value) {
this.fooStorage = value;
display("Foo was set to '" + value + "'");
},
get: function() {
return this.fooStorage;
}
});
// Create a `Thing`
var t = new Thing();
// Set its property
t.foo = "bar";
When the t.foo = "bar"; assignment is executed, the setter function is called. You can have the setter function call a callback if you like, to notify you that the Thing object was changed.
Note that the above is just an example. It uses the fooStorage property to store the value of foo, which is less than ideal but nice and simple for an example.
To do this in a way that's compatible with non-ES5 JavaScript engines, you either have to fall back on some proprietary and now-deprecated syntax from Mozilla (which won't work on other engines), or just use an explicit setter function (live example):
// Our constructor function
function Thing() {
}
// Define a "foo" property with a setter and getter
Thing.prototype.setFoo = function(value) {
this.fooStorage = value;
display("Foo was set to '" + value + "'");
};
Thing.prototype.getFoo = function() {
return this.fooStorage;
};
// Create a `Thing`
var t = new Thing();
// Set the property
t.setFoo("bar");
(And again, this is just an example using a simplistic means of storing foo's value.)
This has the advantage that it works with just about any JavaScript engine, not just ES5-compliant ones, and it's explicit that setting foo is a function call, not just a property assignment (whereas with the ES5 setter/getter syntax, the person setting the foo property doesn't know that it's a function call — which has upsides and downsides).
So that's how you capture the fact that the property changed. Then it's just a matter of allowing callbacks to be registered and removed to receive notification of changes. These are easily managed in a simple array. Here's an ES5-based example doing it on a per-object basis; obviously you could do this in some kind of grouped way instead for the entire array of objects you want to let people watch. (live copy)
window.onload = function() {
// Our constructor function
function Thing() {
this.fooHandlers = [];
}
// Add a listener for "foo" changes
Thing.prototype.addFooChangeHandler = function(callback) {
this.fooHandlers.push(callback);
};
// Remove a listener for "foo" changes
Thing.prototype.removeFooChangeHandler = function(callback) {
var index;
index = this.fooHandlers.indexOf(callback);
if (index >= 0) {
this.fooHandlers.splice(index, 1);
}
};
// Define a "foo" property with a setter and getter
Object.defineProperty(Thing.prototype, "foo", {
set: function(value) {
var index;
for (index = 0; index < this.fooHandlers.length; ++index) {
try {
// Handler receives a reference to this Thing,
// foo's old value, and foo's new value.
this.fooHandlers[index](this, value, this.fooStorage);
}
catch (e) {
}
}
this.fooStorage = value;
},
get: function() {
return this.fooStorage;
}
});
// Create a `Thing`
var t = new Thing();
// Add a foo change handler
t.addFooChangeHandler(function(t, newValue, oldValue) {
display("Handler 1: Foo changed from '" + oldValue + "' to '" + newValue + "'");
});
// Add another
t.addFooChangeHandler(function(t, newValue, oldValue) {
display("Handler 2: Foo changed from '" + oldValue + "' to '" + newValue + "'");
});
// Set the property
t.foo = "bar";
t.foo = "boo";
// === Basic utility functions
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
};
To do that without an ES5 JavaScript engine, just set the setFoo / getFoo model described earlier, and make sure the engine supports Array#indexOf correctly (some engines don't have it at all, some use == rather than === equivalence) or replace the use of Array#indexOf in removeFooChangeHandler with a simple loop through the array looking for the callback:
// Remove a listener for "foo" changes
Thing.prototype.removeFooChangeHandler = function(callback) {
var index;
for (index = 0; index < this.fooHandlers.length; ++index) {
if (this.fooHandlers[index] === callback) {
this.fooHandlers.splice(index, 1);
break;
}
}
};
Side note: There are a number of anonymous functions in these examples. I've done that to avoid making things seem complex, but I'm not a fan of anonymous functions, I prefer that functions have names. See the link for details.
One thing you could do is set the property of the object as private and then change them only with a setter. This way you could trigger an event each time you set the variable with the setter. this would work in any environment.
If you ar limited to the browser you could use watch (give a look here Object.watch() for all browsers? for compatibilty between browsers)
Take a look at this: http://blog.hydroprofessional.com/?p=84
This is get you notified when you property changes
Does anyone have any example implementation of making individual object props readOnly/non-configurable? I mean primitive data types. Have tried using ES5 Object API, but hitting a brick wall.
I can't show code, because it's still at that "messy" phase, but basically I'm iterating through an outside object which, itself, holds numeruos objects. Those objects each hold various primitive data types. I have made the outer objects readOnly, non-config, etc, but can't figure out how to do likewise for individual props, the innermost props.
So, if outer.inner.prop === "Hello", I want to make that value readOnly.
Thanks!
UPDATE
I just figured this out, it was all in the for loop I was using to iterate over props. Now I've actually get data descriptors for the props, even the primitive ones. :) Thanks all!
You have to iterate through the inner object, since there is no way to deep-freeze an object using standard ES5 methods.
function deepFreeze(obj) {
Object.keys(obj).forEach(function (key) {
if (typeof obj[key] == 'object')
deepFreeze(obj[key]);
});
Object.freeze(obj);
}
Edit:
Also works for defineProperty if you don't want to freeze:
function deepWriteProtect(obj) {
Object.keys(obj).forEach(function (key) {
if (typeof obj[key] == 'object')
deepWriteProtect(obj[key]);
Object.defineProperty(obj, key, { writable: false });
});
}
I'm not 100% sure I understand your question correctly, but from what I gather you are asking for private variables. If so, that can be easily achieved using closures.
function myClass(){
var mySecretProperty = 10;
this.getMySecretProperty = function(){
return mySecretProperty;
}
this.changeMySecretProperty = function(s){
// whatever logic you need for a setter method
mySecretProperty = s;
}
}
var myObj = new MyClass();
myObj.changeMySecretProperty(120);
myObj.getMySecretProperty(); // will return 120
myObj.mySecretProperty // will return undefined
Would the following (ES5) example help? It creates an empty constructor, with a getter for property a (and no setter, so de facto a is read only):
var Obj = function(){};
Obj.prototype = {
get a() {return 5;}
}
var x = new Obj;
alert(x.a); //=> 5
x.a = 6; //=> TypeError: setting a property that has only a getter
Not using ES5 you can do
var Obj = function(){
var a = 5;
if (!Obj.prototype.getA) {
Obj.prototype.getA = {
toString: function() {
return a;
}
};
}
}
var y = new Obj;
alert(y.getA); //=> 5
But that is not 100% failsafe: Obj.prototype.getA can be overwritten.
Here is a jsfiddle showing how you can use ES5 getter/setter definitions to make a property of an object something that can only be fetched. The code looks like this:
var object = {
get x() {
return 17;
}, set x() {
alert("You cannot set x!");
}
};
Of course the getter could obtain the value of the property ("x") from anywhere, like a closure from a constructor or something. The point is that the setter simply does not change the value, so attempts to change it:
object.x = 100;
will not have any effect.
I want to create a quick function that will console.log a variable name and the value. I'd like the result of the function to show in the console: foo: bar.
My basic idea for the function looks like this:
function varlog(var_name)
{
console.log(var_name + ": " + eval(var_name));
}
And I'd call is thusly:
function someRandomFunction()
{
var foo = "bar";
// ... some stuff happens
varlog("foo");
}
This works if foo is global, but doesn't work in the example provided. Another option that also only works globally is using window[var_name] instead of the scary eval.
I don't think what I'm asking is possible, but I figured I'd throw it out there.
I'm spending a lot of time attempting to be lazy. My current method is just console.log('foo: ' + bar); which works just fine. But now I just want to know if this is possible.
Some other questions I referenced in searching for this / creating what I have now:
Variable name as a string in Javascript
How to convert variable name to string in JavaScript?
Javascript, refer to a variable using a string containing its name?
How to find JavaScript variable by its name
--
Edit: I'd love to just call varlog(foo), if the name "foo" can be derived from the variable.
Solution - (for your actual use case) - console.log({foo})
In ES6 IdentifierReferences are being accepted as PropertyDefinitions on the ObjectLiteral's PropertyDefinitionList (see compatibility chart):
The variable name is being set to the Object's Property's key
and the variable value is being set to the Object's Property's value.
As console.log shows Objects with their Propertiy/ies' keys and values you can use that to see both your variable's name and value by invoking console.log({foo}).
Note that when you initialize a single anonymous object with several
variables as I did in the second console.log while they appear in
the same order as initialized here in the snippet's output they might
get reordered (alphabetically) elsewhere.
var testint = 3
var teststring = "hi"
var testarr = ["one", 2, (function three(){})]
var testobj = {4:"four", 5:"five", nested:{6:"six",7:"seven"}}
console.log({testint})
console.log({testint, teststring, testarr, testobj})
Answer - (to the question title) - Object.keys({foo})[0]
You can also use this shorthand Object Initializer together with Object.keys() to straightly access the variable name:
var name = "value"
console.log(Object.keys({name})[0])
The reason it doesn't work is because the variable foo is not accessable to the function varlog! foo is declared in someRandomFunction, and is never passed into varlog, so varlog has no idea what the variable foo is! You can solve this problem by passing the variable foo into the function(or using some sort of closure to make foo in the scope of varlog) along with its string representation, but otherwise, I think you are out of luck.
Hope this helps.
While I'm not aware of such a possibility, I'd wanted to share a small idea:
Object.prototype.log = function(with_message) {
console.log(with_message + ":" + this);
}
var x = "string";
x.log("x");
Like I said, a small idea.
Kind of combining a couple of anwers into a small function
Would this work for you?
const log = function() {
const key = Object.keys(this)[0];
const value = this[key];
console.log(`${key}: ${value}`);
}
let someValue = 2;
log.call({someVlaue}); //someValue: 2
Works with function too, even itself.
log.call({log});
// It would return the following
log:function() {
const key = Object.keys(this)[0];
const value = this[key];
console.log(`${key}: ${value}`);
}
I don't believe what you want to do is possible.
The best alternative I can think of is to pass an object to varlog that is basically a key-value hash:
function varlog(obj)
{
for (var varname in obj) {
console.log(varname + ": " + obj[varname]);
}
}
function someRandomFunction()
{
var foo = "bar";
// ... some stuff happens
varlog({foo: foo});
}
I loved #mhitza idea, so I'm making it a little bigger...
The downside is the need to use .valueto reach the variable content.
Object.prototype.log = function(message) {
if (message) console.log(this.name, this.value, message);
else console.log(this.name, this.value);
}
function nar (name, value) {
var o = {name: name, value: value};
this[name] = o;
return o;
}
// var globalVar = 1;
nar('globalVar', 1);
globalVar.log();
// > globalVar 1
globalVar.value += 5;
globalVar.log('equal six');
// > globalVar 6 equal six
var someFunction = function () {
// var localVar = 2;
nar('localVar', 2);
localVar.log('someInfo');
// > localVar 2 someInfo
};
someFunction();
Surprised to see no super simple solution yet.
let varname = "banana"
console.log(`${JSON.stringify({varname}).split('"')[1]}`)
Prints varname in the console