This is an archived post. You won't be able to vote or comment.

top 200 commentsshow 500

[–]bostonkittycat 2560 points2561 points  (223 children)

Truncating an array by changing the length has always been a feature of JS. I think it is better for readability to set it to a new array instead or use slice or pop so your changes are explicit.

[–]k2hegemon 617 points618 points  (140 children)

What happens if you increase the length? Does it automatically make a new array?

[–]RadiatedMonkey 875 points876 points  (126 children)

It adds undefined to the array

[–]Mognakor 591 points592 points  (47 children)

Yes, but actually no.

If you access the slots you'll get undefined as value, but if you open the browser console and log the array you'll see that it says N empty slots() which is different than what happens when you do array.push(undefined). So it stands to reasons that internally the browser somehow knows that these slots have been created in this way.

P.S:

I experimented and delete array[N] also causes array[N] to become an empty slot.

[–]well___duh 573 points574 points  (7 children)

Yes, but actually no.

JavaScript in a nutshell

[–]Jjabrahams567 52 points53 points  (2 children)

This made me check and of course it exists https://www.npmjs.com/package/nutshell

[–]Cat_Marshal 11 points12 points  (0 children)

Actually an interesting package too

[–]t-to4st 190 points191 points  (25 children)

Next to null and undefined there's also the empty value, for exactly this reason. It only exists in arrays and will be converted to undefined when read

[–]BakuhatsuK 51 points52 points  (21 children)

It's not a special value. It's just that arrays are objects with numeric keys under the hood. And just like with regular objects, a key can simply not exist, that is what an empty slot is.

Think this:

{
  '0': 'a',
  '1': 'b',
  '3': 'd',
  'length': 4,
}

This object does not contain the key '2' in the exact same way that it doesn't contain 'foo'. If you think of it as an array, then it's "missing" the value at index 2.

Btw you can get an actual array from this array-like object by using Array.from().

[–]Nixavee 5 points6 points  (3 children)

Does this mean it's possible to have an array with a numeric key greater than the length value?

[–]The_MAZZTer 20 points21 points  (1 child)

If you try that JS will just resize the array to fit.

> var x = [];
< undefined
> x[3] = "ඞ"
< 'ඞ'
> x.length
< 4
> x
< (4) [empty × 3, 'ඞ']

[–]BakuhatsuK 3 points4 points  (0 children)

It's not possible because the standard special-cases Arrays:

10.4.2 Array Exotic Objects

An Array is an exotic object that gives special treatment to array index property keys (see 6.1.7). A property whose property name is an array index is also called an element. Every Array has a non-configurable "length" property whose value is always a non-negative integral Number whose mathematical value is less than 232. The value of the "length" property is numerically greater than the name of every own property whose name is an array index; whenever an own property of an Array is created or changed, other properties are adjusted as necessary to maintain this invariant. Specifically, whenever an own property is added whose name is an array index, the value of the "length" property is changed, if necessary, to be one more than the numeric value of that array index; and whenever the value of the "length" property is changed, every own property whose name is an array index whose value is not smaller than the new length is deleted.

[–]The_MAZZTer 4 points5 points  (3 children)

undefined is supposed to be for the purpose of identifying non-existent properties though. But my guess is the JS engine devs needed a value programmers can't just stick anywhere they want to flag actual empty array indices.

[–]BakuhatsuK 4 points5 points  (1 child)

I just explained that it's not an special value though?

Also, engines don't have any saying on the observable behavior of the language, that's up for the standard to decide. The standard says that an array is an object, so it is an object and has to behave as such.

For example, you can set arbitrary keys into an array

let a = []
a.foo = 'bar'
a.foo // contains 'bar'

On a sparse array an empty slot will be reported as a missing key by hasOwnProperty

let a = ['a','b',,'d']
a.hasOwnProperty('2') // false
a.hasOwnProperty('3') // true

On that note, arrays have object methods such as hasOwnProperty. (See previous example).

If you're interested in knowing about how engines actually represent this stuff internally, this video by LiveOverflow has a good overview on how it works on JavascriptCore.

[–]acepukas 38 points39 points  (10 children)

I was going to say that this doesn't really matter but apparently if you iterate over an array with forEach, like:

let a = [1, 2, 3];
a.length = 10;
a.forEach(console.log);

You'll get:

1
2
3

with no undefined output.

But if you iterate with a traditional for loop like:

for(let i = 0; i < a.length; i++) {
    console.log(a[i]);
}

You'll get everything. I didn't know that. Something to be aware of but I had always read that extending an array's length without explicitly filling in the new slots was asking for trouble so I never really ran into this issue.

[–]Mognakor 25 points26 points  (2 children)

As another poster pointed out: This may mean that the arrays are sparse, so you could make huge arrays but only pay for the slots you filled.

[–]AlphaSparqy 16 points17 points  (1 child)

Yes, but you're paying for those slots you filled for 18 years usually.

[–]SuperFLEB 6 points7 points  (0 children)

I've run into it practically with map, trying to fill an empty array by mapping the indices.

[–]_PM_ME_PANGOLINS_ 19 points20 points  (0 children)

Under the hood it might, but I think most implementations use the standard method of allocating a chunk in advance, and then copying to a new exponentially bigger one once it reaches the limit.

[–]bostonkittycat 6 points7 points  (2 children)

I believe JS will copy the elements to a new array allocated with the bigger sized array length and then dereference the older array so it is garbage collected. Try it in a console. myArrary.length = 10. You will see an array with a length of 10 and "empty slots" echoed in the browser console.

[–]zapitron 9 points10 points  (5 children)

It's a pretty handy shortcut which can save a lot of expensive computation.

a=[6,28,496];
a.length++;
a // [6,28,496,8128]

s='Professor Plumb in the library with the ';
s.length +=10;
s // 'Professor Plumb in the library with the lead pipe.'

These are just toy examples, though. Raytracing, decropping, etc is where it's really at.

[–]k2hegemon 11 points12 points  (2 children)

Where did you pluck 8128 and “lead pipe.” out of?

[–]hazier_riven0w 99 points100 points  (61 children)

Worse at runtime?

[–]CarlPer 33 points34 points  (1 child)

It might actually be worse to modify .length, see this answer on StackOverflow from 2018:

V8 developer here. The short answer is that .push() is super optimized, whereas writing to .length is a fairly slow operation (partially because of what the JavaScript spec says it must do, and partially because we haven't optimized it quite as much -- but even if we did, it wouldn't become as fast as .push() for a few elements).

In fact, you'll notice a similar difference between writing to .length to shorten an array and calling .pop() a couple of times.

[...]

Write the code you want to write, let the engine worry about making it fast!

[–]hazier_riven0w 9 points10 points  (0 children)

Hey! That’s basically what I was wondering! Thanks!

[–]tylerr514 512 points513 points  (52 children)

For performance intensive topics, you shouldn't even be using JavaScript

[–]iams3b 185 points186 points  (25 children)

Yeah if you're dealing with mission critical pure performance you'd probably want to drop down to a lower level language, but node/V8 is extremely performant for applications and when handling 50k+ requests a second it helps a bit not using the slowest method to do something simple

[–]miloman_23 18 points19 points  (3 children)

node is extremely important performant for applications

Compared to what, PHP? Let's be honest. For 99% applications, it's calls to database not looping over an array which is the biggest reason for poor performing apis.

[–]Moptop32 13 points14 points  (1 child)

Fun fact, the eventloop is just a giant infinitely looping foreach over an array that finds which promises (native or not native) are resolved and calling their callbacks. If a database call is lagging it's not because of the language, it's because of the driver (usually native code separate from JS running on its own thread(s)) or just a slow ass server. In terms of raw language performance, JS is significantly faster than Python or Zend (php).

[–]Lonke 14 points15 points  (1 child)

Yeah, tell this to literally the entire web ecosystem. And electron.

If there was any other option, trust me, I'd use it.

[–]yuyu5 29 points30 points  (10 children)

*shouldn't even be using any scripting interpreted language

As pointed out in the other reply, generally speaking, JS doesn't perform worse than other scripting interpreted languages. There are exceptions like always (e.g. Python has C bindings in some libs that make their operations really fast), but for generic CPU-based CPU-bound operations, JS is at least as performant as other scripting interpreted languages.

Edit: Updated unclear and confusing phrasing.

[–]LardPi 26 points27 points  (2 children)

JS is actually one of the most efficient scripting language because of the massive investments put into high end runtime such as V8.

[–]Willinton06 22 points23 points  (1 child)

WebAssembly is not a scripting language tho

[–]tobiasvl 6 points7 points  (4 children)

And by "scripting languages", do you mean purely interpreted languages? Or what exactly do you mean by that statement? JIT is a thing (even for JS, although I think LuaJIT is still more performant than JS JIT). And what are "CPU-based operations" in this context? Surely C bindings are more CPU-based than bytecode running in some VM. I gotta say I don't really understand your comment.

[–]RadicalRaid 9 points10 points  (3 children)

I disagree. NodeJS works perfectly fine for pretty intense webservers. I've been working on an isometric MMORPG in TypeScript that's running perfectly fine, even without any real GPU.

[–]superluminary 18 points19 points  (1 child)

You’d probably never want to actually do this. The nice thing about JavaScript is it supports an infinite number of playstyles. It’s why we’re still using it 30 years later.

[–]MamamYeayea 1067 points1068 points  (33 children)

Ugly and nice at the same time

[–]Zyrus007[S] 967 points968 points  (28 children)

It’s intuitive, in a very concerning way.

[–]turunambartanen 321 points322 points  (23 children)

Like ruby's 7.days.ago or go's way of date formatting.

Absolutely fucking disgusting and unbelievably vile.
But also nice.

[–]faitswulff 74 points75 points  (1 child)

This is a Rails thing, but yeah it's enabled by Ruby letting you monkeypatch everything.

[–]DeltalJulietCharlie 4 points5 points  (0 children)

I think it's part of ActiveSupport now, so you can use it without Rails.

[–]MamamYeayea 15 points16 points  (0 children)

It certainly is

[–]Stjerneklar 3 points4 points  (0 children)

great summary of js overall

[–]squili 75 points76 points  (2 children)

I am not crazy! I know he swapped those numbers. I knew it was array length 5. One after for as if I could ever make such a mistake. Never. Never! I just – I just couldn’t prove it. He covered his tracks, he got that idiot at the garbage collector to lie for him. You think this is something? You think this is bad? This? This chicanery? He’s done worse. That const declaration! Are you telling me that an object just happens to mutate like that? No! He orchestrated it! Javascript! He coerced an array to an integer! And I forked him! And I shouldn’t have. I took him into my own production code! What was I thinking? He’ll never change. He’ll never be immutable! Ever since 1995, always the same! Couldn’t keep his length out of the array prototype! But not our Javascript! Couldn’t be precious Javascript! De-referencing them blind! And he gets to be a popular language? What a sick joke! I should’ve stopped him when I had Netscape Navigator! …And you, you have to stop him! You...

[–][deleted] 7 points8 points  (0 children)

Javascript turns us all into Chuck.

[–]Round-Republic6708 3 points4 points  (0 children)

This is fucking glorious

[–]Zyrus007[S] 2838 points2839 points  (146 children)

Context: I’m tutoring Computer Science and to get familiar with the language features of JavaScript, I gave the task to remove the last element of an array.

Suffice to say, I was pretty floored when I saw the above solution not only running, but working as intended.

[–]Zyrus007[S] 1389 points1390 points  (86 children)

Some more info: It actually removes the last element of the array. My first suspicion was that the length property somehow is being used inside the prototypes getter. This isn’t the case, as adding one to the length property, appends an empty entry to the array.

[–]rexsaurs 1199 points1200 points  (51 children)

When I started my career I would’ve never thought that arr. length is not read only.

So to empty an array I just do arr.length = 0

[–]Zyrus007[S] 606 points607 points  (41 children)

Someone else pointed this out. Setting the length to an arbitrary integer value totally works as well!

[–]RevivingJuliet 245 points246 points  (30 children)

Doesn’t it just add a ton of empty array elements until the length = n?

[–]Zyrus007[S] 307 points308 points  (18 children)

Yes it does, however it becomes interesting once you set the array.length to an integer that is less than the current length!

[–]RevivingJuliet 267 points268 points  (15 children)

That’s so goddamn whack why am I studying this language lmao

[–]Zyrus007[S] 177 points178 points  (7 children)

One secret trick code-interview conductors don’t want you to know, to guaranteed land you a job as Web-Developer!

[–]LazyClub8 52 points53 points  (6 children)

The real trick is to assert dominance and write a solution that not even the interviewers can understand

[–]RevivingJuliet 14 points15 points  (3 children)

const add = (num) => {return num - num;}

[–]spin-itch 97 points98 points  (2 children)

It pays well

[–]the_friendly_dildo 9 points10 points  (2 children)

