mirror of
				https://github.com/Brandon-Rozek/website.git
				synced 2025-10-31 13:51:13 +00:00 
			
		
		
		
	New post
This commit is contained in:
		
							parent
							
								
									8198ffdb9b
								
							
						
					
					
						commit
						6d2b22eef8
					
				
					 1 changed files with 160 additions and 0 deletions
				
			
		
							
								
								
									
										160
									
								
								content/blog/cursed-knowledge-javascript-arrays-are-objects.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								content/blog/cursed-knowledge-javascript-arrays-are-objects.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,160 @@ | ||||||
|  | --- | ||||||
|  | title: "Cursed Knowledge: Javascript Arrays Are Objects" | ||||||
|  | date: 2025-09-01T09:47:01-04:00 | ||||||
|  | draft: false | ||||||
|  | tags: [] | ||||||
|  | math: true | ||||||
|  | medium_enabled: false | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | My friend Ethan recently wrote a blog post on [cursed commands](https://emar10.dev/posts/cursed-commands-part-1/). Chris shared with me that Immich has a page on their site called [cursed knowledge](https://immich.app/cursed-knowledge/), and it looks like this has started a trend. I've seen my fair share of the dark arts in programming, so I'll hop on and share what I know about JavaScript arrays. | ||||||
|  | 
 | ||||||
|  | JavaScript arrays are [*exotic objects*](https://262.ecma-international.org/#sec-array-exotic-objects) according to the ECMAScript specification. Therefore, they may lead to unintuitive behavior if we think of these arrays as C-like. | ||||||
|  | 
 | ||||||
|  | Let's play around. | ||||||
|  | 
 | ||||||
|  | ### Concept 1: JavaScript arrays are not continguous | ||||||
|  | 
 | ||||||
|  | First, consider the following array: | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | let x = [0, 1, 2]; | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | As one might expect, `x.length` is equal to `3`. To tell whether or not an index is in an array, we can use the `in` operator. | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | 3 in x // Evaluates to false | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | If we try to access the 3rd index, the result will evaluate to `undefined`. | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | x[3] // Evaluates to undefined | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Now let's assign an element to the 4th index. Keep in mind that we're skipping over the 3rd one. | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | x[4] = 4; | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Now when we check our `length` property, it'll say that our array is now of size `5`. | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | x.length // Evaluates to 5 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | However, the 3rd index still does not exist | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | 3 in x // Evaluates to false | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Concept 2: Explicit vs Implicit `undefined` | ||||||
|  | 
 | ||||||
|  | Recall that `x[3]` evaluates to `undefined`. What happens when we set the value explicitly? | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | x[3] = undefined; | ||||||
|  | 3 in x // Evaluates to true | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | So there is a difference on whether we have explicitly set an index to `undefined`! This distinction is not always used. For example, our trusty for-of loop does not care. | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | x = [0, 1, 2]; | ||||||
|  | x[4] = 4; | ||||||
|  | for (a of x) { | ||||||
|  |     console.log(a) | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Will print out | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | 0 | ||||||
|  | 1 | ||||||
|  | 2 | ||||||
|  | undefined | ||||||
|  | 4 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Concept 3: Indices are actually strings | ||||||
|  | 
 | ||||||
|  | Given that we have a length property and that we've been indexing with numeric keys, it must mean that arrays have numeric indices. Right? | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | "0" in ["a", "b"] // Evaluates to true | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Okay, it looks like there's some conversion magic that's happening behind the scenes here. The ECMAScript specification says that an array index must be strictly less than $2^{32}$. So what happens if it is not? | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | let x = [0]; | ||||||
|  | x[4294967296] = true | ||||||
|  | x // Evaluates to [ 0, '4294967296': true ] | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | It looks like it no longer gets treated as an array item, but instead treats it as an arbitrary key-value pair. Why stop there, this must mean that we can store any sort of arbitrary data in our array. | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | x.name = "Brandon" | ||||||
|  | x // Evaluates to [ 0, '4294967296': true, name: 'Brandon' ] | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Viewing arrays as objects | ||||||
|  | 
 | ||||||
|  | Now everything starts to make more sense when we think of these arrays as objects. | ||||||
|  | 
 | ||||||
|  | ```javscript | ||||||
|  | let x = [0, 1, 2]; | ||||||
|  | x[4] = 4; | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Internally, this corresponds to the object: | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | { | ||||||
|  |     "0": 0, | ||||||
|  |     "1": 1, | ||||||
|  |     "2": 2, | ||||||
|  |     "4": 4 | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | From this object, we can see that the keys are strings and that the 3rd key is not in the object. Now let's see what happens when we explicitly set `x[5] = undefined`. | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | { | ||||||
|  |   "0": 0, | ||||||
|  |   "1": 1, | ||||||
|  |   "2": 2, | ||||||
|  |   "4": 4, | ||||||
|  |   "5": undefined | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | The 5th key is now in our object and it's set to an undefined value. We also get an undefined value when we try to retrieve a value of a key that is not in our object. | ||||||
|  | 
 | ||||||
|  | The length of our array is the highest "numeric" key within our object (subject to the size limit). When we iterate over our array using `for-of`, we're iterating from `"0"` to our length. | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | for (a of x) { | ||||||
|  |     console.log(a); | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Is the same as: | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | console.log(x[0]); | ||||||
|  | console.log(x[1]); | ||||||
|  | console.log(x[2]); | ||||||
|  | console.log(x[3]); | ||||||
|  | console.log(x[4]); | ||||||
|  | console.log(x[5]); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | That's an exotic object for you. | ||||||
|  | 
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue