0

I have divided this question into two sub-parts, because they are related, and to make it easier to understand.

var outside = ['laptop', 'tv', 'car'];
for (p in outside) {
    var message = p;
    console.log(message);
}

Now, here we have an Array, and the variable p jumps through all properties, in this case, array items, and assigns to itself the index number of the items. So, 1..2..3. This allows me to then do this:

var outside = ['laptop', 'tv', 'car'];
    for (p in outside) {
        var message = outside[p];
        console.log(message);
    }

..use the variable p as a numerical substring to return the property name of each array item.

Now, two questions here:

  1. If the variable goes through properties, like its description says, then why does using it on an array, the variable assigns the index numerical value of the items?
  2. Why does using outside[number] returns the name of the property?

One explanation that would make sense is if each array item is actually an object, and obviously the substring [number] returns the name of that object, where as the for..in variable jumps through each property of the array item objects, and their properties are actually the index number, so that's what it returns.

Is that explanation right, or completely wrong?

The second part of my question is using JSON objects.

var marry = '{"age":"30","height":"180","eyecolor":"blue"}';

var maryobject = JSON.parse(marry);

var out = "";

for (i in maryobject) {
 out = i;
  console.log(out);
 }

Now, here the variable i returns the property names of the object.

However, using the variable i as a substring like I did before with the Array, returns the value of the properties:

    var marry = '{"age":"30","height":"180","eyecolor":"blue"}';

    var maryobject = JSON.parse(marry);

    var out = "";

    for (i in maryobject) {
     out = maryobject[i];
      console.log(out);
     }

Now, two questions here too:

  1. Why does [i] indicate the position of the property, since [i] is not a numerical value?

  2. Why does maryobject[i] returns back the value of the property?

Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
happy_story
  • 1
  • 1
  • 7
  • 17
  • 1. arrays are exotic objects. so the property names (not methods) are numerical indexes --- 2. `outside[number]` doesn't return the name. it returns the property value – evolutionxbox Feb 03 '21 at 23:30
  • 1
    In regards to your second lot of questions: 1. `[i]` is not an numerical value. --- 2. That's called "square bracket notation" for property access. – evolutionxbox Feb 03 '21 at 23:59
  • So, the property name of the array items is their numerical indexes, but the value of the properties is the name that I have given to the array items? So, the array items are properties, not objects, correct? – happy_story Feb 04 '21 at 12:08
  • Arrays are objects (with special abilities). Objects have properties. Properties have values. Given `arr = ['a','b','c']` then the array has three properties (plus extra). `arr[0]` will return the value of the property `0` which is `a`. – evolutionxbox Feb 04 '21 at 12:11
  • [ i ] is not a numerical value, exactly, so why does it indicate the number of the properties as if the object is an array, and the [ i ] is a number? – happy_story Feb 04 '21 at 12:11
  • Which example are you talking about? There are multiple that use `[i]` – evolutionxbox Feb 04 '21 at 12:12
  • The second part. maryobject[i] this one. Used on JSON object. – happy_story Feb 04 '21 at 12:13
  • On JSON object, the property name is not an index number, so why does using the [ i ] indicates the number of the property? – happy_story Feb 04 '21 at 12:13
  • Given the object `obj = {"a":1,"b":2}` the object has two properties (plus others). `obj.a` and `obj['a']` will return the value `1` and `'a'` is the property name. For each iteration of the loop `for (i in obj)`, `i` will be the property name. Therefore to get the value in each iteration we must use the bracket notation `obj[i]` – evolutionxbox Feb 04 '21 at 12:23
  • Yes, I think I understand now. I was confused because the explanation given to me in the JS book I'm reading led me to believe that the [ i ] variable indicates the number of the properties, rather than the property name itself. – happy_story Feb 04 '21 at 12:32
  • Can I ask you one more thing? What is the difference between creating a JSON object like this: var jsonobject = { "param1":"value1", "param2":"value2" } AND var jsonobject = '{ "param1":"value1", "param2":"value2" }'; like this? I.e. with and without the single quote? When using no quotes, I can say this: alert(jsonobject.param2) and it works. It returns the value of the property, however, parsing does not work. var newobject = eval ('(' + jsonobject + ')') this doesn't work, however it works if I use the quote before and after the braces. What is the difference between them? – happy_story Feb 04 '21 at 12:36
  • The first isn't JSON. It's a JS object. The second is JSON and it's a string. The second needs to be parsed into a JS object before it can be used. – evolutionxbox Feb 04 '21 at 12:38
  • If I define the JSON object with a single quote, like this var jsonobject = '{ "param1":"value1", "param2":"value2" }', now if I try to take the property value, it won't work. alert(jsonboject.param1) does not work until i parse it first, but it works if i define it without the single quote. I don't understand the difference. – happy_story Feb 04 '21 at 12:38
  • I see. So, what exactly is the benefit of using JSON object instead of JS object? Isn't it so much easier to use JS object instead of defining a JSON and then having to parse it? – happy_story Feb 04 '21 at 12:40
  • `jsonobject = { "param1":"value1", "param2":"value2" }` this defines an "object literal" and assigns it to the variable `jsonobject`. `jsonobject = '{ "param1":"value1", "param2":"value2" }'` is a string. – evolutionxbox Feb 04 '21 at 12:40
  • https://stackoverflow.com/questions/286945/what-is-json it's for data transfer. JS objects cannot be passed around to other computers/systems. – evolutionxbox Feb 04 '21 at 12:41
  • I see. The book said that too, but never clarified the difference between JS object and JSON object, so I got confused. Thanks a lot for the help. – happy_story Feb 04 '21 at 12:43

