Calling a variable to reference an array - javascript

I am trying to call a string variable to reference an array variable.
message1[0][0] = "Hello."; // existing array
var caller = ['message1', 'message2', 'message3'];
alert(message1[0][0]);
But instead of using the message1 array in the alert, I want to use caller[0] (which is equal to "message1") so that it displays "Hello". How do you do this? This doesn't seem to work:
alert(caller[0][0][0]);

The best way is to put message1 on an object, then use [] notation to index into the object:
var obj = {
message1: [
["Hello.", "two", "three"]
]
};
var caller = ['message1', 'message2', 'message3'];
alert(obj[caller[0]][0][0]);
If message1 is a global variable, it's already on an object — the global object, which you can access as window on browsers. So if it's a global, you could use:
alert(window[caller[0]][0][0]);
But global variables are a Bad Idea(tm), so best to use your own object instead.
Full disclosure: You can also use eval to do it, but it's a big hammer for a task this small. Just for completeness:
alert(eval(caller[0] + "[0][0]"));
I don't recommend it, but provided you're fully in control of the text you pass into it, it's workable. Much better to use an object, though.

Related

How to variable shadow Javascript Array?

I am running a self invoked function, and I would like to change the Array object in it, and only in its scope.
var foo = "Foo Outer";
(function(){
var foo = 'Foo Inner !';
console.log('INNER>',foo);
// prints INNER> Foo Inner !
var Array = {};
var Array = Object.getPrototypeOf([]).constructor;
Array.prototype.filter = ()=>{return "Array filter inner"}
console.log('INNER> array filter literal:', [1,2,3].filter(x=>x));
// prints INNER> array filter literal:Array filter inner
})()
console.log('OUTER> foo ', foo);
// prints OUTER> foo Foo outer
console.log('OUTER> Array filter', [1,2,3].filter(x=>x));
// prints OUTER> Array filter Array filter inner
// I want ->
// OUTER> Array Filter [1,2,3]
How can I variable shadow Array and its methods only inside the scope of the self invoked function, while keeping it the same for the rest of the script ?
So in javascript, Arrays are passed around by reference. That means that you're always referencing the same "source" thing. If you mutate the array in one place, those changes will be reflected on all the references to that array.
That means that if you want changes to one version of shadow Array to be different than another version, you need to make a copy of it and change that copy.
It also looks like what you're really trying to accomplish here is a local monkey patch of the native array filter method. I believe that the technique you're looking for here is a proxy. Using a proxy, you can intercept the call to the native filter() method and substitute in your own. Proxies were introduced in ECMAScript2015.
see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
This previous answer might be most relevant to you Extending Array / Proxy in ES6 correctly?
Apparently the answer (as was written in the comment by #Bergi) is that it isn't possible to variable shadow Array)

Accessing object in array using string

I have this code:
var dictionary=[
apple={name:"apple",pos:"noun",d:"",c:["fruit"]},
banana={name:"banana",pos:"noun",d:"",c:["fruit"]}
];
How could I access one of the objects in the array by using a string of its name. In the way that you could access an object as
object['propertyName']
is there a way to do something similar with an array? I want to access it in a way like
dictionary["apple"].pos
//Want to return "noun"
Is there a simple way to do something like that with an array, and if not is there an alternative method that I could use?
The way you're generating your dictionary s wrong; it's syntactically valid, but it's almost certainly not what you intended to do. It's not binding the key apple to that object. Rather, it's defining an implicit (global) variable named apple and assigning the object to that, as well as the first element of the array.
Try this:
var dictionary= {
apple: {name:"apple",pos:"noun",d:"",c:["fruit"]},
banana: {name:"banana",pos:"noun",d:"",c:["fruit"]}
};
console.log(dictionary["apple"].pos); // "noun"
// This also works:
console.log(dictionary.apple.pos); // "noun"

JavaScript property access using brackets

If I declare the following in my Chrome console:
var object = {0:0, 1:1}
I can call object[0] and object[1] and get their values. I can also call object["0"] and object["1"]. Next, if I declare:
var object = {"0":0, "1":1}
I can also make all four of the above calls. But if I declare:
var object = {a:0, 1:1}
I get a ReferenceError of "a is not defined" when I call object[a], but object["a"] returns 0, even though the property name in the declaration is not a string. I guess JavaScript thinks I'm calling a variable that doesn't exist in the first example. But why do calling object[0] and object["0"] both work? It seems that JavaScript is doing some kind of automatic conversion for numbers (presumably since they can't be variable names), but what are the rules for this? And is this behavior universal to other places it might come up or just to the bracket notation for objects?
When you use brackets, the expression inside the brackets is evaluated. What's the value of the expression
a
?? Well, if "a" isn't a declared variable, it's nonsense. When you use . notation, the identifier (and it must be an identifier) following the operator is treated as a string. It's just the way the language works.
The reason you're getting a ReferenceError for object[a] is because a literal a is a variable in javascript. "a" is a string containing the letter a.
You can use the dot notation object.a or the bracket notation with object["a"]
object.a; //=> 0
object["a"]; //=> 0
object[1]; //=> 1
object["1"]; //=> 1
Or you can use a variable for access
var x = "a";
object[x]; //=> 0
var y = 1;
object[y]; //=> 1
You are correct.
a there is a token which the engine assumes is a variable.
If you type "a" JS knows it's a string-primitive.
If you type 0, JS knows it's a number-primitive.
So on top of obj.a, obj["a"], obj[0], obj["0"], you can also say:
var a = 0;
obj[a]; // 0
Your app is exploding, because a hasn't been defined yet, and now you want to use it.
And yes, this is the expected behaviour.
What's inside of the brackets isn't seen as a "part" of the object -- it's a way of saying "give me the value of the object which is referenced by this key", where the key might be a number or string (or something that can be coerced into a string or number).
In the future, with maps and weakmaps, you would actually be able to use other objects/functions as keys as well.
var obj = new Map(),
func = function () { },
el = document.getElementById("myId");
obj[func] = 1;
obj[el] = 2;
Right now, these things technically work... ...but only because they're converted to their string values... ...so if you had two functions which were written the same (but technically two different objects), you would overwrite values, currently.
Inside of a map, they'd be treated as separate objects.
Using DOM elements is even worse, right now, as it might be useful to store hundreds of those and bind references to them, so that you don't have to keep looking for them... ...but for now, you need to make a unique ID number/key for each one, and store that, and remember the keys, and then create a child object to hold the data you want...
Whereas in the future, with maps, you could say:
var my_els = document.querySelector(".lots-of-els");
for (let el of my_els /* also the future */) {
console.log( my_map_of_data[el].info );
}

Javascript array becomes an object structure

I'm experiencing an odd behavior (maybe it isn't odd at all but just me not understanding why) with an javascript array containing some objects.
Since I'm no javascript pro, there might very well be clear explanation as to why this is happening, I just don't know it.
I have javascript that is running in a document. It makes an array of objects similar to this:
var myArray = [{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...];
If I print out this array at the place it was created like JSON.stringify(myArray), I get what I was expecting:
[{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...]
However, if I try to access this array from a child document to this document (a document in a window opened by the first document) the array isn't an array any more.
So doing JSON.stringify(parent.opener.myArray) in the child document will result in the following:
{"0":{"Id":"guid1","Name":"name1"},"1":{"Id":"guid2","Name":"name2"},...}
And this was not what I was expecting - I was expecting to get the same as I did in teh parent document.
Can anyone explain to me why this is happening and how to fix it so that the array is still an array when addressed from a child window/document?
PS. the objects aren't added to the array as stated above, they are added like this:
function objTemp()
{
this.Id = '';
this.Name = '';
};
var myArray = [];
var obj = new ObjTemp();
obj.Id = 'guid1';
obj.Name = 'name1';
myArray[myArray.length] = obj;
If that makes any difference.
Any help would be much appreciated, both for fixing my problem but also for better understanding what is going on :)
The very last line might be causing the problem, have you tried replacing myArray[myArray.length] = obj; with myArray.push(obj);? Could be that, since you're creating a new index explicitly, the Array is turned into an object... though I'm just guessing here. Could you add the code used by the child document that retrieves myArray ?
Edit
Ignore the above, since it won't make any difference. Though, without wanting to boast, I was thinking along the right lines. My idea was that, by only using proprietary array methods, the interpreter would see that as clues as to the type of myArray. The thing is: myArray is an array, as far as the parent document is concerned, but since you're passing the Array from one document to another, here's what happens:
An array is an object, complete with it's own prototype and methods. By passing it to another document, you're passing the entire Array object (value and prototype) as one object to the child document. In passing the variable between documents, you're effectively creating a copy of the variable (the only time JavaScript copies the values of a var). Since an array is an object, all of its properties (and prototype methods/properties) are copied to a 'nameless' instance of the Object object. Something along the lines of var copy = new Object(toCopy.constructor(toCopy.valueOf())); is happening... the easiest way around this, IMO, is to stringency the array withing the parent context, because there, the interpreter knows it's an array:
//parent document
function getTheArray(){ return JSON.stringify(myArray);}
//child document:
myArray = JSON.parse(parent.getTheArray());
In this example, the var is stringified in the context that still treats myArray as a true JavaScript array, so the resulting string will be what you'd expect. In passing the JSON encoded string from one document to another, it will remain unchanged and therefore the JSON.parse() will give you an exact copy of the myArray variable.
Note that this is just another wild stab in the dark, but I have given it a bit more thought, now. If I'm wrong about this, feel free to correct me... I'm always happy to learn. If this turns out to be true, let me know, too, as this will undoubtedly prove a pitfall for me sooner or later
Check out the end of this article http://www.karmagination.com/blog/2009/07/29/javascript-kung-fu-object-array-and-literals/ for an example of this behavior and explanation.
Basically it comes down to Array being a native type and each frame having its own set of natives and variables.
From the article:
// in parent window
var a = [];
var b = {};
//inside the iframe
console.log(parent.window.a); // returns array
console.log(parent.window.b); // returns object
alert(parent.window.a instanceof Array); // false
alert(parent.window.b instanceof Object); // false
alert(parent.window.a.constructor === Array); // false
alert(parent.window.b.constructor === Object); // false
Your call to JSON.stringify actually executes the following check (from the json.js source), which seems to be failing to specify it as an Array:
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
//stringify

How To Set a Property's Property While Creating the Object

I have an object and one of the properties is an array. I want to set a new property on that array (customProp = 0). However, I want to do so inside myObject's declaration.
myObject = {
someString: "text",
someArray: ["one","two"],
/* What I've Tried */
someArray: { customProp: 0 } // Overwrites my array with a new object
someArray["customProp"]: 0 // Invalid syntax
someArray: customProp: 0 // Also invalid
}
If I can't create the array then set a property on it, can I do so in one fell swoop (again, staying within the confines of this object)?
I have another (small) question: How can I reference one of the properties still inside the declaration. I want to set otherProp = someString, how would I do so?
myObject = {
someString: "text",
otherString: someString, // someString is undefined
otherString: myObject["someString"], // myObject is undefined
otherString: this["someString"] // Just... undefined
}
I may need to split this into a separate question, but hopefully whoever answers the first will know the answer to the second.
Unfortunately neither of your requests are possible. Object literals in JavaScript are convenient but come with drawbacks, mostly what you've discovered.
When you are inside an object literal, the object doesn't quite exist yet. The JavaScript interpreter hasn't finished ingesting it. So inside the literal, this points to the object just outside the literal, and the literal has no way to refer to other parts of itself.
Fortunately, you can do both of what you want by just doing it after the literal declaration.
myObject = {
someString: 'text',
someArray: ['one', 'two']
};
myObject.someArray.customProp = 0;
myObject.otherString = myObject.someString;
Or if you want, you can wrap all of this inside a constructor function and create your object with new.
function MyObject() {
this.someArray = ['one', 'two'];
this.someArray.otherProp = 0;
this.otherString = this.someString = 'text';
}
var myObject = new MyObject();
Well, arrays are numeric-based, so
someArray["customProp"]: 0
wouldn't work. It should be a Javascript Object {} for string-based keys to work. And then you could just say
someArray: {0:"one",1:"two","customProp":0},
For your second question: I don't think that's possible. The object is not yet initialized, so you can't yet read out of it...
You can set properties on the Array but they will not get iterated using native functions. Your alternatives are an object (as suggested) or an array with an object as one of the members:
["one", "two", {customProp: 0}]

Categories