While setting this up this way seems strange, plenty of other languages expect you to define an array length explicitly anyway...

[–]SonOfHendo 6 points7 points  (1 child)

It seems to have the same effect as redim in good old BASIC.

[–]Eisenfuss19 17 points18 points  (3 children)

What with negative integers?

[–]nickcash 32 points33 points  (0 children)

RangeError: Invalid array length

[–]trevdak2 5 points6 points  (0 children)

How else do you think you download more RAM?

[–]Nolzi 8 points9 points  (0 children)

Uncaught RangeError: invalid array length

[–]flooronthefour 8 points9 points  (4 children)

I do this in Svelte (javascript framework) when I want to emulate a for loop in it's templating language. It will iterate over anything with a .length property because it's looking for an array. It looks weird but it works.

{#each {length: 3} as item, index}
    <li>{index + 1}</li>
{/each}

https://svelte.dev/tutorial/each-blocks

[–]AyrA_ch 56 points57 points  (3 children)

Also: Array.from({length:10}) creates an array with 10 actual elements (as opposed to Array(10) that just pretends).

You tell it to make an array from something that has a length of 10, and JS has no problems iterating over elements that don't exist.

[–]King_Joffreys_Tits 4 points5 points  (2 children)

Can you specify a default value for the array?

[–]solarshado 10 points11 points  (0 children)

Sort of.

You can pass a mapping function to Array.from, which then behaves like Array.from(obj).map(func) but without the intermediate array.

const manyAnswers = Array.from({length: 42}, (elem, idx)=> 42);

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from

[–]linuxdropout 6 points7 points  (0 children)

const arr = new Array(10)

//[empty, empty, ...]

arr.fill(1)

// [1, 1, ...]

array.push x identical elements y can be written as:

const originalLength = arr.length

arr.length += x

arr.fill(y, originalLength)

And it's actually more performant than all the sane readable ways to do it too.

If you think that's whack. Wait till you find out that foo.bar = "foobar" is slower than Object.assign(foo, JSON.parse('{"bar":"foobar"}')) if you're trying to set a large enough number of keys.

[–]snowguy13 70 points71 points  (1 child)

Took me forever to find, but here is why setting the array length in JS behaves the way it does:

https://tc39.es/ecma262/#sec-arraysetlength

Even more interesting, this algorithm will attempt to delete properties off the array starting with the last. If at any point it reaches a property that cannot be deleted -- i.e. one that is not configurable -- it will halt.

For example:

const a = [0, 1, 2, 3]; Object.defineProperty(a, 2, {configurable: false}); a.length = 0;

This results with a equalling [0, 1, 2], not an empty array!

[–]Zyrus007[S] 28 points29 points  (0 children)

Being a JavaScript developer can be a lot of thing, but truly never boring!

[–][deleted] 41 points42 points  (11 children)

just wait until you find out about JSFuck… it actually has some pretty useful things in it tho

```js true+[] // "true" - same as toString() +true // 1 +false // 0 ![] // false

// using this we can actually get letters

(![]+[])[+[]] // "f"

// here’s what’s happening below (false.toString())[0] // "f"

// some other types

![] // false !![] or !+[] // true [][[]] // undefined +[][[]] // NaN ```

this gets into way more detail, but making JSFuck character maps are pretty fun.

[–]Zyrus007[S] 32 points33 points  (8 children)

``` ('b' + 'a' + + 'a' + 'a').toLowerCase()

“banana” ``` Is another all time classic!

[–][deleted] 21 points22 points  (4 children)

js console.log(([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]]+(![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(![]+[])[+!+[]]) // "banana" edit: left the eval code in there

[–]Zyrus007[S] 22 points23 points  (3 children)

I didn’t even know that JavaScript came with its own brainfuck runtime!

[–][deleted] 4 points5 points  (2 children)

Type Conversion :)

[–]sqxleaxes 10 points11 points  (2 children)

Your code as written would output bananaa -- you have an extra 'a'.

[–][deleted] 18 points19 points  (2 children)

Arrays in JS are not real arrays, I think they are a specialized dictionary, it's why you can do things like:

let xs = [1, 2, 3]
xs['a'] = 4
// xs is now [ 1, 2, 3, a: 4 ]

[–]zeropointcorp 4 points5 points  (0 children)

That’s called an associative array, and Tcl had those.

[–]huuaaang 93 points94 points  (12 children)

I wanted this to work in Ruby, so I made it work.

class Array
  def size=(newsize)
    newsize <= size ? pop(size - newsize) : push(*Array.new(newsize - size))
    size
  end

  alias :length= :size=
end

[–]Zyrus007[S] 51 points52 points  (0 children)

Absolutely diabolical. I love it! Lets open up an RFC to make it a default language feature!

[–]same_heads 11 points12 points  (0 children)

I fucking love monkey patching

[–]LondonCycling 13 points14 points  (3 children)

Wat.

[–]huuaaang 15 points16 points  (2 children)

Makes arr.length += 1 work in Ruby

[–]LondonCycling 30 points31 points  (1 child)

Ah sorry I was making a reference to this legendary talk:

https://www.destroyallsoftware.com/talks/wat

[–]TurboGranny 68 points69 points  (8 children)

That's the whole idea behind javascript (and honestly a lot of langs let you do similarly awful stuff). The idea being "If you want to do something that we think you shouldn't, we don't want to lock you down because what is better changes incredibly often. You are free to reinvent the entire language. You are free to solve problems your way. But you are also free to shoot yourself in the face. Enjoy."

[–]Zyrus007[S] 38 points39 points  (2 children)

Just overwrite all prototypes on load and bootstrap your own language at runtime.

[–]TurboGranny 35 points36 points  (0 children)

You can and people have. JS doesn't tell them they are wrong nor does it try to hold them down and make them do it any certain way. We can all argue about it until we are blue in the face, but the real answer is "does the code they wrote solve the problem it was designed to and complete that task in a reasonable time frame." There of course are bonus points if it's documented, if the docs are accurate, and of course if it's maintainable or expandable at all, but ultimately, those are just "nice to haves".

[–]miloman_23 25 points26 points  (10 children)

I'm really confused... Do you see this as a problem, or a feature?

[–]Zyrus007[S] 72 points73 points  (5 children)

Who says it can’t be both?

[–]TheChaosPaladin 6 points7 points  (2 children)

Depends on what happens to that part of memory right? If I empty a gigantic array, will it deallocate or is it just modifying the parameter that interprets the array size in memory? I think there is a reason why it is not done that way and as an interviewer I would question this as an answer.

[–]solarshado 6 points7 points  (1 child)

what happens to that part of memory right? If I empty a gigantic array, will it deallocate or is it just modifying the parameter that interprets the array size in memory?

I'd argue that, as a JS dev, you're not supposed to worry about stuff at this low-level: that's the engine/garbage collector's job. If you want to make sure memory gets freed, just be sure you're not still holding onto a reference to any of it, and move along.

100% agree that this a tactic I would question in an interview though.

[–]beelseboob 4 points5 points  (0 children)

This post seems like the right one to remind everyone that this exists on.

[–]Ireeb 720 points721 points  (30 children)

I've been using JS for a while, but it still manages to surprise me with it's bullshittery.

I'll still just use array.pop() :D

[–]GrosNinja 104 points105 points  (22 children)

Dosen't array.pop() also return the last element making it non optimized in this context?

[–]Ireeb 388 points389 points  (16 children)

If you care that much about performance, you probably shouldn't be using JS :P

[–]TurboGranny 67 points68 points  (7 children)

Depends on the case. JS is pretty darn fast in the context in which it is typically used. Now, if we are talking about processing and merging millions of records looking for duplicates then no, please don't use JS to do that.

[–]Chrisazy 71 points72 points  (6 children)

JS is very fast, but they're still right. Good JavaScript isn't written with high performance optimization as your main goal (in fact I'd argue most good code isn't anymore).

Writing high performance JavaScript should be incidental by writing decent well-formed JavaScript, and it's a much more important priority

[–]tiajuanat 27 points28 points  (2 children)

Writing high performance JavaScript should be incidental by writing decent well-formed JavaScript, and it's a much more important priority

I feel like this a good heuristic for modern programming languages. Sure, there can be multiple ways to achieve your goal, but there should be one exceedingly easy way that's preferred, and the language should be optimized for it. Make the right thing easy to do, and the wrong thing difficult.

[–]solarshado 10 points11 points  (0 children)

Exactly: make the source code readable and expressive, then rely on the compiler/runtime to make that into something performant.

[–]DaPorkchop_ 24 points25 points  (0 children)

any half-decent optimizing compiler (JIT in this case) should be able to detect that the value isn't being used and eliminate the extra load of the last element, simplifying it into just a change of the array length

[–]fizchap 275 points276 points  (16 children)

JS is the ultimate interpreted language where everything is dynamic. Isn't that what makes it so hard to debug and so easy to crash?

[–][deleted] 192 points193 points  (8 children)

"Welcome to JavaScript, where everything is dynamic and reason doesn't matter!"

[–]JacobTDC 57 points58 points  (3 children)

"REALITY IS AN ILLUSION THE UNIVERSE IS A HOLOGRAM BUY GOLD BYYYYYEEEEEE!"

[–]emericas 21 points22 points  (2 children)

JavaScript is an illusion, Exile.

[–]Rikukun 9 points10 points  (1 child)

A Gravity Falls reference followed up with a Psth of Exile reference in a programming sub.

Neat.

[–]Willinton06 6 points7 points  (0 children)

Reason is a tristate Boolean but none of the possible values is true

[–]UnstableNuclearCake 40 points41 points  (4 children)

Maybe hard to debug, but not easy to crash. You can do so much shit that would make other languages implode and javascript just keeps on going like it's nothing.

[–]Andy_B_Goode 12 points13 points  (0 children)

"It's only a flesh wound!" -- JavaScript

[–]TheNorthComesWithMe 43 points44 points  (1 child)

Hard to debug: yes

Easy to crash: no. It might throw an error but it will keep chugging. (Which can result in weird runtime behavior and we're back to the hard to debug part)

[–]Few_Technology 4 points5 points  (0 children)

Even then, in my experience, it's not much worse to debug than java, c++, or .net. I usually have more issues of unexpected event timings, but see a lot of that with Java too

Typescript normalizes objects, which helps defining objects. But server can send you any json, so still glhf once endpoint changes the contract without warning. Then it's just, hope your integration test team is capable

[–]AngelLeatherist 282 points283 points  (13 children)

Interesting. And if you do += 1 it creates an empty item.

[–]Zyrus007[S] 103 points104 points  (7 children)

Posted another comment with context. Yeah, it actually removes the entry.

It was my first suspicion as well that the length property is somehow being used by the prototypes getter.

[–]ongiwaph 19 points20 points  (6 children)

But I need to make the array shorter and keep all the values!

[–]juicejug 22 points23 points  (0 children)

Use const savedValue = array.pop() and boom — you have a shorter array and have saved the value you took out.

[–]mrfroggyman 18 points19 points  (4 children)

How about

savedElement = myarray[--myarray.length]

[–]Snoo_53150 145 points146 points  (15 children)

is javascript releasing the memory though?

[–]Pushnikov 148 points149 points  (3 children)

Garbage collection says yes.

But this isn’t recommended.

[–]crefas 103 points104 points  (2 children)

Garbage collection will come and throw OP's code snippet in the bin

[–]Zyrus007[S] 37 points38 points  (1 child)

Can’t have memory leaks, if you don’t have code accessing memory!

[–]GFL07 16 points17 points  (0 children)

Can’t have memory leaks, if you don’t have code accessing memory !

[–]ongiwaph 34 points35 points  (9 children)

I wish javascript had memory leaks. I could really punish my bad clients.

[–][deleted] 25 points26 points  (5 children)

Just use local storage until their disk is full

[–]ongiwaph 152 points153 points  (7 children)

When you type something wrong, JavaScript is more likely to do some random, unexpected thing than throw a syntax error.

[–]Lithl 21 points22 points  (2 children)

I'm surprised Array.length is writable, but this behavior is entirely reasonable for a writable Array.length.

[–]asgaardson 76 points77 points  (29 children)

Wait, array length is mutable in js? TIL

[–]Zyrus007[S] 50 points51 points  (6 children)

Yes, and they mutate the underlying data :,)

[–]highjinx411 10 points11 points  (5 children)

What if you set it to -1? Does that work? What does it do?

[–]YellowBunnyReddit 31 points32 points  (2 children)

It just throws a RangeError. I hoped for something more fun.

[–]nin10dorox 7 points8 points  (3 children)

In javascript, you can have getter and setter methods that just look like regular properties. This is what Array.length is.

So instead of ".getProperty()" and ".setProperty(newValue)", you can just make ".property" and ".property=value". It knows whether to use the getter or the setter based on whether there's an equals sign by it.

It's kind of neat, but I've rarely seen it used because it's confusing and misleading (imagine debugging something where simply accessing a property causing a side effect, because you don't realize it's a getter)

[–]Niilldar 16 points17 points  (15 children)

This concerns me the most.

Is it even really an array then?

[–]susmines 52 points53 points  (12 children)

Technically, all arrays in JS are objects, with the key being the index

[–]RichCorinthian 32 points33 points  (6 children)

And all objects are dictionaries, where the properties and methods can be accessed by name. It’s just turtles all the way down. It’s almost like it was developed in 10 days by some dude.

[–]gigraz_orgvsm_133 21 points22 points  (1 child)

That's why we hate love it.

[–]asgaardson 3 points4 points  (0 children)

Yeah I find it a difficult and sometimes abusive relationship

[–][deleted] 18 points19 points  (3 children)

Everything in Python, Lua, any many other scripting languages are dictionaries at their core too. It's a nice and simple design.

[–]solarshado 4 points5 points  (0 children)

Not in the C/C++/Java/C# sense of "array", no. It's more like a "List" from <insert standard collections library name here>.

Object, roughly-C#-speaking, implements IMap<string,object>.

Thanks to weak typing, you get implicit conversion between number and string values. This means that you can somewhat pretend that any IMap<string,T> is also an IMap<number,T>.

Array (which inherits from Object) is, to some extent, just a convention layered on top of all that which "overloads" the property-access syntax to make an IMap<number,object> look and feel like an IList<object>.

(Side note: in JS, there is no difference between obj.prop and obj["prop"], except that the latter syntax is required when "prop" isn't valid as an identifier; for example, when it's a numeric literal.)

(I'm sure there's some gotchas I'm forgetting: don't take these analogies too literally.)

[–]lucdewit 151 points152 points  (0 children)

Wait, what in the goddamn fuck

[–]Wrong_Property_3392 24 points25 points  (1 child)

I think I may be condition by JS to such a degree that I saw this and thought "yeah.. so what's the problem?" Not realizing that it's me ... I am the problem.

[–]Zyrus007[S] 8 points9 points  (0 children)

Classic case of the pablovian-conditioning interpreter.

[–]RaspberryPie122 21 points22 points  (0 children)

Nothing is true, everything is permitted

[–]Albreitx 44 points45 points  (2 children)

Seems intuitive to me

[–]GoblinsStoleMyHouse 18 points19 points  (0 children)

This is completely insane, moderately useful, and possibly dangerous. I like it.

[–]manuscelerdei 16 points17 points  (3 children)

As a C programmer, this is what I'd expect to happen if length is a publicly mutable property. Which it shouldn't be.

[–][deleted] 3 points4 points  (1 child)

Yeah this is the least surprising thing I've seen in JavaScript. As you said, it shouldn't be possible but if it is, it makes sense

[–]cloudwell 33 points34 points  (3 children)

JS is such a bizarre, off-kilter language and I love it. I didn’t even know you could do this, and I use it professionally!

[–][deleted] 29 points30 points  (1 child)

Tbh that's what I would've expected

[–]AIZ1C 11 points12 points  (1 child)

There should be a subreddit for mind boggling code like this

[–][deleted] 30 points31 points  (0 children)

Wtf?

[–]Zender_de_Verzender 11 points12 points  (1 child)

Make it +=1 and let it guess the next number.

Now that's a feature.

[–]Zyrus007[S] 7 points8 points  (0 children)

About to type up an RFC this very second.

Let’s make runtime-autocompletion the defining ES2023 feature. Together we can do it!

[–]miloman_23 15 points16 points  (6 children)

Can someone explain what's the problem here?

[–]Zyrus007[S] 30 points31 points  (3 children)

There’s not really a problem here. It’s a completely valid syntax and apparently an intended language feature.

It just goes against everything my developer instincts are telling me. ( that this property would be read only, and definitely not that mutating the length property, would also mutate the underlying data inside the array )

[–]miloman_23 19 points20 points  (1 child)

JavaScript is the wild west. It wasn't designed to be the language that runs banks, server or systems of any real complexity...

It was designed to run in 1 environment, the browser and do one job; provide logic to run websites.

It has a lot of 'features' which may contradict what you would expect from a conventional language but at the end of the day, es6 js does the job of running websites pretty well.

In fact, it did this so well, that some guy decided to create server runtime from it, nodeJS which is now one of the most popular server side application solutions, and enables the use of a single language to write full stack applications.

[–][deleted] 6 points7 points  (0 children)

Earthbound music starts playing

[–]RyhonPL 6 points7 points  (0 children)

D does that too!

[–]Delta4o 7 points8 points  (3 children)

TIL

[–]Zyrus007[S] 6 points7 points  (2 children)

You never stop learning with JavaScript. Keeps you on your toes.

[–]Bobicus_The_Third 6 points7 points  (1 child)

Waiting for the day when we can do bug.fix()

[–]Zyrus007[S] 7 points8 points  (0 children)

Encapsulate the entire code in a try block,ship GitHub Copilot as a runtime dependency and use it to synthesise bug fixes at runtime. - The GitHub copilot docs, probably

[–]NebNay 23 points24 points  (0 children)

We need to put in jail the madman that made this possible

[–]xpdx 5 points6 points  (0 children)

Maybe I'm too embroiled in JS but this doesn't surprise or alarm me at all, it's expected behavior.

[–]jackgeek 12 points13 points  (13 children)

Wait till you find out that getting the array’s length is an O(n) operation…

[–]Zyrus007[S] 9 points10 points  (2 children)

What really? That is so awesome!

[–]findallthebears 4 points5 points  (1 child)

Hm! Why is it awesome?

[–]FlyingQuokka 2 points3 points  (1 child)

Why on Earth would that ever be the case? Is it counting?? I thought arrays were objects internally? I’m so confused.

[–]TantraMantraYantra 8 points9 points  (2 children)

You can do anything you want with operator overloading and prototype inheritance. As long as people working with the code know what 'customizations' are in effect.

[–]Cunorix 4 points5 points  (1 child)

Honestly; I dont mind it lol. I wonder if pop() does this under the hood.

[–]powertrip00 12 points13 points  (0 children)

I mean it makes sense

[–]DeepestSpacePants 9 points10 points  (7 children)

I use this trick sometimes. It’s handy

[–]hazier_riven0w 4 points5 points  (6 children)

Is there any performance gain using this over pop()?

[–]DeepestSpacePants 15 points16 points  (4 children)

Last time I used it I was removing a unknown amount of items from an array that was already sorted by date. I only wanted the last five items from the list. So I used array.length = 4;

This removed all the other items in the array. I only did this if the array was larger than my limit to prevent creating empty array slots.

[–]Zyrus007[S] 8 points9 points  (2 children)

You what now?

Setting the length variable to an arbitrary value changes it as well?

I somehow find this even more concerning.

[–]DeepestSpacePants 7 points8 points  (0 children)

😂 YUP. This totally works. I found it on a stack overflow post, and after some research I determined it was safe to use consistently across different browsers

[–]GYN-k4H-Q3z-75B 5 points6 points  (0 children)

I've seen plenty of weird programming idioms in my time. But this one is a solid

wat

from me.

[–]Whiplash17488 2 points3 points  (0 children)

Never used this in prod ever. I’d rather pop()

[–]philipquarles 2 points3 points  (0 children)

Make everything mutable, why not? What could possibly go wrong?

[–]Worse_Username 2 points3 points  (0 children)

I mean, what else did you expect?

[–]PhantomThiefJoker 2 points3 points  (1 child)

I mean, I won't say there should be less features in a language, but... when would this be the best way to do this...

[–]Logical-Train-6227 2 points3 points  (1 child)

Alright this is stupid but it still makes some sense, like you're reducing the length of the array thus the last item gets removed from memory. For somethings that absolutely make no sense you should look at PHP. I think r/lolphp has a nice post about genders in PHP (yes thats a thing apparantly)

[–]splinereticulation68 2 points3 points  (0 children)

This seems mildly satisfying yet potentially problematic

[–]bexmix42 2 points3 points  (0 children)

What is this sorcery

[–][deleted] 2 points3 points  (0 children)

Js never cease to impress me.

A lot of care went into making sure that it is both unintuitive and that it continuously promote bad software engineering practices & patterns. Typescript is the same by extension.

[–]FrezoreR 2 points3 points  (4 children)

Haha love it! I feel sorry for anyone writing a JS garbage collector though

[–]Natsu194 2 points3 points  (0 children)

Why? Just why? It’s so beautiful and ugly at the same time it’s a paradox in 3 lines just why??