3 Answers3

2

Arrays are Objects that have fake numeric properties. That's one way of looking at it, anyways. Check this out:

const a = ['neat', 'no', 'not', 'really'];
console.log(a['0']); console.log(typeof a); console.log(a instanceof Array); 
console.log('-'.repeat(50));
for(let i in a){
  console.log(typeof i);
}
const o = {0:'I', nice:"can't'", prop:'believe', 3:'this', key:'is a question'};
console.log('-'.repeat(50));
console.log(o[0]); console.log(typeof o); console.log(o instanceof Array);
console.log('-'.repeat(50));
for(let p in o){
  console.log(typeof p);
}

Notice that you're able to access an Object with a Numeric index. Objects really take String properties. When a Numeric property is used they cast it to a String. In for(let prop in obj), prop is the actual property value, which is a String. Yeah, scoping off those loop variables is a good idea too.

StackSlave
  • 10,613
  • 2
  • 18
  • 35
1

Both Arrays and parsed JSON objects (even functions) are objects in a sense in JavaScript. as @StackSlave said, you can define objects with numerical names for properties; however, they are not valid JSON. This demo shows that:

var o = {
  0: "a"
};
var s = '{0:"a"}';

console.log(JSON.stringify(o));
console.log(JSON.parse(JSON.stringify(o)));

try {
  console.log(JSON.parse(s));
} catch (error) {
  // preventing error in console
  console.log(error.message);
}

Now, if you open up this page's console in developer tools, you will see the line which parses the stringified object, is shown as {0: "a"} in contrast to the snippet's result which considered the parsed property name a string. That's because of the V8, and is not a standard to rely on.

So you see, what consoles show us, are what JS engines interpret. In the standard arrays (lowercase) and Arrays (capitalized) are two things. But JS interpreters and engines sometimes do what they think are best.

The for...in for this matter iterates over enumerable properties of an object and in old days (or different engine) you may have actually received some kind of error because the right way to iterate over an array (lowercase) was for(i=0; i<a.length; i++). Period. Nowadays, with the advent of objectified Arrays, the right ways of iterating over these objects' values arguably are Array.prototype.forEach() and for...of:

