@@jnoded Those are values defined by the floating point spec all processors implement. Any language that has a floating point type has to support them.
@jnoded in other languages is "spelled" `-0.0`, JS simply drop `.0` and handle every thing as float until you force to` int` using some thing like `x | 0`
Yeah, the real problem here is that JS doesn't really have integers. It has floating point numbers and then pretends really hard that they are integers.
Hey Theo! The creator of the wtfjs repo here 👋 Thank you so much for making a walkthrough of my examples. I really enjoyed watching it, and I think we should definitely add a link to this video in the README. You’ve made it both entertaining and interesting to watch. P.S: I noticed that you decided to cut out the repo banner asking for support for Ukraine. As someone still living in Ukraine, I experience the daily realities of missile and drone strikes, electricity outages, and all the other hardships that come with war. I, along with all Ukrainians, still need the community’s support.
I did not mean to cut out the banner I just trimmed up the page to make it readable on my 720p screen! Donated $1k to Ukraine a week ago and will add a link in description to your preferred Ukrainian charity if you dm it to me (yt eats links)
the reason -0 exists is because of IEEE 754. it makes sense, Floating point is an approximation of the real number line. -0 represents that the very small number we want to represent is negatice
+0 and -0 in floats are more like right and left limits. Floats simply don't have *real* 0. Real 0 inversed is always undefined (in a NaN sense, not js-undefined sense) while left and right limits are inversions of infinities
@@PaweMateuszBytner the reason why there's -0 is because unlike integers in 2's complement, the mantissa in a floating point type, is stored as an unsigned integer, and there's a separate bit for sign. So, if you have a bit pattern for zero, then if you just flip the sign bit, well, you have a bit pattern for -0 (-0.0, actually). CPUs will take care of it, tho, and will compare as equal one to the other.
Floats represent the *affinely* extended real number line which includes ±0 and ±∞. This was intentionally chosen over the *projectively* extended real line where 0 and ∞ do not have a sign, because every possible number having a sign makes hardware implementation easier, and it also makes the entire space of representable numbers completely symmetric around 0, unlike signed integers which have one extra negative value.
I'm absolutely baffled that people expect "false" to be falsy. That's like expecting the string "JavaScript" to be a full Javascript interpreter. Just because you type out the characters doesn't mean it should behave the same way when represented as a totally different data type.
Yeah that’s a more slight-of-hand situation if the reader isn’t paying attention. If you are paying attention, it’s more of a silly joke. Shouldn’t be in this list as a “wtf” situation, at least not one to be taken seriously.
@@paultapping9510 no, false is indeed falsey (as are 0, “”, and the ones you mentioned) We’re dealing with the STRING “false” which is truthy just like any other string (except empty string)
Exactly. But also, the author of the document. I find it weird that they dwell so long on `NaN` not being equal to itself. Is as if no one has elementary computer org classes anymore. JavaScript makes it a bit quirkier because all of the automatic promotions/coercions between objects, arrays, numbers, and booleans, then somewhere some silent `NaN`s pop up and act weirdly. But the rest of the `NaN` behavior is standard across the board on any language.
@lightning_11 you can watch 10min yt video it's not magic all operations on floats done by cpu directly (eg. someone designed an electrical circuit to do math) it's a lot simpler than any js or py library
The reason for NaN not being equal itself is that, if it was equal, then this would be true: Math.sqrt(-2) == Math.sqrt(-3) [NaN == NaN] where it is obviously false.
Math.sqrt(-2) and Math.sqrt(-3) have different Complex values, but both cleanly map to the same IEEE 754 value. It's no different from 1e30 + 1e-30 comparing as equal to 1e30 if you ask me. An unavoidable consequence of mapping the uncountably infinite set of Complex numbers to a mere 2^64 distinct values.
@@DimkaTsvThis is actually mathematically correct behavior because Math.sqrt(-2) is not defined (in the real numbers) so there is no equality to anything
Well, yes, but also, isn't the whole point of using a high-level language to prevent coders from doing stupid shit. JS doesn't seem to fulfill that, does it. Not that it's surprising or anything.
@@Jiyoon02 "isn't the whole point of using a high-level language to prevent coders from doing stupid shit" I would argue no. it shouldnt prevent you from being stupid
Honestly, almost all of these weird things come down to the same root cause: type coercion. JS is just far too willing to coerce types, so if you code in a way that avoids it, you'll probably be fine; explicitly convert types yourself, don't rely on JS to do it for you. Or, you know, use TypeScript.
Throwing stuff in strings in general (vs a more fitting type) has always given me the heebie-jeebies, and it seems like JS is super eager to do just that.
@@seanchen9771 Just rely on the every single person in the team of people you work with throughout time to be this way and your codebase won't be awful? I'd rather not.
@@zanebartlett8004 what you gonna do about it. Rewrite the whole codebase? I mean people throw objects in for boolean checks every now and then right? If the project youre working on started with js you are most likely stuck with it and most people would not do obscure things mentioned in this video. If it happens it happens there is nothing you can do about it.
@@carlpittenger Any scripting lan guage that follows the ECMAScript spec (i.e. ActionS cript and JScript) represents Numbers as 64-bit floating points. not just JavaScript.
@@dealloc jscript is dead and no one uses actionscript anymore. i'm obviously in this context meaning (and in fact most people in any context mean) ecmascript implicitly
The worst language ever made is MUMPS. Even if you try to create a garbage language, unless you took inspiration from MUMPS, it’s unlikely you’re making something worse. But the actually worst part of MUMPS is that there’s real production code out there. It’s not a toy language, it’s the _Massachusetts General Hospital Utility Multi-Programming System._
Example MUMPS HelloWorld: W "Hello world,! Q Terminating strings is optional. Spelling out keywords is optional. Also, there are no keywords, all keywords are valid identifiers.
"Why does -0 exist?", "Why is NaN so weird?", etc most of the weird things pointed out here are just actual parts of IEEE 754. IEEE 754 is what we usually call the floating point spec but its actually more than that its the floating point arithmetic spec and as such includes a bunch of things that are weird or confusing if you dont work on a very low level with floats. Things like NaN, -0, infinity and -infinity are all values that IEEE 754 have defined for good reason and their weird behaviours follow therefrom. NaN should not equal NaN since NaN is more of an error state than an actual value and are in spec returned from any "invalid" arithmetic operation (0/0, infinity * 0 that kinda thing). This is good! This makes float arithmetic algebraically complete that is every operation returns some value even if that value is kinda nonsense. This also means that there are multiple ways of getting NaN hence why it would make no sense for NaN to equal itself. Most other "weird" operations with these values have similar justifications. In my opinion the fault isn't in the fact that these values behave like this. They should behave like this there is a good reason they do and they don't (or at least they shouldn't but who knows what happens once JS type coercion is done destroying everyone reasoning abilities) show up in most code because they are edge case definitions. In general these values are very low level constructs so you could even argue about whether or not JS should even expose these directly instead of handling them in a more language appropriate way within the engine itself but it's too late for that now so. If you want a better understanding of why IEEE 754 is like it is go read the "Design Rationale" part of it's Wikipedia page or better yet go read the spec itself! It's a very interesting topic if you like these more low level things
IEEE 754 really is one of those specs that makes perfect sense to maths people but little sense to laymen. Still, it has stood the test of time because it's actually very mathematically coherent. One of the best IEEE ever made tbh
@@0106johnny Actually, I think a lot of math people would expect numbers to have an equivalence relation that upholds the reflexive property, and would also recognize that 1/(1-1) is neither positive nor negative. While it may be useful to treat 1/(1e-300*1e-300) as positive and 1/(-1e-300*1e-300) as negative, the subexpression (1e-300*1e-300) present in both should be recognized as distinct from (1-1), with the latter being neither positive nor negative.
Regarding the empty slots in array: They denote a missing index it's not a different type of `undefined`. Say you have the array `const sparseArray = [/* empty */, "1", 2]`, then you can check if a slot is empty by doing `0 in sparseArray` if the array doesn't have the property `0` it indicates an empty slot in index position `0`. The reason why the return type is a normal undefined is because if you access an attribute that's not there you simply get `undefined`. For example `const obj = { a: 1 }` then `obj.b` will yield `undefined`. Just like `sparseArray[0]` yields `undefined`. You are accessing a property that doesn't exist. If I would do `delete sparseArray[1]` then index `1` is now also an empty slot.
Other programming languages do a better job at keeping these weird parts of the spec from directly impacting a programmer. Usually by not having floats be the only or even the default way to handle number. Especially high level languages tend to keep that low level weirdness away from the things the programmer has to personally worry about
I've used non-IEEE floats because they needed to be done in software and worrying less about edge cases was more performant (I assume, IDK it was a library from I think the compiler vendor). I wouldn't call the spec broken, though. Maybe they're a disgruntled microchip engineer who could make division faster if it weren't for this danged spec, or something.
Honestly, I feel these issues are overblown by people who dislike javascript for entire different reasons. Most of these boil down to "don't use operators on non-primitive types". I genuinely don't remember last time I would trip on any of these in last 15 years writing JS.
As someone who doesn’t use Js these seem a pain to deal with or could cause completely stupid bugs if you ain’t paying attention like what on earth is going on here 30:47
> Why is `Number.MIN_VALUE` the smallest _positive_ value? Because the smallest _negative_ value is `-Number.MAX_VALUE`. As simple as that. Again, simple floating point practices, common to all languages I am aware of. The ways how JavaScript treats floating point numbers is actually *consistent* , even though they may look weird and illogical (and appear "truly JavaScript-y").
"OMG this language is SO CURSED 🤣" 33% of the time: type coercion failures 33% of the time: misunderstanding IEEE 754 33% of the time: trying to do stupid shit that wouldn't work in any language (1 < 2 < 3 is sinful) 1% of the time: actual poorly-implemented JS edge cases
@@BetaDude40 1 < 2 < 3 is, like.. either it works as it feels (1 < 2, 2 < 3) or it works as it should work (true < 3). These people are saying all logic errors due to language misunderstandings are on the language itself! that's like saying that it's on chinese that it has tones!
12:50 Actually java also uses the to string. BUT ONLY WHEN ADDING SOMETHING TO A STRING. WHEN ADDING TO OBJECTS IT JUST TYPE ERRORS LIKE WHAT A NORMAL PROGRAMMING LANGUAGE WOULD DO.
I thought the double not operator converts values to their boolean equivalent so !!"false" would be true because a string is truthy. !!"" would be false because an empty string is falsy. Same thing for the !!null, that would be false because null is falsy. The explanations around all the double nots seem to be convoluted at best.
No, the single bang (unary logical negation) not only coerces the value into a boolean, but also negates the operand after coercion. This is boolean logic and it is equivalent to `Boolean("string") === false`. Applying two bangs just negates the negated value i.e. `(Boolean("string") === false) === false` It's the same for unary plus/minus operator (+, -). It coerces into a number like (Number("123string")) and evaluates it. So -`"-12" === 12`. This is different from the addition operator, which will coerce both operands into primitives before evaluating the expression. If one side is a string, the other is coerced to a string as well. Otherwise if both are numbers it will perform an addition.
12:00 "Why is the smallest *_positive_* number? Why? I don't know what the use of that is" Clearly Theo has never dealt with the _joy_ of handling floating point numbers before
These kind of videos serve mostly to show off the ignorance of the person looking at the examples. In the nineties and naughties, people had this silly idea that languages shouldn't trigger error messages when people did something wrong, but instead try to do what the programmer intended. I think that was a reaction to the cryptic core dumps and errors of languages of the seventies and eighties. People thought error messages was the problem so they avoided them. This is common to all languages of that era, JavaScript, PHP (which has exactly one good feature), Ruby, Python, CoffeeScript, jQuery... Some are worse than others, but all are infected with that mindset. And then people usually go on to complaining about floating point arithmetic to just _really_ show off their ignorance.
I agree, error messages are stupid, if I type code then I want that code to run, not for a compiler to think it knows what I want better than me, Windows should want people to be able to break their computer, then I'd have to buy a new one, more money to Microsoft as a result. The worst error messages are the ones that try to stop you from performing low-level memory manipulation, of course I suspect this is largely because Windows probably doesn't really want you doing anything outside of the virtual memory it gives you, but Windows should keep its mouth shut and do as I tell it to do, if that means wiping my computer's memory then so be it.
@@flameofthephoenix8395there's a middle ground. Allowing the programmer to do whatever they want so long as the command makes sense, but not guessing at what the command means when it doesn't make sense. Letting you fuck up your own machine, and just throwing something into a string when you aren't sure what the programmer wanted are two very different things
@@GhostGlitch. I'm glad we agree that I should be allowed to break my machine if I want to, and I don't see why the compiler would go out of its way to try to interpret the meaning of non sensical code in the first place, so we agree on that too. Those array out of bound errors are particularly obnoxious, how are people supposed to read garbage data if the language itself is working against them? If a programmer fails to put in safety checks that is their problem, all the out of bounds checks do is prevent some people from reading outside of the array, slow down the program with unnecessary checks, and dampen the speed at which beginners learn the importance of not screwing up and accidentally reading garbage data when the program shouldn't.
@@FurryDanOriginal A separate operator for concatenation: . I despise languages for using + for string concatenation, addition and concatenation are not similar in any way. Multiplication is closer, but still obviously silly. Some use ++ which is at least different, but not as good as PHP.
@@kartikjha833 You have explenation on video for example you shoud always set type of parsing as second argument, because not all runtimes have it the same
@@itszzupart6607 He didn't get things wrong, he was "shocked" multiple times over things he didn't know. His fundamental understanding of JS is low. JS being a very messy language shouldn't come as a surprise to anyone with a couple of years experience, and you should know these things and the reason why it works the way it does.
thank you for inspiring me to watch actual coding channels who know what they're talking about and also to join your nemesis as a member, he needs more funding to do what he's doing for the community :p
Theo, you are uniquely talented in both your ability to understand an process the examples quickly, and in not getting bogged down (as I believe most people would) in the endless details and potential rabbit trails that present themselves along with the seemingly inexplicable results. If I were to walk through this list of examples it would take me hours to complete the journey.
Your comments - especially the resignation and despair - were absolutely hilarious. Thank you... :D And just so you know, this is how I sometimes feel while writing PHP. xD You are not alone.
A common misconception that I try to teach students when learning about floating point is that 0.0 and -0.0 (represented in IEEE 754 as the hex strings 0x00000000 and 0x80000000) are not actually equivalent to the true value of 0, but instead they're ranges of values. 0.0 can represent a number anywhere between 0 and about 1e-127 inclusive, while -0.0 can represent anything from -1e-127 to 0. Thus we should treat 0.0 as "0 or any arbitrarily small positive number" and -0.0 as "0 or any arbitrarily small negative number." This is just a consequence of the fact that the sign bit used in IEEE 754 doesn't matter if you're considering only true 0 since true 0 is unsigned and integer implementations skirt around it with 2's complement. But the reason why we still use this is because knowing whether you have a positive or negative arbitrarily small value is very important for calculations, especially multiplication or division (though if you're getting numbers whose magnitude is so arbitrarily small then you really need to rework your code to avoid the catastrophic cancellation in the first place or use epsilons to force expected behavior)
15:57 sometimes I think that He doesn't know what he talking about ) so, basically what happened, first array is empty and when you try to access nonexisting key you will get undefined the same that you store in second array under existing key you can not get "empty" value from array
"the falseyness occurs before the number coercion...why?" I think you already know why, it's just about evaluation order. The real questions are why was it decided that the empty array is truthy and why was it decided that == coerces things to numbers.
The question is why it is named MIN_VALUE when it is not actually the minimal value. (And no, it's not epsilon; epsilon is the difference between 1 and the next larger exactly representable value.) However the problem is not unique to JS, it is already in C, where INT_MIN is the minimal value of int (and therefore negative), while FLOAT_MIN is the minimal positive value of float (and thus positive).
I think just renaming it to "LEAST_ABS" or something would be a bit more intuitive to what it means, but this is also a quirk of the English language. "What is the smallest number?" could either mean 0 or -infinity depending on how you interpret that statement, because -infinity < 0 but abs(-infinity) > abs(0)
The only reason I watched this was to hear someone else explain the concept of the minimum of an empty set being ∞ and the maximum being -∞. But you didn't. Dammit.
Defenses for NaN and -0: According to the specifications of float32 numbers (in all programming languages that use that type behind the scenes) can represent the value NaN in [2 to the power 23 (iirc) minus 1] ways. Logically, Not Valid Numbers cannot be equal to any computed value (0/0 is not equal to 0^0), therefore NaN != NaN. As for -0, in float32, the representation of -0 in bits is distinct from 0. So while -0 is not useful in practice, which is why it is hard to get it in implicit ways, it is a distinct value from 0.
I'm not a JS guru, but I'm using it for decades. For the vast majority of these quirks I never had a problem , simply by not relying on stupid syntax. I never use a string as a boolean value. Why would I? Even if I remembered the detailed implications, I would never expect the next guy working on my code to remember it. I don't use `+` to concatenate strings unless there is a good reason to. When I do, I start the expression with `"" + ...` to make sure the intention is clear. Why would I ever want to `str - number`? Why would I ever use an array in a scalar context? Knowing the semantics of `==` why would I ever prefer it over `===` unless I want the precise semantics of `==`, I can't remember such a use case. I don't remember the precise semantics of `==`, it's faster to express the semantics I need explicitly instead of looking up what `==` does precisely and burden my coworkers with the same task. I'm not stupid, neither am I malicious or irresponsible. I'm not saying JS is a great language. But for all practical purposes, it's by far not as bad as its reputation. Anybody who uses `false == "false"` to say anything about the language that is interpreting this code is not qualified. Such code should never have been written for any purpose other to condemn authors of such code to hell fire. However, if you are stupid, malicious or irresponsible, you can create the very same kind of confusion with any other language, Rust, Zig and Python included. Well maybe not the same kind, but the same headache resulting from where-ever your stupidity leads you to go.
Really this just shows that the mentality of "sensible defaults" rather than erroring is a terrible idea for language design and programming in general
Exactly, this is just a series of examples of the robustness principle in action. It makes sense in a way, that x = 5; console.log("your number is " + x); should work. Any beginner programmer would instinctively try that. It makes sense that !!"false" should equal true, because "false" isn't false. I think people would be far more annoyed if someone could input the string "false" into a form field and their check for `if (input.value)` would return false instead of true. If you're going to have type coercion, it makes sense that +"some string" should equal NaN. It's literally not a number. That's kind of what NaN is for. Everything is just a sneaky combination of these things. You can do that in every language. In python and lua, you can redefine `print` to whatever function you want. In java, you can do all sorts of weird stuff with boxed types. In typescript, you can define utterly incomprehensible behaviour within the type system. And don't get me started on C, which lets you edit parts of the stack that are in use by the program, access variables that are supposed to no longer exist, and treat the binary at any particular pointer as any type you want, even though it makes no sense.
@@zettca Well yeah. That's why Typescript exists. It's much easier to fix the result of an error than the result of a security flaw caused by implicit behavior.
12:00 This is an important concept in math. I was just reading about Kakeya’s Needle Problem today, and the goal in it is to minimize the area needed to sweep a needle of zero-width 180°. Various mathematicians found ways to minimize the area needed that approach zero at the limit, but the area can never actually _be_ zero. This would be a perfect application of Number.MIN_VALUE. The limit would be equal to it, and it would be greater than zero, for good reason. Meanwhile, negative infinity would be smaller than any number, and negative Number.MIN_VALUE would be the largest negative number before zero.
My experience is that my final code never contains these issues, to the extent I can manage, but I frequently stumble upon them during development and cleaning up the mess slows me down. JS has too many buried landmines and cursed sections of the language, it's unpleasant.
The fun stuff for NaN is equality in the simple sense of numerical value. If the result of a math operation is undefined, then you get NaN. By definition, this cannot equal any other number. Just like infinity cannot equal infinity. As you cannot make a comparison between their values. For 0, by definition -0 is a different object to 0. But their values are both 0, so if you compare them by value, even using the strict equality operator, the result is true.
4:00 x: "2" y: 2 z: "2.0" The strings evaluate each other as strings so they're false. But they both evaluate the integer value when comparing to the integer y.
Someone has probably already mentioned this, but JS actually can't take credit for the NaN != NaN thing. That's completely from the IEEE floating point spec. It's in basically every language and baked into the silicon.
Basically every NaN operation always just fails, because otherwise you could say Math.sqrt(-2) === Math.sqrt(-3) -> true. NaN is basically a placeholder for "this data representation is insufficient, math breaks down here, go find a new datatype if you really want to do this" and it wouldn't make sense for it to do anything but instantly fail out.
@@BetaDude40 Prior to the introduction of IEEE-754, I think it would have been obvious that programming languages should provide a transitive numeric comparison operator that can be used to sort any kind of numbers that could be produced as a result of numberic computation. I guess one could minimize the cost of dealing with NaN in a sort by partitioning data into "NaN" and "non-NaN" regions and only sorting the regions where all NaN values have been excluded, but that sitll seems like a lot of extra work that would be needed to prevent a sort routine from turning into a pile of goo if there are any NaN values in the data.
I feel like Theo is the kind of guy that if he ever used ActionScript3, he'd be stuck using it since its got most modern syntax features (E4X, JS typing, Embedding file) in an old language
15:09 is not about 3 different values for empty value, it's about setting length to 3, but array object have not keys "0" and "1" assigned. [undefined, undefined] assigns that keys to undefined. Array in JavaScript are just Objects with string keys and magical 'length' property.
About the new String thing: you can call the String and Number constructor with and without new: without new, it works just like you would expect, it converts the argument to the respective type, and typeof will give you a reasonable result. with new, you get a "String object" or "Number object", which is basically like a string or number, except that you can actually override functions and stuff, as you've shown with your "this is the wrong string" example.
you can even create unrelated properties on the String/Number objects if you created them with new: > s = "hello" 'hello' > s.a = 42 42 > s.a undefined > s = new String("hello") [String: 'hello'] > s.a = 42 42 > s.a 42 >
MIN_VALUE is used to add to division operations so they don't break. There are other uses too, like checking a range of values to prevent float precision errors.
I use Ruby, so here's something fun: 0.1 + 0.2 == 0.3 returns false, but 0.1r + 0.2r == 0.3r returns true. Explanation: "r" is a Rational literal. Rational is a number type which, when called via a literal like this, casts the preceding number to an Integer over another Integer, which means you get, in effect, 1/10 + 1/5 == 3/10, which is correct. Without it, values like 0.1 are assumed to be Float values, which follow the IEEE spec and so sum dumbly.
Javascript has a reason for most of its weirdness. The reason some design decisions are criticized is that much like required reading in highschool, if you work with the web at any point, you have to deal with javascript. People just grow resentful they have no choice to use it, and making fun of certain features become memes. Because of its position as the main programming language of the web, it can't really do breaking changes, and undoing certain core design desicions isnt feasiable. When it was created, its creator had no idea of the scope it would eventually cover, and since the community grew so huge, contributors had to balance giving everyone enough features so that everyone was only half angry. But yes it is fun to make fun of all the quirks of the language, just don't forget that there is context to all of the design decisions you don't love. Also python, get some brackets, ruby, not everything has to be magic, Php, you're cool now.
I agree with most things but some stuff is really simple, such as 3:07, and I feel like you may be overexaggerating this a bit. !!"false" == !!"true" being true is completely normal. A non empty string is truthy. The first ! makes it false, and the second flips the false and makes it true. There is nothing wrong with this, and yet you make it seem like it's very strange, and that we are in "hell". 4:57, When checking if a boolean is equal to a string, it obviously wouldnt return true. Now given my previous point, you might then confuse why !!"true" is true, and that is because ! converts it to a boolean. So it goes !!"true" -> !false -> true, which also explains 5:03. I wouldn't say that is "dumb". 9:20 Now I know this one isn't intuitive to understand, but I will try to explain it in a way that hopefully the confusion would go away. I believe the reason why null == false is false but 0 == false and "" == false is true, is because null is loosely connected to undefined (which is also why at 10:28, document.all == null is true, while document.all == undefined would also be true, but document.all == false is fasle). Undefined is when the value isnt assigned, and null is when its been assigned but has no value, and so there is no type coercion. Strings and numbers go through type coercion but null doesnt, since it's not really a type. All you need to remember is that there is no type coercion for null and undefined and that should make sense of this. People should not be told these are challenging or strange. You can make simple things become difficult by simply pretending that they are. Many people including myself look up to you, and so I think we would all appreciate it if you would actually pause to think about these things instead of hopping on the "Yep there it is, JavaScript is dumb".
It's convoluted BS that, in my opinion, hardly anyone uses and would cause unintended side effects. There is no need to use boolean negation on a string, it makes no sense and it just causes massive amounts of type confusion. It is not normal, in any sense, to boolean negate a string to a boolean value because what if you never intended said string to be a boolean, but simply a string with the word true or false? This is why I hate dynamically typed languages. They treat everything as a variant essentially which causes all these conversion side effects that you never intend.
@@jshowao Yeah I mean there is literally no reason anyone would be using double negation like that. I find myself using boolean negation on strings in situations such as: if (!node.textContent) throw Error(“Node has no content”) Or for double negation: function isValidName(name) { return !!name } But the above could also just be written as: return name != “”. For me, I think projects that I faced some side effects were the ones that I used a ton of APIs with varying and sometimes unpredictable response values. And even then, I never really found myself thinking JavaScript was the problem, it was just that I didn’t know enough. With what I know now, I would make fewer mistakes and likely waste little to no time dealing with these side effects. I think all of this JS hate is pointless, it’s still a very powerful language regardless. If you write good code, it would be easy to debug the side effects if they ever occur
@@jshowao It's actually normal to use negation on a string to check if it's empty or not in a dynamically typed language. That's what you would expect: an empty structure is falsy. Tho I prefer the C++ way, just a simple empty() mehod to check.
@@楊立慈-o3f Ive never see it done in a statically typed language. It would fail in C#, fail in Java, fail in C. You dont use a negation operator to convert a "false" or "true" string to boolean. You convert it to boolean through a cast or parse method, then use boolean operators on it. That makes sense because a string is not a boolean type. This way, Javascript just assumes the string value should be boolean and converts it as a side effect.
−0.0 exists because IEEE floats are implemented as sign-magnitude pairs, so with magnitude 0, you can choose a positive or negative sign. A different approach to floating-point numbers is, effectively, using INT_MIN as NaN and using 2’s complement for negatives. That format exists and is called Unum.
The reason it's treated as a distinct value is to make 1/(1e-300*1e-300) positive but 1/-(1e-300*1e-300) negative. Unfortunately, IEEE fails to recognize that both +/- (1e-300*1e-300) should be distinct from 1/0.0 or 1/(1-1), both of which should be NaN.
patching the Number prototype has a neat application: you can make numbers iterable (counting 0 to n-1), so you can "for...of" them or use them everywhere iterables can be used. i also added some forEach, map and reduce methods. for numbers that aren't positive intergers, the behaviour isn't quite so obvious but your also unlikely to use them for those. :P i get why expanding builtins is probably not something you should do in a professional setting, but it's a very nice feature to use and it may be a cool in other languages.
Please, don't make something like for (const i of 10), it doesn't make sense semantically, consider creating a range class and do for I of range(1, 10), or similar easier to understand intentions
Given how much of JS's weirdness is actually just IEEE754 abuse (like .1+.2 = .30..04). Go look up some talks on it!
5 місяців тому+5
NaN != NaN is part of IEEE spec and same in other languages. It makes sense since something like 0/0 == parseInt(“hello”) would be crazy, and make debugging harder. Classic frontend script kiddie to blame standard floating point behaviour on js ;)
Barring potential weirdo string conversion stuff, no comparison operator with NaN returns true other than NaN != x, where x is any floating point value, including NaN, which is about as close to NaN being non-comparable to other Numbers (including itself) as you can get without introducing null or undefined as a possible outcome. Basically this lets you do operations with "infinity" without having to worry about spitting out true when comparing two indeterminate forms that have different limiting values. The question comes down to, "How do you evaluate the truth value of something like (2x - 4) / (x - 2) == (x^2 - 4) / (x - 2) at x = 2?" The only way to reasonably do it is actually taking the limit of both sides (in this case 2 != 4). And you could do this programmatically with some effort and probably be right most of the time, but boy is it easier to just say, "Lol no."
@@Mystic998 With regular languages like JS you just don't look at any statement with comparison operators and treat it as an equation to solve, because those are just simple operators that are evaluated in particular order. You know, CPUs don't think and don't learn calculus. Solving equations is a task for specialised tools that either have actual concept of a limit and can approximate it numericaly or even can do symbolic math and give you general solutions. And indeed, in JS (and C# and any reasonable non-specialised language that does floats), you just say "lol, no". More accurately, you evaluate to NaN that can stand for undefined value
This stuff happens in any dynamic language where they decide you should be able to compare things that have different types and also that types should coerce automatically. The rules they decide usually make sense if you look at them in isolation of the original intent of the rule, but the resulting semantics is so broken that you can't rely on it because it quickly becomes mostly impossible to predict for a human. Basically showing that double equals shouldn't exist.
41:20 "this is huar stupid don't do it that way" This just makes sense because promises are monads and the point of monads is exactly this flattening or removing of multiple boxes of the same type. So this is actually good behaviour for promises
I have 2 uses for the double equal: - x != null // check if variable x is defined - x == y // check if values are equal, when you KNOW they are either a number or a string, like for checks with input values
best part is receiving bug reports that the app would randomly consume 100% cpu turns out it was a relative date component (i.e. turns new Date(0) into "54 years ago"), that updated in real time. turns out if the time until the next update (ie. 5.4 seconds ago is 600ms away from displaying "6 seconds ago") was greater than 24.8 days, or 2,147,483,647 ms it would call setState() or forceUpdate() or whatever it was in a loop
The String.split() behaviour is correct (at least the one shown here, it might do something weird in some other cases, it's JS after all): - There are empty strings between each character (conceptually), so splitting on "" must return an array of every character on the string. Since you are calling it on an empty string, that result will be empty. - Splitting by something not in the string should return nothing, because there's nothing to each side of that separator ( the case for "".split(" ") ). These are just edge cases, but the behaviour is logical.
There is a complicated table of how == equality works. But the truth is… you don't need it in reality, unless you're doing something strange. If you're comparing primitives they would be coerced to string or number depending on what you have passed in. So, it's handy to check for example == 2, when your backend may send "2" with no need of manual coercion Number('2'). I'd recommend to use strict comparing === to *true* or *false* , but that's a rare case, especially after defaults where introduced, and one can do something like function foo(bar = true) { if (bar) … // could be “if (bar !== false)” in the old days } Objects are compared their to strict identities between them, no matter are you using == or ===. [] != [] is same as [] !== [] and is true, since you're comparing two different freshly created arrays here. *null* and *undefined* are equal only to self, as already noted. So it's safe to compare anything with them. (Usually != null is handy and shorter.) The unexpected part begins when you're comparing any type of object to primitive, so it's a bad idea to do. Just don't do it. Why would you? Why would one compare *false* and an array? It's that case when explicit better than implicit. E.g. by using .toString(). But still it's quite a rare case too. Usually, objects compared to be same or being nullish. So, that WAT part is quite overhyped really. Yep, one cannot use + to concatenate arrays like in Python, [...array] spread or .concat() should be used for that. But you learn it when you learn basics about arrays and their methods. No big deal.
You can attach properties to String objects but not native string instances. I've used this for templating meta information for database string templates.
In the Godot game engine, a freed object == null, but is truthy. Assigning a freed object to a variable _sometimes_ throws an error, and a freed object inside a closure gets turned into a null. And of course you also need to check if an object is queued to be freed at the end of the frame but hasn't actually been freed yet. I think some of this is changing in 4.3.
undefined is very fun.. You cab delete object keys by setting them explicitly to undefined, but a key can exist with value undefined and be different to not existing on an object at all, despite always reading out the value as undefined
Half of this weirdness comes from it having the weakest ass type system known to mankind. Strict types somewhat force the rest of a programming language to make sense.
@theo deviding by zero is technically undefined (in mathematics) as assigning it a value would break maths ;) if you’d do that you could do stuff like reasoning 1 === 2 === 3 === 4 === … The intuitive way of assigning it infinity leads to infinity === -infinity - because it depends on from which direction you approach zero from. there is a nice numberphile video on that if you are interessted in the specifincs ;)
The difference between "truly not defined" and "undefined" is that "truly not defined" only exists in array contexts, and is defined by the given index of the array not existing. When you call a function and omit an argument, the argument is referenced as undefined, but you can tell whether it's undefined or truly not set by receiving ...args and checking the length.
If you don't get -0, then you can't understand floating point. It's not a JavaScript thing at all. All floats are approximations. They have limited precision. And sometimes it matters if the result you get is slightly less than 0 or slightly more than 0. NaN !== NaN makes sense, too. NaN is a result you should not get. If two expressions fail, you don't really want them to be equal to each other. And I get why "false" != false. It's normal to check if a value has been input by doing "if (textbox)". It would suck if that failed because the user typed in "false." The idea that any non-empty string is true makes sense to me. I only find it weird when the DOM coerces false or undefined into "false" and "undefined". That's dumb, and should be the same as assigning the empty string. And of course string- number equal not a number. There is no defined operation for subtracting strings. What else could it return?
This was some.. uhh..amazing(?)... things? Welp, I had 'fun' watching this video. Some of these things I knew, many I did not. The knowledge that this list is surely incomplete simply adds on to the 'joy' of javascript.
The reason for there being a NEGATIVE zero is simple, actually - if you add a sign to a value, you split it in half and get mirror sides (so 0..255 becomes -127..127). But you still have _256_ values, not 255. 0 is reflected on BOTH sides - an unsigned 0 (00000000) and a _signed_ zero (10000000). For all intents and purposes, -0 == 0. However, they are NOT the same value, because bitwise 0x00000000 *!=* 0x10000000.
I'm wondering... What if I define a toString method which does not return a string? How will the + operator work then? Just try it: const a = { toString: ()=> 1, }; const b = { toString: ()=> 3, }; a + b;
7:55 “I’m going to be a farmer” boy do I have a game for you to play… The Farmer Was Replaced is a farming game where you program a drone to do all the farming on your grid based farm
Wtfjs a single markdown README with paragraphs with at most three lines each + at most 10 line code snippets in between, well structured, almost none of the (paragraph,snippet) pairs share context. It takes 45 minutes to go through, if you know how 75% of it already. Truly, WTF JS
the let x, { x: y = 1 } = { x } thing is self-explanatory. first, u declare x which is undefined. then u redeclare it to be 1, and set it to be equal to x inside the object so u get 1 MAYBE JUST MAYBE i understood it wrong but i think its like this
After reaching 5 minutes in, I almost want to pull the transcript and see if he beats Limp Bizkit - Hot Dog's count of 46 fucks in this fucked up video.
The hell I went through coming from Java and C to javascript. The syntax didn't make sense at all simply because of those small unintentional bugs until I started working it it from the beginning forgetting all the programming concepts, learning the syntax and then reapply those concepts. For a person starting with JavaScript its okay but coming from a statically typed language, you will hate it.
What looks like a duck, quacks like a... You entered the JS world with the knowledge that is not completely transferable and applicable. Most devs with different backgrounds make assumptions about how JS works. When they get hoisted on their own petard, they hate the language. ECMAScript spec (and some other standards) addresses most if not all the weird stuff shown here. It is 2024, Theo's take on this topic is archaic.
I would assume (I haven't looked at the actual implementation) that values are stored in a way similar to this: struct Value { bool is_empty; enum { VAL_UNDEFINED; VAL_NULL; /* ... */ } type; void *value; } so when you set no value, it just sets is_empty to true, but when you set a value manually, it sets it to false, and then sets the type to undefined. I'm not sure why undefined doesn't just always set it to empty, or empty is just the undefined type, but maybe its because JS is old and people rely on it now.
The Math.min() behavior is imo perfectly reasonable. We want that the min of the combination of two sets is the same as the min of the min of the sets seperately, i.e: min(min(a, b), min(c, d)) = min(a,b,c,d) What does that mean for min()? (The min of the empty list): min(min(), min(a)) == min(a) = a The only value for min() that makes this work is +Infinity.
IIRC, NaN != NaN and -0 == 0 are part of the floating-point spec; most languages follow it.
most languages simply don't have NaN or -0 as far as I know
@@jnoded Those are values defined by the floating point spec all processors implement. Any language that has a floating point type has to support them.
@jnoded in other languages is "spelled" `-0.0`, JS simply drop `.0` and handle every thing as float until you force to` int` using some thing like `x | 0`
Yeah, the real problem here is that JS doesn't really have integers. It has floating point numbers and then pretends really hard that they are integers.
@@ruroruro I would not say "pretends" as if you try enough that you will get real `int`, other wise `asm.js` would not work.
Hey Theo! The creator of the wtfjs repo here 👋
Thank you so much for making a walkthrough of my examples. I really enjoyed watching it, and I think we should definitely add a link to this video in the README. You’ve made it both entertaining and interesting to watch.
P.S: I noticed that you decided to cut out the repo banner asking for support for Ukraine. As someone still living in Ukraine, I experience the daily realities of missile and drone strikes, electricity outages, and all the other hardships that come with war. I, along with all Ukrainians, still need the community’s support.
I did not mean to cut out the banner I just trimmed up the page to make it readable on my 720p screen!
Donated $1k to Ukraine a week ago and will add a link in description to your preferred Ukrainian charity if you dm it to me (yt eats links)
@@t3dotgg oh, got it. Sorry and thanks for your generous donation! DMed you on twitter.
the reason -0 exists is because of IEEE 754. it makes sense, Floating point is an approximation of the real number line. -0 represents that the very small number we want to represent is negatice
+0 and -0 in floats are more like right and left limits. Floats simply don't have *real* 0. Real 0 inversed is always undefined (in a NaN sense, not js-undefined sense) while left and right limits are inversions of infinities
@@PaweMateuszBytner the reason why there's -0 is because unlike integers in 2's complement, the mantissa in a floating point type, is stored as an unsigned integer, and there's a separate bit for sign. So, if you have a bit pattern for zero, then if you just flip the sign bit, well, you have a bit pattern for -0 (-0.0, actually). CPUs will take care of it, tho, and will compare as equal one to the other.
Floats represent the *affinely* extended real number line which includes ±0 and ±∞. This was intentionally chosen over the *projectively* extended real line where 0 and ∞ do not have a sign, because every possible number having a sign makes hardware implementation easier, and it also makes the entire space of representable numbers completely symmetric around 0, unlike signed integers which have one extra negative value.
-0 exists because of mathematics, not because of floating point.
@@skipfred bruh...
I'm absolutely baffled that people expect "false" to be falsy. That's like expecting the string "JavaScript" to be a full Javascript interpreter. Just because you type out the characters doesn't mean it should behave the same way when represented as a totally different data type.
Yeah that’s a more slight-of-hand situation if the reader isn’t paying attention.
If you are paying attention, it’s more of a silly joke. Shouldn’t be in this list as a “wtf” situation, at least not one to be taken seriously.
is that because false is a defined value and thus truthy right? the only falsy values are NaN, Null, and undefined?
@@paultapping9510 no, false is indeed falsey (as are 0, “”, and the ones you mentioned)
We’re dealing with the STRING “false” which is truthy just like any other string (except empty string)
Indeed, that was quite a shocker. JavaScript has a lot of unreasonable quirks but this is not one of them.
Knowing Javascript, I would've expected it to treat "false" that way just because it's dumb and nonsensical behaviour.
Ok 0*-1 =-0 is the funniest thing I have ever seen
Not a JS issue. It's called IEEE754
@@thomassynths regardless it’s funny 🤷🏻♂️
@@thomassynthsyeah, other languages just hide the negative sign when you print
i feel like its actually intuitive... 0 * positive int = 0, 0 * negative int = -0
@@nitsanbhno, every other language i know evaluates literal -0 to integer 0. but yes, 0.0 and -0.0 are different values according to IEEE
half off video is Theo doesn't understand floating point numbers
Exactly. But also, the author of the document. I find it weird that they dwell so long on `NaN` not being equal to itself. Is as if no one has elementary computer org classes anymore.
JavaScript makes it a bit quirkier because all of the automatic promotions/coercions between objects, arrays, numbers, and booleans, then somewhere some silent `NaN`s pop up and act weirdly. But the rest of the `NaN` behavior is standard across the board on any language.
To be fair, floating point is basically magic to even programmers sometimes... (Python dev here)
Does anyone really?
@@MaximNightFury Everything is basically magic to python devs. In fact, the entire language is basically magic. :| (I wish I was a python dev)
@lightning_11 you can watch 10min yt video
it's not magic
all operations on floats done by cpu directly (eg. someone designed an electrical circuit to do math)
it's a lot simpler than any js or py library
The reason for NaN not being equal itself is that, if it was equal, then this would be true: Math.sqrt(-2) == Math.sqrt(-3) [NaN == NaN] where it is obviously false.
But then isn't it allows for
Math.Sqrt(-2) == Math.Sqrt(-2) [NaN == NaN]
false
Where it should be true?
Math.sqrt(-2) and Math.sqrt(-3) have different Complex values, but both cleanly map to the same IEEE 754 value. It's no different from 1e30 + 1e-30 comparing as equal to 1e30 if you ask me. An unavoidable consequence of mapping the uncountably infinite set of Complex numbers to a mere 2^64 distinct values.
@@DimkaTsvThis is actually mathematically correct behavior because Math.sqrt(-2) is not defined (in the real numbers) so there is no equality to anything
I love how nearly all of these are impossible to do on accident if...
- You use TypeScript
- You don't do stupid shit
Well, yes, but also, isn't the whole point of using a high-level language to prevent coders from doing stupid shit. JS doesn't seem to fulfill that, does it. Not that it's surprising or anything.
@@Jiyoon02 I don't disagree-JS just had very naïve design goals w/ dynamic typing and such.
@@Jiyoon02 "isn't the whole point of using a high-level language to prevent coders from doing stupid shit" I would argue no. it shouldnt prevent you from being stupid
Well, I think the only one you can do on error is the sort function on arrays
Also the try/finally thing
Honestly, almost all of these weird things come down to the same root cause: type coercion. JS is just far too willing to coerce types, so if you code in a way that avoids it, you'll probably be fine; explicitly convert types yourself, don't rely on JS to do it for you.
Or, you know, use TypeScript.
Throwing stuff in strings in general (vs a more fitting type) has always given me the heebie-jeebies, and it seems like JS is super eager to do just that.
This comment right here. This. This. This.
just be somewhat aware of types and readability then all these weird code will not appear
@@seanchen9771 Just rely on the every single person in the team of people you work with throughout time to be this way and your codebase won't be awful? I'd rather not.
@@zanebartlett8004 what you gonna do about it. Rewrite the whole codebase? I mean people throw objects in for boolean checks every now and then right?
If the project youre working on started with js you are most likely stuck with it and most people would not do obscure things mentioned in this video. If it happens it happens there is nothing you can do about it.
"Ask stupid question, get stupid answer" - JavaScript truthy, probably
lmao
+0 and -0 is not JavaScript thing. This is feature of floa ting point numbers internal format which is used by CPUs and GPUs for a very long time.
That is in deed correct!
the 0 and -0 literals meaning 0.0 and -0.0, respectively, are, however, a javascript thing. even python got that right. hence bigint being tacked on
NaN != NaN is also a floa ting point quirk
@@carlpittenger Any scripting lan guage that follows the ECMAScript spec (i.e. ActionS cript and JScript) represents Numbers as 64-bit floating points. not just JavaScript.
@@dealloc jscript is dead and no one uses actionscript anymore. i'm obviously in this context meaning (and in fact most people in any context mean) ecmascript implicitly
This makes Dreamberd look credible
I hope you find the charity DB supports credible
The worst language ever made is MUMPS. Even if you try to create a garbage language, unless you took inspiration from MUMPS, it’s unlikely you’re making something worse. But the actually worst part of MUMPS is that there’s real production code out there. It’s not a toy language, it’s the _Massachusetts General Hospital Utility Multi-Programming System._
Example MUMPS HelloWorld:
W "Hello world,!
Q
Terminating strings is optional. Spelling out keywords is optional. Also, there are no keywords, all keywords are valid identifiers.
@@Seedwrecktheo certainly did in his review of the language, so idk what you're on about
@@plesleron
He did find Dreamberd credible
"Why does -0 exist?", "Why is NaN so weird?", etc most of the weird things pointed out here are just actual parts of IEEE 754. IEEE 754 is what we usually call the floating point spec but its actually more than that its the floating point arithmetic spec and as such includes a bunch of things that are weird or confusing if you dont work on a very low level with floats. Things like NaN, -0, infinity and -infinity are all values that IEEE 754 have defined for good reason and their weird behaviours follow therefrom. NaN should not equal NaN since NaN is more of an error state than an actual value and are in spec returned from any "invalid" arithmetic operation (0/0, infinity * 0 that kinda thing). This is good! This makes float arithmetic algebraically complete that is every operation returns some value even if that value is kinda nonsense. This also means that there are multiple ways of getting NaN hence why it would make no sense for NaN to equal itself. Most other "weird" operations with these values have similar justifications. In my opinion the fault isn't in the fact that these values behave like this. They should behave like this there is a good reason they do and they don't (or at least they shouldn't but who knows what happens once JS type coercion is done destroying everyone reasoning abilities) show up in most code because they are edge case definitions. In general these values are very low level constructs so you could even argue about whether or not JS should even expose these directly instead of handling them in a more language appropriate way within the engine itself but it's too late for that now so. If you want a better understanding of why IEEE 754 is like it is go read the "Design Rationale" part of it's Wikipedia page or better yet go read the spec itself! It's a very interesting topic if you like these more low level things
IEEE 754 really is one of those specs that makes perfect sense to maths people but little sense to laymen. Still, it has stood the test of time because it's actually very mathematically coherent. One of the best IEEE ever made tbh
@@0106johnny Actually, I think a lot of math people would expect numbers to have an equivalence relation that upholds the reflexive property, and would also recognize that 1/(1-1) is neither positive nor negative. While it may be useful to treat 1/(1e-300*1e-300) as positive and 1/(-1e-300*1e-300) as negative, the subexpression (1e-300*1e-300) present in both should be recognized as distinct from (1-1), with the latter being neither positive nor negative.
Regarding the empty slots in array: They denote a missing index it's not a different type of `undefined`. Say you have the array `const sparseArray = [/* empty */, "1", 2]`, then you can check if a slot is empty by doing `0 in sparseArray` if the array doesn't have the property `0` it indicates an empty slot in index position `0`. The reason why the return type is a normal undefined is because if you access an attribute that's not there you simply get `undefined`. For example `const obj = { a: 1 }` then `obj.b` will yield `undefined`. Just like `sparseArray[0]` yields `undefined`. You are accessing a property that doesn't exist. If I would do `delete sparseArray[1]` then index `1` is now also an empty slot.
Dude, seriously. Read the floating point spec. Most if those "errors" are in all programming language and for good reason
They are in all programming languages, but I wouldn't consider "compliance to a broken spec" to be a "good reason". A reason, yes, but not a good one.
Other programming languages do a better job at keeping these weird parts of the spec from directly impacting a programmer. Usually by not having floats be the only or even the default way to handle number. Especially high level languages tend to keep that low level weirdness away from the things the programmer has to personally worry about
@@angeldude101imaging calling IEEE floating point a “broken spec”
@@IanBLacy I can easily imagine it, because I do it regularly. 🙃
I've used non-IEEE floats because they needed to be done in software and worrying less about edge cases was more performant (I assume, IDK it was a library from I think the compiler vendor).
I wouldn't call the spec broken, though. Maybe they're a disgruntled microchip engineer who could make division faster if it weren't for this danged spec, or something.
Honestly, I feel these issues are overblown by people who dislike javascript for entire different reasons. Most of these boil down to "don't use operators on non-primitive types". I genuinely don't remember last time I would trip on any of these in last 15 years writing JS.
Exactly!
As someone who doesn’t use Js these seem a pain to deal with or could cause completely stupid bugs if you ain’t paying attention like what on earth is going on here 30:47
> Why is `Number.MIN_VALUE` the smallest _positive_ value?
Because the smallest _negative_ value is `-Number.MAX_VALUE`. As simple as that. Again, simple floating point practices, common to all languages I am aware of. The ways how JavaScript treats floating point numbers is actually *consistent* , even though they may look weird and illogical (and appear "truly JavaScript-y").
Honestly, that should just be "Number.MIN_MAGNITUDE". It is a valuable constant to have, but confusingly named.
@@angeldude101Or use the standard name for it: epsilon.
I swear there's a cron job somewhere that churns these articles out on a quarterly basis, complete with all the same convoluted examples.
"OMG this language is SO CURSED 🤣"
33% of the time: type coercion failures
33% of the time: misunderstanding IEEE 754
33% of the time: trying to do stupid shit that wouldn't work in any language (1 < 2 < 3 is sinful)
1% of the time: actual poorly-implemented JS edge cases
@@BetaDude40 1 < 2 < 3 is, like.. either it works as it feels (1 < 2, 2 < 3) or it works as it should work (true < 3). These people are saying all logic errors due to language misunderstandings are on the language itself! that's like saying that it's on chinese that it has tones!
12:50 Actually java also uses the to string. BUT ONLY WHEN ADDING SOMETHING TO A STRING. WHEN ADDING TO OBJECTS IT JUST TYPE ERRORS LIKE WHAT A NORMAL PROGRAMMING LANGUAGE WOULD DO.
I thought the double not operator converts values to their boolean equivalent so !!"false" would be true because a string is truthy. !!"" would be false because an empty string is falsy. Same thing for the !!null, that would be false because null is falsy. The explanations around all the double nots seem to be convoluted at best.
No, the single bang (unary logical negation) not only coerces the value into a boolean, but also negates the operand after coercion. This is boolean logic and it is equivalent to `Boolean("string") === false`. Applying two bangs just negates the negated value i.e. `(Boolean("string") === false) === false`
It's the same for unary plus/minus operator (+, -). It coerces into a number like (Number("123string")) and evaluates it. So -`"-12" === 12`. This is different from the addition operator, which will coerce both operands into primitives before evaluating the expression. If one side is a string, the other is coerced to a string as well. Otherwise if both are numbers it will perform an addition.
"Array equality makes NO SENSE!!" and then array equality makes complete sense
12:00 "Why is the smallest *_positive_* number? Why? I don't know what the use of that is"
Clearly Theo has never dealt with the _joy_ of handling floating point numbers before
Though it's actually the smallest magnitude
Mandatory comment complaining about blocks being called closures in this video
These kind of videos serve mostly to show off the ignorance of the person looking at the examples.
In the nineties and naughties, people had this silly idea that languages shouldn't trigger error messages when people did something wrong, but instead try to do what the programmer intended.
I think that was a reaction to the cryptic core dumps and errors of languages of the seventies and eighties. People thought error messages was the problem so they avoided them.
This is common to all languages of that era, JavaScript, PHP (which has exactly one good feature), Ruby, Python, CoffeeScript, jQuery... Some are worse than others, but all are infected with that mindset.
And then people usually go on to complaining about floating point arithmetic to just _really_ show off their ignorance.
I agree, error messages are stupid, if I type code then I want that code to run, not for a compiler to think it knows what I want better than me, Windows should want people to be able to break their computer, then I'd have to buy a new one, more money to Microsoft as a result. The worst error messages are the ones that try to stop you from performing low-level memory manipulation, of course I suspect this is largely because Windows probably doesn't really want you doing anything outside of the virtual memory it gives you, but Windows should keep its mouth shut and do as I tell it to do, if that means wiping my computer's memory then so be it.
@@flameofthephoenix8395there's a middle ground. Allowing the programmer to do whatever they want so long as the command makes sense, but not guessing at what the command means when it doesn't make sense. Letting you fuck up your own machine, and just throwing something into a string when you aren't sure what the programmer wanted are two very different things
@@GhostGlitch. I'm glad we agree that I should be allowed to break my machine if I want to, and I don't see why the compiler would go out of its way to try to interpret the meaning of non sensical code in the first place, so we agree on that too. Those array out of bound errors are particularly obnoxious, how are people supposed to read garbage data if the language itself is working against them? If a programmer fails to put in safety checks that is their problem, all the out of bounds checks do is prevent some people from reading outside of the array, slow down the program with unnecessary checks, and dampen the speed at which beginners learn the importance of not screwing up and accidentally reading garbage data when the program shouldn't.
Out of curiosity, what good feature of PHP are you talking about?
@@FurryDanOriginal A separate operator for concatenation: .
I despise languages for using + for string concatenation, addition and concatenation are not similar in any way. Multiplication is closer, but still obviously silly.
Some use ++ which is at least different, but not as good as PHP.
Saying +"5" is a bad way to coerce and then suggesting parseInt is hilarious
why?
@@kartikjha833 You have explenation on video for example you shoud always set type of parsing as second argument, because not all runtimes have it the same
Imagine a language that you need a fucking phd to convert a string to int, lol
He should have use Number("5"). it actually is the same thing as +"5". but Number actually is a declarative way to say what you're doing.
@@CrazyWinner357 fortunetry in lower languages like C or rust converting to int is a breeze
Yes? Yes?!
Fun fact, -0 is actually part of the FP standard, a lot of languages have it! I've specifically used it as a special flag in games I've made.
I'm frankly more surprised how little he knows about how javascript works tbh.
So what in ur opinion did he got wrong
@@itszzupart6607 He didn't get things wrong, he was "shocked" multiple times over things he didn't know. His fundamental understanding of JS is low. JS being a very messy language shouldn't come as a surprise to anyone with a couple of years experience, and you should know these things and the reason why it works the way it does.
@@johanrg70 i didnt get it, it is absolutely normal to be shocked on things that are just dumb in language even if you are some expert
thank you for inspiring me to watch actual coding channels who know what they're talking about and also to join your nemesis as a member, he needs more funding to do what he's doing for the community :p
Theo, you are uniquely talented in both your ability to understand an process the examples quickly, and in not getting bogged down (as I believe most people would) in the endless details and potential rabbit trails that present themselves along with the seemingly inexplicable results. If I were to walk through this list of examples it would take me hours to complete the journey.
Your comments - especially the resignation and despair - were absolutely hilarious. Thank you... :D And just so you know, this is how I sometimes feel while writing PHP. xD You are not alone.
Repo: Consider this step-by-step
Theo: "I don't want to consider this step by step"
cracked me up
typeof 1 == typeof "1" being false makes total sense because 1 is an int and "1" is a string
-0 and 0 are warts of IEEE754 Floating numbers and as such they exist in most programming languages. There's no -0 in integer numbers.
A common misconception that I try to teach students when learning about floating point is that 0.0 and -0.0 (represented in IEEE 754 as the hex strings 0x00000000 and 0x80000000) are not actually equivalent to the true value of 0, but instead they're ranges of values. 0.0 can represent a number anywhere between 0 and about 1e-127 inclusive, while -0.0 can represent anything from -1e-127 to 0. Thus we should treat 0.0 as "0 or any arbitrarily small positive number" and -0.0 as "0 or any arbitrarily small negative number." This is just a consequence of the fact that the sign bit used in IEEE 754 doesn't matter if you're considering only true 0 since true 0 is unsigned and integer implementations skirt around it with 2's complement. But the reason why we still use this is because knowing whether you have a positive or negative arbitrarily small value is very important for calculations, especially multiplication or division (though if you're getting numbers whose magnitude is so arbitrarily small then you really need to rework your code to avoid the catastrophic cancellation in the first place or use epsilons to force expected behavior)
@@BetaDude40Yeah, IEEE 754 is very mathematically coherent but very hard to grasp for laypeople
15:57 sometimes I think that He doesn't know what he talking about )
so, basically what happened, first array is empty and when you try to access nonexisting key you will get undefined the same that you store in second array under existing key
you can not get "empty" value from array
"the falseyness occurs before the number coercion...why?"
I think you already know why, it's just about evaluation order. The real questions are why was it decided that the empty array is truthy and why was it decided that == coerces things to numbers.
12:02 What do you mean why?? thats literally your epsilon
well it's just badly named…
The question is why it is named MIN_VALUE when it is not actually the minimal value. (And no, it's not epsilon; epsilon is the difference between 1 and the next larger exactly representable value.)
However the problem is not unique to JS, it is already in C, where INT_MIN is the minimal value of int (and therefore negative), while FLOAT_MIN is the minimal positive value of float (and thus positive).
I think just renaming it to "LEAST_ABS" or something would be a bit more intuitive to what it means, but this is also a quirk of the English language. "What is the smallest number?" could either mean 0 or -infinity depending on how you interpret that statement, because -infinity < 0 but abs(-infinity) > abs(0)
The only reason I watched this was to hear someone else explain the concept of the minimum of an empty set being ∞ and the maximum being -∞. But you didn't. Dammit.
Defenses for NaN and -0:
According to the specifications of float32 numbers (in all programming languages that use that type behind the scenes) can represent the value NaN in [2 to the power 23 (iirc) minus 1] ways. Logically, Not Valid Numbers cannot be equal to any computed value (0/0 is not equal to 0^0), therefore NaN != NaN.
As for -0, in float32, the representation of -0 in bits is distinct from 0. So while -0 is not useful in practice, which is why it is hard to get it in implicit ways, it is a distinct value from 0.
I'm not a JS guru, but I'm using it for decades. For the vast majority of these quirks I never had a problem , simply by not relying on stupid syntax.
I never use a string as a boolean value. Why would I? Even if I remembered the detailed implications, I would never expect the next guy working on my code to remember it.
I don't use `+` to concatenate strings unless there is a good reason to. When I do, I start the expression with `"" + ...` to make sure the intention is clear.
Why would I ever want to `str - number`? Why would I ever use an array in a scalar context? Knowing the semantics of `==` why would I ever prefer it over `===` unless I want the precise semantics of `==`, I can't remember such a use case. I don't remember the precise semantics of `==`, it's faster to express the semantics I need explicitly instead of looking up what `==` does precisely and burden my coworkers with the same task. I'm not stupid, neither am I malicious or irresponsible.
I'm not saying JS is a great language. But for all practical purposes, it's by far not as bad as its reputation.
Anybody who uses `false == "false"` to say anything about the language that is interpreting this code is not qualified. Such code should never have been written for any purpose other to condemn authors of such code to hell fire.
However, if you are stupid, malicious or irresponsible, you can create the very same kind of confusion with any other language, Rust, Zig and Python included. Well maybe not the same kind, but the same headache resulting from where-ever your stupidity leads you to go.
Really this just shows that the mentality of "sensible defaults" rather than erroring is a terrible idea for language design and programming in general
nah it just shows that js defaults are everything but sensible XD
@@benebene9525 But maybe there is just NO way to have sensible defaults: they will always result in unsensible results in some scenario.
yeah; websites blowing left and right would have been so much better!
or, you know... don't use type coercion - just an an extra =
Exactly, this is just a series of examples of the robustness principle in action.
It makes sense in a way, that x = 5; console.log("your number is " + x); should work. Any beginner programmer would instinctively try that.
It makes sense that !!"false" should equal true, because "false" isn't false. I think people would be far more annoyed if someone could input the string "false" into a form field and their check for `if (input.value)` would return false instead of true.
If you're going to have type coercion, it makes sense that +"some string" should equal NaN. It's literally not a number. That's kind of what NaN is for.
Everything is just a sneaky combination of these things. You can do that in every language. In python and lua, you can redefine `print` to whatever function you want. In java, you can do all sorts of weird stuff with boxed types. In typescript, you can define utterly incomprehensible behaviour within the type system. And don't get me started on C, which lets you edit parts of the stack that are in use by the program, access variables that are supposed to no longer exist, and treat the binary at any particular pointer as any type you want, even though it makes no sense.
@@zettca Well yeah. That's why Typescript exists. It's much easier to fix the result of an error than the result of a security flaw caused by implicit behavior.
12:00 This is an important concept in math. I was just reading about Kakeya’s Needle Problem today, and the goal in it is to minimize the area needed to sweep a needle of zero-width 180°. Various mathematicians found ways to minimize the area needed that approach zero at the limit, but the area can never actually _be_ zero. This would be a perfect application of Number.MIN_VALUE. The limit would be equal to it, and it would be greater than zero, for good reason. Meanwhile, negative infinity would be smaller than any number, and negative Number.MIN_VALUE would be the largest negative number before zero.
The more crazy js is, the more I like it. Awesome video!
Some of these are just normal programming things, and are quite intuitive if you understand a little bit of how computers work under the hood.
TLDW Every few years someone makes this video. These issues do exist but they don't seem to manifest in real code.
My experience is that my final code never contains these issues, to the extent I can manage, but I frequently stumble upon them during development and cleaning up the mess slows me down. JS has too many buried landmines and cursed sections of the language, it's unpleasant.
The fun stuff for NaN is equality in the simple sense of numerical value.
If the result of a math operation is undefined, then you get NaN.
By definition, this cannot equal any other number. Just like infinity cannot equal infinity.
As you cannot make a comparison between their values.
For 0, by definition -0 is a different object to 0.
But their values are both 0, so if you compare them by value, even using the strict equality operator, the result is true.
4:00
x: "2"
y: 2
z: "2.0"
The strings evaluate each other as strings so they're false. But they both evaluate the integer value when comparing to the integer y.
Fun fact, the empty items in an array are called "The Hole" within the V8 engine. "The Hole" falls into the "Oddball" category of types
Someone has probably already mentioned this, but JS actually can't take credit for the NaN != NaN thing. That's completely from the IEEE floating point spec. It's in basically every language and baked into the silicon.
Basically every NaN operation always just fails, because otherwise you could say Math.sqrt(-2) === Math.sqrt(-3) -> true. NaN is basically a placeholder for "this data representation is insufficient, math breaks down here, go find a new datatype if you really want to do this" and it wouldn't make sense for it to do anything but instantly fail out.
@@BetaDude40 Prior to the introduction of IEEE-754, I think it would have been obvious that programming languages should provide a transitive numeric comparison operator that can be used to sort any kind of numbers that could be produced as a result of numberic computation. I guess one could minimize the cost of dealing with NaN in a sort by partitioning data into "NaN" and "non-NaN" regions and only sorting the regions where all NaN values have been excluded, but that sitll seems like a lot of extra work that would be needed to prevent a sort routine from turning into a pile of goo if there are any NaN values in the data.
I feel like Theo is the kind of guy that if he ever used ActionScript3, he'd be stuck using it since its got most modern syntax features (E4X, JS typing, Embedding file) in an old language
15:09 is not about 3 different values for empty value, it's about setting length to 3, but array object have not keys "0" and "1" assigned. [undefined, undefined] assigns that keys to undefined.
Array in JavaScript are just Objects with string keys and magical 'length' property.
About the new String thing: you can call the String and Number constructor with and without new:
without new, it works just like you would expect, it converts the argument to the respective type, and typeof will give you a reasonable result.
with new, you get a "String object" or "Number object", which is basically like a string or number, except that you can actually override functions and stuff, as you've shown with your "this is the wrong string" example.
you can even create unrelated properties on the String/Number objects if you created them with new:
> s = "hello"
'hello'
> s.a = 42
42
> s.a
undefined
> s = new String("hello")
[String: 'hello']
> s.a = 42
42
> s.a
42
>
even though its totally obvious you are being sarcastic and having a fun, i still really appreciate your hint here at 1:45
MIN_VALUE is used to add to division operations so they don't break. There are other uses too, like checking a range of values to prevent float precision errors.
I use Ruby, so here's something fun: 0.1 + 0.2 == 0.3 returns false, but 0.1r + 0.2r == 0.3r returns true.
Explanation: "r" is a Rational literal. Rational is a number type which, when called via a literal like this, casts the preceding number to an Integer over another Integer, which means you get, in effect, 1/10 + 1/5 == 3/10, which is correct. Without it, values like 0.1 are assumed to be Float values, which follow the IEEE spec and so sum dumbly.
Rationals are fine, decimal values being floating point by default is mostly an hysterical reason, ruby didn't have rationals for a while.
Javascript has a reason for most of its weirdness. The reason some design decisions are criticized is that much like required reading in highschool, if you work with the web at any point, you have to deal with javascript. People just grow resentful they have no choice to use it, and making fun of certain features become memes.
Because of its position as the main programming language of the web, it can't really do breaking changes, and undoing certain core design desicions isnt feasiable. When it was created, its creator had no idea of the scope it would eventually cover, and since the community grew so huge, contributors had to balance giving everyone enough features so that everyone was only half angry.
But yes it is fun to make fun of all the quirks of the language, just don't forget that there is context to all of the design decisions you don't love.
Also python, get some brackets, ruby, not everything has to be magic, Php, you're cool now.
We should break things more often.
Honestly confused on why everybody hates this weird JS, you never run into these issues if you write good code
Hype
All fun and games till you have to touch a 30k line codebase that uses == everywhere
@@woofcaptain8212 If you use == to compare potentially unknown stuff that can lead to unexpected scenarios, then I'd say that's bad code.
@@woofcaptain8212 Well, if you aren't dealing with data that isn't very specific like data with strings only, then yes
I agree with most things but some stuff is really simple, such as 3:07, and I feel like you may be overexaggerating this a bit. !!"false" == !!"true" being true is completely normal. A non empty string is truthy. The first ! makes it false, and the second flips the false and makes it true. There is nothing wrong with this, and yet you make it seem like it's very strange, and that we are in "hell".
4:57, When checking if a boolean is equal to a string, it obviously wouldnt return true. Now given my previous point, you might then confuse why !!"true" is true, and that is because ! converts it to a boolean. So it goes !!"true" -> !false -> true, which also explains 5:03. I wouldn't say that is "dumb".
9:20 Now I know this one isn't intuitive to understand, but I will try to explain it in a way that hopefully the confusion would go away. I believe the reason why null == false is false but 0 == false and "" == false is true, is because null is loosely connected to undefined (which is also why at 10:28, document.all == null is true, while document.all == undefined would also be true, but document.all == false is fasle). Undefined is when the value isnt assigned, and null is when its been assigned but has no value, and so there is no type coercion. Strings and numbers go through type coercion but null doesnt, since it's not really a type. All you need to remember is that there is no type coercion for null and undefined and that should make sense of this.
People should not be told these are challenging or strange. You can make simple things become difficult by simply pretending that they are. Many people including myself look up to you, and so I think we would all appreciate it if you would actually pause to think about these things instead of hopping on the "Yep there it is, JavaScript is dumb".
Agreed
It's convoluted BS that, in my opinion, hardly anyone uses and would cause unintended side effects. There is no need to use boolean negation on a string, it makes no sense and it just causes massive amounts of type confusion.
It is not normal, in any sense, to boolean negate a string to a boolean value because what if you never intended said string to be a boolean, but simply a string with the word true or false?
This is why I hate dynamically typed languages. They treat everything as a variant essentially which causes all these conversion side effects that you never intend.
@@jshowao Yeah I mean there is literally no reason anyone would be using double negation like that. I find myself using boolean negation on strings in situations such as:
if (!node.textContent) throw Error(“Node has no content”)
Or for double negation:
function isValidName(name) {
return !!name
}
But the above could also just be written as:
return name != “”.
For me, I think projects that I faced some side effects were the ones that I used a ton of APIs with varying and sometimes unpredictable response values. And even then, I never really found myself thinking JavaScript was the problem, it was just that I didn’t know enough. With what I know now, I would make fewer mistakes and likely waste little to no time dealing with these side effects. I think all of this JS hate is pointless, it’s still a very powerful language regardless. If you write good code, it would be easy to debug the side effects if they ever occur
@@jshowao It's actually normal to use negation on a string to check if it's empty or not in a dynamically typed language. That's what you would expect: an empty structure is falsy. Tho I prefer the C++ way, just a simple empty() mehod to check.
@@楊立慈-o3f Ive never see it done in a statically typed language. It would fail in C#, fail in Java, fail in C.
You dont use a negation operator to convert a "false" or "true" string to boolean. You convert it to boolean through a cast or parse method, then use boolean operators on it. That makes sense because a string is not a boolean type. This way, Javascript just assumes the string value should be boolean and converts it as a side effect.
−0.0 exists because IEEE floats are implemented as sign-magnitude pairs, so with magnitude 0, you can choose a positive or negative sign. A different approach to floating-point numbers is, effectively, using INT_MIN as NaN and using 2’s complement for negatives. That format exists and is called Unum.
The reason it's treated as a distinct value is to make 1/(1e-300*1e-300) positive but 1/-(1e-300*1e-300) negative. Unfortunately, IEEE fails to recognize that both +/- (1e-300*1e-300) should be distinct from 1/0.0 or 1/(1-1), both of which should be NaN.
patching the Number prototype has a neat application: you can make numbers iterable (counting 0 to n-1), so you can "for...of" them or use them everywhere iterables can be used. i also added some forEach, map and reduce methods. for numbers that aren't positive intergers, the behaviour isn't quite so obvious but your also unlikely to use them for those. :P
i get why expanding builtins is probably not something you should do in a professional setting, but it's a very nice feature to use and it may be a cool in other languages.
Please, don't make something like for (const i of 10), it doesn't make sense semantically, consider creating a range class and do for I of range(1, 10), or similar easier to understand intentions
Couldn't even watch the whole video because of the frustration
it was very fun, I am a beginner in JS (with a year of exp, nothing imo) and learned a lot
Given how much of JS's weirdness is actually just IEEE754 abuse (like .1+.2 = .30..04). Go look up some talks on it!
NaN != NaN is part of IEEE spec and same in other languages. It makes sense since something like 0/0 == parseInt(“hello”) would be crazy, and make debugging harder.
Classic frontend script kiddie to blame standard floating point behaviour on js ;)
Barring potential weirdo string conversion stuff, no comparison operator with NaN returns true other than NaN != x, where x is any floating point value, including NaN, which is about as close to NaN being non-comparable to other Numbers (including itself) as you can get without introducing null or undefined as a possible outcome.
Basically this lets you do operations with "infinity" without having to worry about spitting out true when comparing two indeterminate forms that have different limiting values.
The question comes down to, "How do you evaluate the truth value of something like (2x - 4) / (x - 2) == (x^2 - 4) / (x - 2) at x = 2?" The only way to reasonably do it is actually taking the limit of both sides (in this case 2 != 4). And you could do this programmatically with some effort and probably be right most of the time, but boy is it easier to just say, "Lol no."
@@Mystic998
With regular languages like JS you just don't look at any statement with comparison operators and treat it as an equation to solve, because those are just simple operators that are evaluated in particular order. You know, CPUs don't think and don't learn calculus. Solving equations is a task for specialised tools that either have actual concept of a limit and can approximate it numericaly or even can do symbolic math and give you general solutions. And indeed, in JS (and C# and any reasonable non-specialised language that does floats), you just say "lol, no". More accurately, you evaluate to NaN that can stand for undefined value
This stuff happens in any dynamic language where they decide you should be able to compare things that have different types and also that types should coerce automatically. The rules they decide usually make sense if you look at them in isolation of the original intent of the rule, but the resulting semantics is so broken that you can't rely on it because it quickly becomes mostly impossible to predict for a human.
Basically showing that double equals shouldn't exist.
41:20 "this is huar stupid don't do it that way"
This just makes sense because promises are monads and the point of monads is exactly this flattening or removing of multiple boxes of the same type.
So this is actually good behaviour for promises
I have 2 uses for the double equal:
- x != null // check if variable x is defined
- x == y // check if values are equal, when you KNOW they are either a number or a string, like for checks with input values
x == null // check if x is nullish (null or undefined)
Your reaction thumbnail is spot on.
best part is receiving bug reports that the app would randomly consume 100% cpu
turns out it was a relative date component (i.e. turns new Date(0) into "54 years ago"), that updated in real time. turns out if the time until the next update (ie. 5.4 seconds ago is 600ms away from displaying "6 seconds ago") was greater than 24.8 days, or 2,147,483,647 ms it would call setState() or forceUpdate() or whatever it was in a loop
Somebody told me after Theo published this video four friendly men in white cloaks had to take him away in a “Love-myself-so-much” jacket.
Integer arrays being sorted alphabetically is remarkable...
The String.split() behaviour is correct (at least the one shown here, it might do something weird in some other cases, it's JS after all):
- There are empty strings between each character (conceptually), so splitting on "" must return an array of every character on the string. Since you are calling it on an empty string, that result will be empty.
- Splitting by something not in the string should return nothing, because there's nothing to each side of that separator ( the case for "".split(" ") ).
These are just edge cases, but the behaviour is logical.
36:09 - "We're moving on because nobody uses 'this' anymore." * _cries in Angular_ *
Javascript needs a boolean called "flase" that == false 90% of the time, and true the rest.
the "wat" talk honestly summarizes javascript pretty well
your sarcasm actually breaks me
There is a complicated table of how == equality works. But the truth is… you don't need it in reality, unless you're doing something strange. If you're comparing primitives they would be coerced to string or number depending on what you have passed in. So, it's handy to check for example == 2, when your backend may send "2" with no need of manual coercion Number('2'). I'd recommend to use strict comparing === to *true* or *false* , but that's a rare case, especially after defaults where introduced, and one can do something like
function foo(bar = true) {
if (bar) … // could be “if (bar !== false)” in the old days
}
Objects are compared their to strict identities between them, no matter are you using == or ===. [] != [] is same as [] !== [] and is true, since you're comparing two different freshly created arrays here.
*null* and *undefined* are equal only to self, as already noted. So it's safe to compare anything with them. (Usually != null is handy and shorter.)
The unexpected part begins when you're comparing any type of object to primitive, so it's a bad idea to do. Just don't do it. Why would you? Why would one compare *false* and an array? It's that case when explicit better than implicit. E.g. by using .toString(). But still it's quite a rare case too. Usually, objects compared to be same or being nullish.
So, that WAT part is quite overhyped really. Yep, one cannot use + to concatenate arrays like in Python, [...array] spread or .concat() should be used for that. But you learn it when you learn basics about arrays and their methods. No big deal.
It's weird because most people don't understand typecasting that's done by js when == is used instead of using ===.
You can attach properties to String objects but not native string instances.
I've used this for templating meta information for database string templates.
"I hate everything. I'm going to become a farmer."
I think about this often...
In the Godot game engine, a freed object == null, but is truthy. Assigning a freed object to a variable _sometimes_ throws an error, and a freed object inside a closure gets turned into a null. And of course you also need to check if an object is queued to be freed at the end of the frame but hasn't actually been freed yet. I think some of this is changing in 4.3.
undefined is very fun.. You cab delete object keys by setting them explicitly to undefined, but a key can exist with value undefined and be different to not existing on an object at all, despite always reading out the value as undefined
Half of this weirdness comes from it having the weakest ass type system known to mankind. Strict types somewhat force the rest of a programming language to make sense.
agree 1000%
@theo deviding by zero is technically undefined (in mathematics) as assigning it a value would break maths ;) if you’d do that you could do stuff like reasoning 1 === 2 === 3 === 4 === …
The intuitive way of assigning it infinity leads to infinity === -infinity - because it depends on from which direction you approach zero from.
there is a nice numberphile video on that if you are interessted in the specifincs ;)
The difference between "truly not defined" and "undefined" is that "truly not defined" only exists in array contexts, and is defined by the given index of the array not existing. When you call a function and omit an argument, the argument is referenced as undefined, but you can tell whether it's undefined or truly not set by receiving ...args and checking the length.
If you don't get -0, then you can't understand floating point. It's not a JavaScript thing at all. All floats are approximations. They have limited precision. And sometimes it matters if the result you get is slightly less than 0 or slightly more than 0.
NaN !== NaN makes sense, too. NaN is a result you should not get. If two expressions fail, you don't really want them to be equal to each other.
And I get why "false" != false. It's normal to check if a value has been input by doing "if (textbox)". It would suck if that failed because the user typed in "false." The idea that any non-empty string is true makes sense to me. I only find it weird when the DOM coerces false or undefined into "false" and "undefined". That's dumb, and should be the same as assigning the empty string.
And of course string- number equal not a number. There is no defined operation for subtracting strings. What else could it return?
This was some.. uhh..amazing(?)... things?
Welp, I had 'fun' watching this video. Some of these things I knew, many I did not. The knowledge that this list is surely incomplete simply adds on to the 'joy' of javascript.
The reason for there being a NEGATIVE zero is simple, actually - if you add a sign to a value, you split it in half and get mirror sides (so 0..255 becomes -127..127). But you still have _256_ values, not 255. 0 is reflected on BOTH sides - an unsigned 0 (00000000) and a _signed_ zero (10000000). For all intents and purposes, -0 == 0. However, they are NOT the same value, because bitwise 0x00000000 *!=* 0x10000000.
I'm so early, most of the comments are spam!
isn’t that the usual?
Lol fr
Fr
I'm not spam
@@thepeer ok
I'm wondering... What if I define a toString method which does not return a string? How will the + operator work then?
Just try it:
const a = { toString: ()=> 1, };
const b = { toString: ()=> 3, };
a + b;
Bro I genuinely didn't know abt the sort thing at 40:49 and now my code doesn't randomly function in dumb orders
The one where he says it works without a semi colon makes sense. He wasn't writing a command. He was accessing a value. (29:33)
I think I would have given up on learning programming had I started with JS.
Javascript is obviously a physics engine! It has types representing empty air, the vacuum of space, and whatever the universe is expanding into!
7:55 “I’m going to be a farmer” boy do I have a game for you to play…
The Farmer Was Replaced is a farming game where you program a drone to do all the farming on your grid based farm
Wtfjs a single markdown README with paragraphs with at most three lines each + at most 10 line code snippets in between, well structured, almost none of the (paragraph,snippet) pairs share context.
It takes 45 minutes to go through, if you know how 75% of it already.
Truly, WTF JS
the
let x, { x: y = 1 } = { x }
thing is self-explanatory.
first, u declare x which is undefined.
then u redeclare it to be 1, and set it to be equal to x inside the object so u get 1
MAYBE JUST MAYBE i understood it wrong but i think its like this
"take my hand through the flames
(I torture you) I'm a slave to your games
I'm just a sucker for pain" - Imagine Dragons
After reaching 5 minutes in, I almost want to pull the transcript and see if he beats Limp Bizkit - Hot Dog's count of 46 fucks in this fucked up video.
The hell I went through coming from Java and C to javascript. The syntax didn't make sense at all simply because of those small unintentional bugs until I started working it it from the beginning forgetting all the programming concepts, learning the syntax and then reapply those concepts. For a person starting with JavaScript its okay but coming from a statically typed language, you will hate it.
What looks like a duck, quacks like a... You entered the JS world with the knowledge that is not completely transferable and applicable. Most devs with different backgrounds make assumptions about how JS works. When they get hoisted on their own petard, they hate the language.
ECMAScript spec (and some other standards) addresses most if not all the weird stuff shown here. It is 2024, Theo's take on this topic is archaic.
I would assume (I haven't looked at the actual implementation) that values are stored in a way similar to this:
struct Value {
bool is_empty;
enum { VAL_UNDEFINED; VAL_NULL; /* ... */ } type;
void *value;
}
so when you set no value, it just sets is_empty to true, but when you set a value manually, it sets it to false, and then sets the type to undefined.
I'm not sure why undefined doesn't just always set it to empty, or empty is just the undefined type, but maybe its because JS is old and people rely on it now.
The Math.min() behavior is imo perfectly reasonable. We want that the min of the combination of two sets is the same as the min of the min of the sets seperately, i.e:
min(min(a, b), min(c, d)) = min(a,b,c,d)
What does that mean for min()? (The min of the empty list):
min(min(), min(a)) == min(a) = a
The only value for min() that makes this work is +Infinity.
I love the fact that javascript has a set of consistent logical rules, even when it this results in it acting absolutely absurd at times.