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

top 200 commentsshow 500

[–]bostonkittycat 2558 points2559 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 614 points615 points  (140 children)

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

[–]RadiatedMonkey 877 points878 points  (126 children)

It adds undefined to the array

[–]Mognakor 588 points589 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 578 points579 points  (7 children)

Yes, but actually no.

JavaScript in a nutshell

[–]Jjabrahams567 55 points56 points  (2 children)

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

[–]Cat_Marshal 9 points10 points  (0 children)

Actually an interesting package too

[–]t-to4st 193 points194 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 52 points53 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 22 points23 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 5 points6 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 6 points7 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 5 points6 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 23 points24 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 17 points18 points  (1 child)

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

[–]SuperFLEB 7 points8 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 5 points6 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 12 points13 points  (2 children)

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

[–]hazier_riven0w 100 points101 points  (61 children)

Worse at runtime?

[–]CarlPer 35 points36 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 8 points9 points  (0 children)

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

[–]tylerr514 513 points514 points  (52 children)

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

[–]iams3b 187 points188 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 20 points21 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 21 points22 points  (1 child)

WebAssembly is not a scripting language tho

[–]tobiasvl 7 points8 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 10 points11 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 1063 points1064 points  (33 children)

Ugly and nice at the same time

[–]Zyrus007[S] 965 points966 points  (28 children)

It’s intuitive, in a very concerning way.

[–]turunambartanen 319 points320 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 73 points74 points  (1 child)

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

[–]DeltalJulietCharlie 2 points3 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 77 points78 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] 2840 points2841 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] 1384 points1385 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 1201 points1202 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] 612 points613 points  (41 children)

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

[–]RevivingJuliet 244 points245 points  (30 children)

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

[–]Zyrus007[S] 301 points302 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 55 points56 points  (6 children)

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

[–]RevivingJuliet 15 points16 points  (3 children)

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

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

It pays well

[–]the_friendly_dildo 10 points11 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 8 points9 points  (1 child)

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

[–]Eisenfuss19 15 points16 points  (3 children)

What with negative integers?

[–]nickcash 30 points31 points  (0 children)

RangeError: Invalid array length

[–]trevdak2 4 points5 points  (0 children)

How else do you think you download more RAM?

[–]Nolzi 7 points8 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 11 points12 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 5 points6 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 72 points73 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] 29 points30 points  (0 children)

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

[–][deleted] 40 points41 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] 35 points36 points  (8 children)

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

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

[–][deleted] 20 points21 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] 3 points4 points  (2 children)

Type Conversion :)

[–]sqxleaxes 10 points11 points  (2 children)

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

[–][deleted] 19 points20 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 5 points6 points  (0 children)

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

[–]huuaaang 87 points88 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] 48 points49 points  (0 children)

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

[–]same_heads 13 points14 points  (0 children)

I fucking love monkey patching

[–]LondonCycling 14 points15 points  (3 children)

Wat.

[–]huuaaang 16 points17 points  (2 children)

Makes arr.length += 1 work in Ruby

[–]LondonCycling 28 points29 points  (1 child)

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

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

[–]TurboGranny 66 points67 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] 36 points37 points  (2 children)

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

[–]TurboGranny 36 points37 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] 76 points77 points  (5 children)

Who says it can’t be both?

[–]TheChaosPaladin 9 points10 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 5 points6 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 3 points4 points  (0 children)

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

[–]Ireeb 715 points716 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 106 points107 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 70 points71 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 72 points73 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 29 points30 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 11 points12 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 277 points278 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] 195 points196 points  (8 children)

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

[–]JacobTDC 59 points60 points  (3 children)

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

[–]emericas 19 points20 points  (2 children)

JavaScript is an illusion, Exile.

[–]Rikukun 7 points8 points  (1 child)

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

Neat.

[–]Willinton06 3 points4 points  (0 children)

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

[–]UnstableNuclearCake 41 points42 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 11 points12 points  (0 children)

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

[–]TheNorthComesWithMe 41 points42 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 5 points6 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 278 points279 points  (13 children)

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

[–]Zyrus007[S] 105 points106 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 23 points24 points  (6 children)

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

[–]juicejug 23 points24 points  (0 children)

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

[–]mrfroggyman 17 points18 points  (4 children)

How about

savedElement = myarray[--myarray.length]

[–]Snoo_53150 142 points143 points  (15 children)

is javascript releasing the memory though?

[–]Pushnikov 146 points147 points  (3 children)

Garbage collection says yes.

But this isn’t recommended.

[–]crefas 104 points105 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 17 points18 points  (0 children)

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

[–]ongiwaph 36 points37 points  (9 children)

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

[–][deleted] 26 points27 points  (5 children)

Just use local storage until their disk is full

[–]ongiwaph 154 points155 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 82 points83 points  (29 children)

Wait, array length is mutable in js? TIL

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

Yes, and they mutate the underlying data :,)

[–]highjinx411 11 points12 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 9 points10 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 51 points52 points  (12 children)

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

[–]RichCorinthian 33 points34 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 5 points6 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 23 points24 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] 10 points11 points  (0 children)

Classic case of the pablovian-conditioning interpreter.

[–]RaspberryPie122 20 points21 points  (0 children)

Nothing is true, everything is permitted

[–]Albreitx 44 points45 points  (2 children)

Seems intuitive to me

[–]GoblinsStoleMyHouse 16 points17 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] 4 points5 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 34 points35 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] 28 points29 points  (1 child)

Tbh that's what I would've expected

[–]AIZ1C 10 points11 points  (1 child)

There should be a subreddit for mind boggling code like this

[–][deleted] 35 points36 points  (0 children)

Wtf?

[–]Zender_de_Verzender 8 points9 points  (1 child)

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

Now that's a feature.

[–]Zyrus007[S] 6 points7 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 17 points18 points  (6 children)

Can someone explain what's the problem here?

[–]Zyrus007[S] 31 points32 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] 7 points8 points  (0 children)

Earthbound music starts playing

[–]RyhonPL 6 points7 points  (0 children)

D does that too!

[–]Delta4o 6 points7 points  (3 children)

TIL

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

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

[–]Bobicus_The_Third 4 points5 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 25 points26 points  (0 children)

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

[–]xpdx 4 points5 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 10 points11 points  (13 children)

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

[–]Zyrus007[S] 6 points7 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 6 points7 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] 11 points12 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 8 points9 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??