"Given that for...in is built for iterating object properties, not recommended for use with arrays, and options like Array.prototype.forEach() and for...of exist, what might be the use of for...in at all?" for...in - JavaScript | MDN

You can also read more about enumerability, iterativeness and ownership of objects and their properties here.

At the end if you want to define a property without the default behavior, you can define it with Object.defineProperty() like this.

Some trickery here:

var o = {};

Object.defineProperty(o, "first_name", {
  value: "John",
  writable: false,
  enumerable: false,
  configurable: true
});

Object.defineProperty(o, "last_name", {
  value: "Doe",
  writable: false,
  enumerable: false,
  configurable: true
});

Object.defineProperty(o, "name", {
  enumerable: true,
  configurable: false,
  get() {
    return `${this.first_name} ${this.last_name}`;
  },
  set(value) {
    let [first_name, last_name] = value?.split(' ') ?? ["N/A", "N/A"];
    
    Object.defineProperty(this, "first_name", {
      value: first_name
    });
    Object.defineProperty(this, "last_name", {
      value: last_name
    });
  }
});

// Look at browser's console for table
console.table({
  "first_name": o.first_name,
  "last_name": o.last_name,
  "name": o.name
});
for (let i in o) {
  console.log(`${i}:`, o[i]);
}

o.name = "No way";

// Look at browser's console for table
console.table({
  "first_name": o.first_name,
  "last_name": o.last_name,
  "name": o.name
});
for (let i in o) {
  console.log(`${i}:`, o[i]);
}
Tala
  • 909
  • 10
  • 29
1

The Spec writes:

An Object is logically a collection of properties.

Properties are identified using key values. A property key value is either an ECMAScript String value or a Symbol value. All String and Symbol values, including the empty String, are valid as property keys. A property name is a property key that is a String value.

An integer index is a String-valued property key that is a canonical numeric String and whose numeric value is either +0 or a positive integer ≤ 2^53 - 1. An array index is an integer index whose numeric value i is in the range +0 ≤ i < 2^32 - 1.

And about arrays:

An Array object is an exotic object that gives special treatment to array index property keys. A property whose property name is an array index is also called an element.

That is, arrays are objects optimized for property names whose numeric value is a sufficiently small positive integer.

Put differently

a = [4];

is like

a = {'0': 4};

except for having a different prototype and being an exotic object.

If the variable goes through properties, like its description says, then why does using it on an array, the variable assigns the index numerical value of the items?

As we just learned, an array is an object whose property names are array indices.

Why does using outside[number] returns the name of the property?

An expression of the form a[b] returns the value of the property whose name is equal to b of the object a. For instance:

const a = {foo: 'bar'};
a['foo']; // returns 'bar'

The same is true for arrays:

const a = ['bar'];  // a = {'0': 'bar'}, except for being exotic and having a different prototype
a['0']; // returns 'bar'
a[0]; // also returns 'bar', since EcmaScript automatically converts number to string where required

Why does [i] indicate the position of the property, since [i] is not a numerical value?

[i] does not "indicate a position". a[i] is a property access expression, and it accesses the value of a property whose name is equal to the value of i.

Since property names are strings (or Symbols), it is up the to runtime environment to find the memory address this property is stored at. How it does this is unspecified - the runtime may chose whatever optimizations it wants as long as the results are correct.

Of course, every modern JavaScript runtime will translate a[i] into a direct memory access if a is an array and i a number (with appropriate bounds checking, of course). But it must also handle the general case where i is an arbitrary string (or Symbol).

For instance, the following is an entirely legal EcmaScript program:

const a = [42];
a.foo = 'bar';   // shorthand for a['foo'] = 'bar'
for (const key in a) {
  console.log(key, a[key]); 
}

and prints both properties:

0 42
foo bar
meriton
  • 68,356
  • 14
  • 108
  • 175