I've done similar things before but I can't get this to work.
I run this and get "Cannot read property '0' or undefined..."
(The randomResult() function returns a random element from a passed array and works ok elsewhere.)
var objectOfArrays = {
array1: ["apple","orange","pear","mango"],
array2: [
"I want no fruit!",
"I will have one " + randomResult(this.array1) + "."
]
}
console.log(randomResult(objectOfArrays.array2));
The object is not defined yet, you can't use this inside an object declaration.
To be clear, something like that:
const obj = {
a: 1,
b: this.a,
c: obj.a
}
Won't work, both b and c cannot access to the object that is currently declared.
However, if you delay the access, then it will works:
const objectOfArrays = {
array1: ["apple","orange","pear","mango"],
get array2() {
return [
"I want no fruit!",
"I will have one " + randomResult(this.array1) + "."
]
}
}
Defining a getter the code will be executed only when you're trying to access to objectOfArrays.array2. Keep in mind two things, however: first, it will be readonly: if you want to set also the prop array2 you have to create a setter too. Second, and more important, in this way, every time you access to array2 the getter is executed, therefore a new array is returned – with a new random result from array1. If you want to return the same, you have to cache the result somewhere:
const objectOfArrays = {
array1: ["apple","orange","pear","mango"],
_array2: null, // this could be actually omitted, see `Symbol` example
get array2() {
return this._array2 || (this._array2 = [
"I want no fruit!",
"I will have one " + randomResult(this.array1) + "."
])
}
}
(You could use Symbol for make the property more private, but it's better not over complicate things now)
Using Symbol
Since it was asked in the comment, even if it's outside the scope of the answer.
Caching the property to _array2 is good enough in most of the cases; however the property is easily accessible directly:
console.log(objectOfArrays._array2) // null
And can be tampered easily:
objectOfArrays._array2 = [1, 2];
Changing the behavior of the getter in array2.
A Symbol could be used to make _array2 more private. Assuming we are in a module:
const _array2 = Symbol("array2"); // the string is just for debug
export default objectOfArrays = {
array1: ["apple","orange","pear","mango"],
get array2() {
return this[_array2] || (this[_array2] = [
"I want no fruit!",
"I will have one " + randomResult(this.array1) + "."
])
}
}
In this way, outside the module there is no a direct way to refer the "private" _array2 property, since the Symbol is needed – and we do not export that.
It won't also appear in iteration (for example, with Object.keys(objectOfArrays)). Of course there is still a way to access to it, using getOwnPropertySymbol, but that means usually a more deliberate code than an accident one – e.g. iterate the keys of an object.
I mentioned module, but this is also applying if we're using a block scope:
// create a block
{
// const is block scoped, therefore is not accessible outside
const _array2 = Symbol("array2");
// `var` is not block scoped, so it will be accessible outside
// also `global.objectOfArrays` would be valid, where `global`
// is a reference to the global object (there is a spec to have
// it by default but is not implemented yet.
var objectOfArrays = ... // same code
}
Or in a closure:
(function(exports) {
const _array2 = Symbol("array2");
exports.objectOfArrays = ... // same code
}(this)); // or `window` or whatever is the export object / global one.
Alternative can be temporary variable:
var temp, objectOfArrays = {
array1: temp = ["apple","orange","pear","mango"],
array2: [
"I want no fruit!",
"I will have one " + temp + "."
]
}
console.log( objectOfArrays );
or separate statements:
var objectOfArrays = { array1: temp = ["apple","orange","pear","mango"] };
objectOfArrays.array2 = [ "I want no fruit!",
"I will have one " + objectOfArrays.array1 + "." ];
console.log( objectOfArrays );
Related
TypeScript function chaining, but I want to programmatically chain them.
Example class: chain.ts
class MyChain {
value: number = 0;
constructor() {
this.value = 0;
}
sum(args: number[]) {
this.value = args.reduce((s, c) => s + c, 0);
return this;
}
add(v: number) {
this.value = this.value + v;
return this;
}
subtract(v: number) {
this.value = this.value - v;
return this;
}
}
const mc = new MyChain();
console.log(mc.sum([1, 2, 3, 4]).subtract(5).value);
I see the number 5 on the console.
Now, I'm still fairly new to JavaScript and TypeScript, so I figured out that the function within this class is actually an element of an array of the instance of the class. Hence, I can do this:
console.log(mc["sum"]([1, 2, 3, 4]).value);
This indeed returns the number 10.
Now, I'm confused as to how I'd chain this programmatically. For example (this is obviously not what I would want to do anyway and shows my boneheaded lack of understanding of JavaScript:
console.log(mc["sum"]([1, 2, 3, 4]).mc["subtract"](5).value);
Error:
Property 'mc' does not exist on type 'MyChain'.ts(2339)
Okay, in all honesty, I kind of intuitively knew that wasn't going to work. However, thinking about it, how would I go about accessing the elements of a multidimensional array in just about any reasonable language?
console.log(mc["sum"]([1, 2, 3, 4])["subtract"](5).value);
Bingo. This does the trick. But, this isn't really the solution I need. What I need is something like this:
interface IChainObject {
action: string;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4] },
{ action: "subtract", operand: 5 },
];
And, to start, I'd like to try this:
console.log(mc[chainObj[0].action](chainObj[0].operand).value);
And consequently, generating a mechanism that would ultimately build something like this:
console.log(
mc[chainObj[0].action](chainObj[0].operand)[chainObj[1].action](
chainObj[1].operand
).value
);
Hence, it seems to me that what I want is some way to generate this:
[chainObj[0].action](chainObj[0].operand)[chainObj[1].action](chainObj[1].operand)
from this, with my chain object having one or many action/operand object sets:
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4] },
{ action: "subtract", operand: 5 },
];
Now, this is where my brain more or less shuts down. I am thinking that I need to generate a chain of string values, but they'll just be strings and won't really work as array indexes into the function as I want.
Why do I want to do this? Ultimately, I want to build a complex Yup schema object from a JSON object. I found this excellent post, but my core issue is I don't really understand how this code works.
At this point, I am able to parse out the way Vijay was able to solve his issue and mimic it, in a way. Here's working code for my example:
const mc = new MyChain();
interface IChainObject {
action: string;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4, 5] },
{ action: "subtract", operand: 5 },
];
let myChain = {};
chainObj.forEach((o) => {
myChain = mc[o.action](o.operand);
});
console.log("myChain is", myChain["value"]);
Results in: myChain is 10
You're probably asking yourself, "What's your problem Dan?. You seem to have a solution in hand now." Yes, I guess I do, but I don't understand it. I'm basically copying and pasting code, marginally understanding it, and making changes that make it work.
My basic issue is I don't understand how this line of code works: myChain = mc[o.action](o.operand);
I get the general gist that it's calling the function based on the action and providing the data to the function via the operand. I'm a copy and paste code monkey. I want to be more than a monkey. Maybe a baboon or even ape. Hence, I want to understand what I've done. What doesn't make sense to me is how it's chaining it.
I thought maybe the secret was in the forEach function, but that doesn't seem to be it. Here is a simple test:
let p = 0;
const x = [1, 2, 3, 4];
x.forEach((y) => {
p = y;
});
console.log("p is", p); p is 4
What is the secret JavaScript magic that is happening under the hood that makes the myChain = mc[o.action](o.operand); code actually chain my functions together rather than simply work one and the work the other. I'm just not seeing it.
Let's start from the first misunderstanding I can find:
Now, I'm still fairly new to JavaScript and TypeScript, so I figured out that the function within this class is actually an element of an array of the instance of the class.
This is not the case. Square brackets in Javascript are used for all property lookups, not just array indexing. x.foo is actually equivalent to x["foo"], and the same syntax works for arrays since arrays are just objects. Classes in Javascript are just objects that have a prototype property, which is itself an object. It contains a list of default attributes, and if you instantiate a class and look up a property that isn't in the object, it'll search for it in the prototype. So, looking at the code:
mc["sum"]([1, 2, 3])
It searches for a "sum" property in mc, and can't find any since you haven't defined one, so it searches in the prototype of MyChain, and finds the mc method. Thus, mc["sum"] is the sum method of mc. Now, this code:
console.log(mc["sum"]([1, 2, 3, 4]).mc["subtract"](5).value);
doesn't work, and it looks very off for a reason. mc["sum"]([1, 2, 3, 4]) returns mc, so why would you have to access the mc property (not that the mc property even exists)? That's why your second example, the one that calls subtract directly, works:
console.log(mc["sum"]([1, 2, 3, 4])["subtract"](5).value);
Now, let's look at the working code:
const mc = new MyChain();
interface IChainObject {
action: string;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4, 5] },
{ action: "subtract", operand: 5 },
];
let myChain = {};
chainObj.forEach((o) => {
myChain = mc[o.action](o.operand);
});
console.log("myChain is", myChain["value"]);
You actually don't need a lot of this code. It can be simplified down to:
const mc = new MyChain();
interface IChainObject {
action: keyof MyChain;
operand: number | number[];
}
const chainObj: IChainObject[] = [
{ action: "sum", operand: [1, 2, 3, 4, 5] },
{ action: "subtract", operand: 5 },
];
chainObj.forEach((o) => {
// bypass typescript type checking with cast
(mc[o.action] as Function)(o.operand);
});
console.log("myChain is", mc.value);
Essentially, the forEach loops through the elements in chainObj in order. The element's value is stored in the variable o. mc[o.action] takes the method name stored in o.action, and accesses it using square brackets. This is basically looking up the method. Then, the method is called with (o.operand) (in Javascript functions are just values, and you can call any value like a function, but if it's not a function it'll error). mc then modifies itself, and you move on to the next loop. If we insert a debugger statement in the function then break on the first loop, we can inspect the variables:
As you can see, the value starts off at 0, o.action is "sum", and mc[o.action] is the sum method. We can then call the sum method with o.operand, which adds the elements up and sets the value to 15. Then, in the second loop:
mc[o.action] is the subtract method, and we call it with o.operand, which is 5, lowering the value to 10.
Most things in Javascript, like classes are basically just objects.1
What that means is that attributes, or in this case - functions, can be accessed via the dot notation or bracket notation.
Lets look at an example that might assist the explanation:
class MyClass {
myFunction(x) {
console.log(x);
}
}
const x = new MyClass();
// attribute accessed via the dot notation
x.myFunction("Hello World!");
// attribute accessed via the bracket notation and a string
x['myFunction']("Hello World, again!");
// attribute accessed via a variable that is a string
const functionName = 'myFunction';
x[functionName]("Well uh, Hello World again?");
// attribute accessed via a variable that is a string, and passing in an argument
const argument = "This is " + "an argument";
x[functionName](argument);
To illustrate the point further:
class MyClass {
myFunction(x) {
console.log(x);
}
}
const x = new MyClass();
console.log(x.myFunction) // returns a function
console.log(x["myFunction"]) // returns a function
// executing the function
x.myFunction("Method One");
x["myFunction"]("Method Two")
We can see that the returned function can be called.
So let's get back to your example
chainObj.forEach((o) => {
myChain = mc[o.action](o.operand);
});
o.action is the function name
o.operand is the argument
Therefore, what is roughly translates to is:
chainObj.forEach((o) => {
myChain = mc[functionName](arugment);
});
just like our previous examples.
1 "classes are basically just objects"
There are so many pieces of this; I'm just going to focus on "what's the secret that makes the forEach() code work?"
The "secret" is that instances of MyChain have a property named value that gets updated after each method is called. The code with forEach() is not really chaining calls together; it just operates on the original MyChain variable named mc each time.
Since all the methods of MyChain that update this.value also return this, it happens not to matter whether you really chain calls (operate on the return value of each method call):
const chaining = new MyChain();
console.log(chaining.add(3).subtract(1).value); // 2
or if you just call methods on the original object in succession:
const notChaining = new MyChain();
notChaining.add(3);
notChaining.subtract(1);
console.log(notChaining.value) // 2
If you want there to be a difference between those, you can show it by making two versions of MyChain; one that only works via chaining, and one that only works in succession.
The following requires chaining because it never updates the original object and method calls return new objects with the results of the method call:
class RealChain {
constructor(public value: number = 0) { }
sum(args: number[]) {
return new RealChain(args.reduce((s, c) => s + c, 0));
}
add(v: number) {
return new RealChain(this.value + v);
}
subtract(v: number) {
return new RealChain(this.value - v);
}
}
const realChaining = new RealChain();
console.log(realChaining.add(3).subtract(1).value); // 2
const notRealChaining = new RealChain();
notRealChaining.add(3);
notRealChaining.subtract(1);
console.log(notRealChaining.value) // 0
and the following prohibits chaining, because it only updates the original object and its methods don't return anything:
class NotChain {
value: number = 0;
constructor() {
this.value = 0;
}
sum(args: number[]) {
this.value = args.reduce((s, c) => s + c, 0);
}
add(v: number) {
this.value = this.value + v;
}
subtract(v: number) {
this.value = this.value - v;
}
}
const realNotChaining = new NotChain();
realNotChaining.add(3);
realNotChaining.subtract(1);
console.log(realNotChaining.value) // 2
const badNotChaining = new NotChain();
console.log(badNotChaining.add(3).subtract(1).value); // error!
// badNotChaining.add(3) is undefined so you can't call subtract() on it
The code with forEach() would only work with NotChain instances and not with RealChain instances.
If you want a programmatic loop-like thing that actually works with chaining and not calling methods on an original object, you should probably use reduce() instead of forEach():
const realChainReduced = chainObj.reduce(
(mc, o) => mc[o.action](o.operand),
new RealChain() // or MyChain, doesn't matter
);
console.log("realChainReduced is", realChainReduced.value); // 10
Note that I didn't cover any of the other parts, including TypeScript specifics (the typings used here give some compiler errors), so be warned.
Playground link to code
I'm sure there is a simple way to do this, but am stumped for now.
I have many variables defined at the top of my script (here is an example of two):
var firstVar,secondVar;
Then I have an object which contains those variables:
var myObj = { a: {name:firstVar, number:1}, b: {name:secondVar, number:2}
I want to assign values to those variables:
keys = Object.keys(myObj);
function getAll(e){
var myArray = [];
for (var prop in myObj){
myArray.push(myObj.prop[e]);
}
return myArray;
}
The behaviour I want is:
var nameVars = getAll(name);
// [firstVar,secondVar]
But instead it returns:
// [undefined,undefined]
How else can I get the variables before defining them?
Then I have an object which contains those variables:
No, it doesn't. It contains a copy of the value those variables contained as of when you created the object (which is undefined, since you've never assigned a value to them). Once created, there is no ongoing link between the object property you've copied the value to and the variable.
Since the object has no enduring link to the variables, there's no way for getAll to return the information you've said you want.
You've said in a comment that you're building d3 graphs and have the same structure with some variables, and want to avoid repeating yourself. It sounds to me like you want a builder function:
function buildObject(firstVar, secondVar) {
return { a: {name:firstVar, number:1}, b: {name:secondVar, number:2} };
}
...which you would then use like this:
var obj1 = buildObject("value1", "value2");
// do a graph
var obj2 = buildObject("valueA", "valueB");
// do a graph
...or possibly even something that just takes the variables and produces the graph:
function makeGraph(firstVar, secondVar) {
buildTheGraph({ a: {name:firstVar, number:1}, b: {name:secondVar, number:2} });
}
I don't think it is, but if it's the names you want, just put them in quotes (and also myArray.push(myObj.prop[e]); should be myArray.push(myObj[prop][e]); and getAll(name) should be getAll("name")), but again there's no link to the variables at all:
// Since they're not used, we don't even need these: var firstVar, secondVar;
var myObj = { a: { name: "firstVar", number: 1 }, b: { name: "secondVar", number: 2 } };
function getAll(e) {
var myArray = [];
for (var prop in myObj) {
myArray.push(myObj[prop][e]);
}
return myArray;
}
var nameVars = getAll("name");
console.log(nameVars);
...but note that having the names doesn't help you get the variable values later (unless you use eval, which you should seek to avoid).
This question already has answers here:
Is JavaScript a pass-by-reference or pass-by-value language?
(33 answers)
Closed 3 years ago.
Does JavaScript pass by references or pass by values?
Here is an example from JavaScript: The Good Parts. I am very confused about the my parameter for the rectangle function. It is actually undefined, and redefined inside the function. There are no original reference. If I remove it from the function parameter, the inside area function is not able to access it.
Is it a closure? But no function is returned.
var shape = function (config) {
var that = {};
that.name = config.name || "";
that.area = function () {
return 0;
};
return that;
};
var rectangle = function (config, my) {
my = my || {};
my.l = config.length || 1;
my.w = config.width || 1;
var that = shape(config);
that.area = function () {
return my.l * my.w;
};
return that;
};
myShape = shape({
name: "Unhnown"
});
myRec = rectangle({
name: "Rectangle",
length: 4,
width: 6
});
console.log(myShape.name + " area is " + myShape.area() + " " + myRec.name + " area is " + myRec.area());
Primitives are passed by value, and Objects are passed by "copy of a reference".
Specifically, when you pass an object (or array) you are (invisibly) passing a reference to that object, and it is possible to modify the contents of that object, but if you attempt to overwrite the reference it will not affect the copy of the reference held by the caller - i.e. the reference itself is passed by value:
function replace(ref) {
ref = {}; // this code does _not_ affect the object passed
}
function update(ref) {
ref.key = 'newvalue'; // this code _does_ affect the _contents_ of the object
}
var a = { key: 'value' };
replace(a); // a still has its original value - it's unmodfied
update(a); // the _contents_ of 'a' are changed
Think of it like this:
Whenever you create an object in ECMAscript, this object is formed in a mystique ECMAscript universal place where no man will ever be able to get. All you get back is a reference to that object in this mystique place.
var obj = { };
Even obj is only a reference to the object (which is located in that special wonderful place) and hence, you can only pass this reference around. Effectively, any piece of code which accesses obj will modify the object which is far, far away.
My two cents.... It's irrelevant whether JavaScript passes parameters by reference or value. What really matters is assignment vs. mutation.
I wrote a longer, more detailed explanation in this link.
When you pass anything (whether that be an object or a primitive), all JavaScript does is assign a new variable while inside the function... just like using the equal sign (=).
How that parameter behaves inside the function is exactly the same as it would behave if you just assigned a new variable using the equal sign... Take these simple examples.
var myString = 'Test string 1';
// Assignment - A link to the same place as myString
var sameString = myString;
// If I change sameString, it will not modify myString,
// it just re-assigns it to a whole new string
sameString = 'New string';
console.log(myString); // Logs 'Test string 1';
console.log(sameString); // Logs 'New string';
If I were to pass myString as a parameter to a function, it behaves as if I simply assigned it to a new variable. Now, let's do the same thing, but with a function instead of a simple assignment
function myFunc(sameString) {
// Reassignment... Again, it will not modify myString
sameString = 'New string';
}
var myString = 'Test string 1';
// This behaves the same as if we said sameString = myString
myFunc(myString);
console.log(myString); // Again, logs 'Test string 1';
The only reason that you can modify objects when you pass them to a function is because you are not reassigning... Instead, objects can be changed or mutated.... Again, it works the same way.
var myObject = { name: 'Joe'; }
// Assignment - We simply link to the same object
var sameObject = myObject;
// This time, we can mutate it. So a change to myObject affects sameObject and visa versa
myObject.name = 'Jack';
console.log(sameObject.name); // Logs 'Jack'
sameObject.name = 'Jill';
console.log(myObject.name); // Logs 'Jill'
// If we re-assign it, the link is lost
sameObject = { name: 'Howard' };
console.log(myObject.name); // Logs 'Jill'
If I were to pass myObject as a parameter to a function, it behaves as if I simply assigned it to a new variable. Again, the same thing with the exact same behavior but with a function.
function myFunc(sameObject) {
// We mutate the object, so the myObject gets the change too... just like before.
sameObject.name = 'Jill';
// But, if we re-assign it, the link is lost
sameObject = {
name: 'Howard'
};
}
var myObject = {
name: 'Joe'
};
// This behaves the same as if we said sameObject = myObject;
myFunc(myObject);
console.log(myObject.name); // Logs 'Jill'
Every time you pass a variable to a function, you are "assigning" to whatever the name of the parameter is, just like if you used the equal = sign.
Always remember that the equals sign = means assignment.
And passing a parameter to a function also means assignment.
They are the same and the two variables are connected in exactly the same way.
The only time that modifying a variable affects a different variable is when the underlying object is mutated.
There is no point in making a distinction between objects and primitives, because it works the same exact way as if you didn't have a function and just used the equal sign to assign to a new variable.
Function arguments are passed either by-value or by-sharing, but never ever by reference in JavaScript!
Call-by-Value
Primitive types are passed by-value:
var num = 123, str = "foo";
function f(num, str) {
num += 1;
str += "bar";
console.log("inside of f:", num, str);
}
f(num, str);
console.log("outside of f:", num, str);
Reassignments inside a function scope are not visible in the surrounding scope.
This also applies to Strings, which are a composite data type and yet immutable:
var str = "foo";
function f(str) {
str[0] = "b"; // doesn't work, because strings are immutable
console.log("inside of f:", str);
}
f(str);
console.log("outside of f:", str);
Call-by-Sharing
Objects, that is to say all types that are not primitives, are passed by-sharing. A variable that holds a reference to an object actually holds merely a copy of this reference. If JavaScript would pursue a call-by-reference evaluation strategy, the variable would hold the original reference. This is the crucial difference between by-sharing and by-reference.
What are the practical consequences of this distinction?
var o = {x: "foo"}, p = {y: 123};
function f(o, p) {
o.x = "bar"; // Mutation
p = {x: 456}; // Reassignment
console.log("o inside of f:", o);
console.log("p inside of f:", p);
}
f(o, p);
console.log("o outside of f:", o);
console.log("p outside of f:", p);
Mutating means to modify certain properties of an existing Object. The reference copy that a variable is bound to and that refers to this object remains the same. Mutations are thus visible in the caller's scope.
Reassigning means to replace the reference copy bound to a variable. Since it is only a copy, other variables holding a copy of the same reference remain unaffected. Reassignments are thus not visible in the caller's scope like they would be with a call-by-reference evaluation strategy.
Further information on evaluation strategies in ECMAScript.
As with C, ultimately, everything is passed by value. Unlike C, you can't actually back up and pass the location of a variable, because it doesn't have pointers, just references.
And the references it has are all to objects, not variables. There are several ways of achieving the same result, but they have to be done by hand, not just adding a keyword at either the call or declaration site.
JavaScript is pass by value.
For primitives, primitive's value is passed. For Objects, Object's reference "value" is passed.
Example with Object:
var f1 = function(inputObject){
inputObject.a = 2;
}
var f2 = function(){
var inputObject = {"a": 1};
f1(inputObject);
console.log(inputObject.a);
}
Calling f2 results in printing out "a" value as 2 instead of 1, as the reference is passed and the "a" value in reference is updated.
Example with primitive:
var f1 = function(a){
a = 2;
}
var f2 = function(){
var a = 1;
f1(a);
console.log(a);
}
Calling f2 results in printing out "a" value as 1.
In the interest of creating a simple example that uses const...
const myRef = { foo: 'bar' };
const myVal = true;
function passes(r, v) {
r.foo = 'baz';
v = false;
}
passes(myRef, myVal);
console.log(myRef, myVal); // Object {foo: "baz"} true
In practical terms, Alnitak is correct and makes it easy to understand, but ultimately in JavaScript, everything is passed by value.
What is the "value" of an object? It is the object reference.
When you pass in an object, you get a copy of this value (hence the 'copy of a reference' that Alnitak described). If you change this value, you do not change the original object; you are changing your copy of that reference.
"Global" JavaScript variables are members of the window object. You could access the reference as a member of the window object.
var v = "initialized";
function byref(ref) {
window[ref] = "changed by ref";
}
byref((function(){for(r in window){if(window[r]===v){return(r);}}})());
// It could also be called like... byref('v');
console.log(v); // outputs changed by ref
Note, the above example will not work for variables declared within a function.
Without purisms, I think that the best way to emulate scalar argument by reference in JavaScript is using object, like previous an answer tells.
However, I do a little bit different:
I've made the object assignment inside function call, so one can see the reference parameters near the function call. It increases the source readability.
In function declaration, I put the properties like a comment, for the very same reason: readability.
var r;
funcWithRefScalars(r = {amount:200, message:null} );
console.log(r.amount + " - " + r.message);
function funcWithRefScalars(o) { // o(amount, message)
o.amount *= 1.2;
o.message = "20% increase";
}
In the above example, null indicates clearly an output reference parameter.
The exit:
240 - 20% Increase
On the client-side, console.log should be replaced by alert.
★ ★ ★
Another method that can be even more readable:
var amount, message;
funcWithRefScalars(amount = [200], message = [null] );
console.log(amount[0] + " - " + message[0]);
function funcWithRefScalars(amount, message) { // o(amount, message)
amount[0] *= 1.2;
message[0] = "20% increase";
}
Here you don't even need to create new dummy names, like r above.
I can't see pass-by-reference in the examples where people try to demonstrate such. I only see pass-by-value.
In the case of variables that hold a reference to an object, the reference is the value of those variables, and therefore the reference is passed, which is then pass-by-value.
In a statement like this,
var a = {
b: "foo",
c: "bar"
};
the value of the 'a' is not the Object, but the (so far only) reference to it. In other words, the object is not in the variable a - a reference to it is. I think this is something that seems difficult for programmers who are mainly only familiar with JavaScript. But it is easy for people who know also e.g. Java, C#, and C.
Objects are always pass by reference and primitives by value. Just keep that parameter at the same address for objects.
Here's some code to illustrate what I mean (try it in a JavaScript sandbox such as https://js.do/).
Unfortunately you can't only retain the address of the parameter; you retain all the original member values as well.
a = { key: 'bevmo' };
testRetain(a);
document.write(' after function ');
document.write(a.key);
function testRetain (b)
{
document.write(' arg0 is ');
document.write(arguments[0].key);
b.key = 'passed by reference';
var retain = b; // Retaining the original address of the parameter
// Address of left set to address of right, changes address of parameter
b = {key: 'vons'}; // Right is a new object with a new address
document.write(' arg0 is ');
document.write(arguments[0].key);
// Now retrieve the original address of the parameter for pass by reference
b = retain;
document.write(' arg0 is ');
document.write(arguments[0].key);
}
Result:
arg0 is bevmo arg0 is vons arg0 is passed by reference after function passed by reference
Primitives are passed by value. But in case you only need to read the value of a primitve (and value is not known at the time when function is called) you can pass function which retrieves the value at the moment you need it.
function test(value) {
console.log('retrieve value');
console.log(value());
}
// call the function like this
var value = 1;
test(() => value);
The changes array of an Object.observe() callback contains objects with the following four properties:
name
object
type
oldValue
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe#Parameters
Why isn't there a path provided natively? Example:
var ob = {
foo: [
{moo: "bar", val: 5},
{val: 8}
]
}
ob.foo[0].val = 1;
// callback should provide path "foo.0.val" or "foo[0].val"
There's a Node.js module that extends Object.observe() to also include the path: observed.js,
but I worry the performance gain of a native observe() will be lost (if no, could you please explain how it is implemented then?). It might be possible to browserify the module, but can't imagine it will perform well in a synchronous environment and I still wonder why nobody seems to have thought about an additional path property.
Because there is no clear path.
Consider the following:
var movall = {moo: "bar", val: 5};
var ob1 = {a: mooval};
var ob2 = {b: movall};
Now let's say I observe movall. Then I update moo. What is the path? Is it movall.moo, or ob1.a.moo, or ob2.b.moo? If I observe ob1, there is no change reported, since there is no change to any of its properties (the change was internal to one of its properties, which doesn't count).
Objects are independent of their existence nested within other objects. They can be nested within multiple other objects. There is no unique "path" that describes how to get from potentially multiple starting points down to a specific property which may have changed.
Nor does JS know the path by which you reached the property being changed. So in ob.foo[0].val = 1;, JS simply evaluates the chain, arrives at the foo[0] object, changes its val property, and at that point has no idea how it happened to arrive at foo[0]. All it knows is that foo[0] has changed. It changed within ob, but it might also have changed within some other object that happens to have foo[0] as a property.
However, you can possibly achieve what you seem to be trying to by building some machinery on top of the low-level observe/notify mechanism. We shall define a function on an object which sets up observers on its property objects, and so on recursively, and propagates change records back up with properly constructed paths:
function notifySubobjectChanges(object) {
var notifier = Object.getNotifier(object); // get notifier for this object
for (var k in object) { // loop over its properties
var prop = object[k]; // get property value
if (!prop || typeof prop !== 'object') break; // skip over non-objects
Object.observe(prop, function(changes) { // observe the property value
changes.forEach(function(change) { // and for each change
notifier.notify({ // notify parent object
object: change.object, // with a modified changerec
name: change.name, // which is basically the same
type: change.type,
oldValue: change.oldValue,
path: k +
(change.path ? '.' + change.path : '') // but has an addt'l path property
});
});
});
notifySubobjectChanges(prop); // repeat for sub-subproperties
}
}
(Note: the change object is frozen and we cannot add anything to it, so we have to copy it.)
Now
a = { a: { b: {c: 1 } } }; // nested objects
notifySubobjectChanges(a); // set up recursive observers
Object.observe(a, console.log.bind(console)); // log changes to console
a.a.b.c = 99;
>> 0: Object
name: "c"
object: Object
oldValue: 1
path: "a.b" // <=== here is your path!
type: "update"
The above code is not production-quality, use at your own risk.
I've got a feeling this might not be possible, but I would like to determine the original variable name of a variable which has been passed to a function in javascript. I don't know how to explain it any better than that, so see if this example makes sense.
function getVariableName(unknownVariable){
return unknownVariable.originalName;
}
getVariableName(foo); //returns string "foo";
getVariableName(bar); //returns string "bar";
This is for a jquery plugin i'm working on, and i would like to be able to display the name of the variable which is passed to a "debug" function.
You're right, this is very much impossible in any sane way, since only the value gets passed into the function.
This is now somehow possible thanks to ES6:
function getVariableName(unknownVariableInAHash){
return Object.keys(unknownVariableInAHash)[0]
}
const foo = 42
const bar = 'baz'
console.log(getVariableName({foo})) //returns string "foo"
console.log(getVariableName({bar})) //returns string "bar"
The only (small) catch is that you have to wrap your unknown variable between {}, which is no big deal.
As you want debugging (show name of var and value of var),
I've been looking for it too, and just want to share my finding.
It is not by retrieving the name of the var from the var but the other way around : retrieve the value of the var from the name (as string) of the var.
It is possible to do it without eval, and with very simple code, at the condition you pass your var into the function with quotes around it, and you declare the variable globally :
foo = 'bar';
debug('foo');
function debug(Variable) {
var Value = this[Variable]; // in that occurrence, it is equivalent to
// this['foo'] which is the syntax to call the global variable foo
console.log(Variable + " is " + Value); // print "foo is bar"
}
Well, all the global variables are properties of global object (this or window), aren't they?
So when I wanted to find out the name of my variables, I made following function:
var getName = function(variable) {
for (var prop in window) {
if (variable === window[prop]) {
return prop;
}
}
}
var helloWorld = "Hello World!";
console.log(getName(helloWorld)); // "helloWorld"
Sometimes doesn't work, for example, if 2 strings are created without new operator and have the same value.
Global w/string method
Here is a technique that you can use to keep the name and the value of the variable.
// Set up a global variable called g
var g = {};
// All other variables should be defined as properties of this global object
g.foo = 'hello';
g.bar = 'world';
// Setup function
function doStuff(str) {
if (str in g) {
var name = str;
var value = g[str];
// Do stuff with the variable name and the variable value here
// For this example, simply print to console
console.log(name, value);
} else {
console.error('Oh snap! That variable does not exist!');
}
}
// Call the function
doStuff('foo'); // log: foo hello
doStuff('bar'); // log: bar world
doStuff('fakeVariable'); // error: Oh snap! That variable does not exist!
This is effectively creating a dictionary that maps variable names to their value. This probably won't work for your existing code without refactoring every variable. But using this style, you can achieve a solution for this type of problem.
ES6 object method
In ES6/ES2015, you are able to initialize an object with name and value which can almost achieve what you are trying to do.
function getVariableName(unknownVariable) {
return Object.keys(unknownVariable)[0];
}
var foo = 'hello';
var output = getVariableName({ foo }); // Note the curly brackets
console.log(output);
This works because you created a new object with key foo and value the same as the variable foo, in this case hello. Then our helper method gets the first key as a string.
Credit goes to this tweet.
Converting a set of unique variable into one JSON object for which I wrote this function
function makeJSON(){ //Pass the variable names as string parameters [not by reference]
ret={};
for(i=0; i<arguments.length; i++){
eval("ret."+arguments[i]+"="+arguments[i]);
}
return ret;
}
Example:
a=b=c=3;
console.log(makeJSON('a','b','c'));
Perhaps this is the reason for this query
I think you can use
getVariableName({foo});
Use a 2D reference array with .filter()
Note: I now feel that #Offermo's answer above is the best one to use. Leaving up my answer for reference, though I mostly wouldn't recommend using it.
Here is what I came up with independently, which requires explicit declaration of variable names and only works with unique values. (But will work if those two conditions are met.)
// Initialize some variables
let var1 = "stick"
let var2 = "goo"
let var3 = "hello"
let var4 = "asdf"
// Create a 2D array of variable names
const varNames = [
[var1, "var1"],
[var2, "var2"],
[var3, "var3"]
]
// Return either name of variable or `undefined` if no match
const getName = v => varNames.filter(name => name[0] === v).length
? varNames.filter(name => name[0] === v)[0][1]
: undefined
// Use `getName` with OP's original function
function getVariableName(unknownVariable){
return getName(unknownVariable)
}
This is my take for logging the name of an input and its value at the same time:
function logVariableAndName(unknownVariable) {
const variableName = Object.keys(unknownVariable)[0];
const value = unknownVariable[variableName];
console.log(variableName);
console.log(value);
}
Then you can use it like logVariableAndName({ someVariable })