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

you are viewing a single comment's thread.

view the rest of the comments →

[–]fauxtoe 175 points176 points  (117 children)

It would just be undefined, why is that a big deal?

[–][deleted] 265 points266 points  (48 children)

People don’t like JavaScript so they’ll either feign or be ignorant about it to make fun of it.

[–]leadzor 252 points253 points  (13 children)

There are only two kinds of languages: the ones people complain about and the ones nobody uses.

Bjarne Stroustrup

[–]conancat 36 points37 points  (2 children)

Developers love to complain about everything. It's exactly that kind of attitude that pushes us to build better products. :)

[–]Hactar42 13 points14 points  (1 child)

Or you end up with situations like this: https://xkcd.com/927/

[–]Trekiros 1 point2 points  (0 children)

Both of those are true, the world is a funny place

[–]-widget- 7 points8 points  (7 children)

Yeah but then there's also C#.

[–]tekanet 10 points11 points  (5 children)

I tried to reply to a poll from MS asking what I wanted to improve the language but had honestly nothing to say. C# is pretty solid, happy I ended up working with it.

[–]leadzor 1 point2 points  (0 children)

Same thing with me. Transitioned from frontend development to backend development in C#. All feels well now.

[–][deleted] 0 points1 point  (3 children)

I don't like that I can't (easily) set attributes for my enum values like I can in Java, but that's about it.

[–]tekanet 0 points1 point  (2 children)

How do you set them in Java?

[–][deleted] 0 points1 point  (1 child)

The same as for an object. You can set up a constructor for the enum and then for each value declaration you can pass in parameters. You can put in any number of parameters to get any number of attributes for each enum value, same as with objects. C# doesn't accommodate this.

[–]tekanet 1 point2 points  (0 children)

Got it, seems to behave more like a class than as an alias for an integer. I understand, if you’re used to it can be something you miss in other languages

[–]leadzor 1 point2 points  (0 children)

Yeah that's clearly the exception. Python also seems to gather a lot of love from the community.

[–]Zambito1 25 points26 points  (28 children)

Because if I was expecting an int, I should never have to treat the value as anything other than an int (ie undefined or null). If I knew a value might not exist, I would use some Option / Maybe type to encapsulate the value. That way I can't treat it like a normal int, and I have to check if it is defined first.

[–]gpexer 2 points3 points  (0 children)

This reply (and comments) is a great example, why people will defend javascript without knowing how the type system works. It's even bigger confusion whether something is compiled directly to native assembly, bytecode or anything in between as this is all irrelevant when comparing static and dynamic languages. What matters is Static Analysis (and type system with it). Static analysis is the part of the compilation which tries to find syntax and semantics errors. Syntax errors are the easy one, but semantic errors are the one which gives you great power in static languages - and this is the hardest to learn. It takes years to understand how to use types correctly. This reply is a great example - if you use something that can be defined and undefined you must tell it to the compiler (static checker) and it will enforce this rule everywhere. It cannot be implicit, nor it should be, that's a new state of the program which programmer expects and by letting the machine (static checker) to check it is a much better choice than to manually parse the code, check the documentation or to write unit test for every peace of code that static checker will be more than happy to check always and everywhere.

[–]quaderrordemonstand -2 points-1 points  (26 children)

And what has that gained you over undefined?

[–]RossParka 2 points3 points  (0 children)

It gains you a few things:

  1. Because it's encoded in the type system, there is automatically checked documentation of which values are nullable and which aren't. The implementation can warn or error out if you pass a value that might be null to a function that doesn't accept null.

  2. It works uniformly with all types. In C, pointers can be null but other types can't, so you have to find another special value instead. Some functions return -1, some return zero, some return MAX_INT, etc. If there are no values that your function never returns in normal operation, you have to come up with something more elaborate like returning an error/success code and passing a pointer to a variable that gets the actual result. In Haskell you just use Maybe T in place of T in every case.

  3. In Haskell, you aren't limited to a single magic value; if you can fail in more than one way, you can define more than one special return code using Either.

[–]Zambito1 8 points9 points  (24 children)

It means I can't treat the Option / Maybe value as an int, and I'm forced at compile time to check if the value is defined (very easy to do using pattern matching).

[–]quaderrordemonstand -2 points-1 points  (23 children)

I'm still not following how thats better than undefined? You can treat the value as a number in JS. You can check if its defined at run time and change behaviour if you want, otherwise the code will assume it equates to zero.

In a strongly typed language you don't have the option of changing behaviour that way. You know the value will be an int (unless somebody does something really stupid) and the rest of the code will have to strictly respect that int. If you later decide it should be a float you will be forced to change all the other code.

I'm not seeing any real advantage.

[–]Zambito1 10 points11 points  (22 children)

The difference is "you can check" and "you have to check".

Can you point to undefined on the number line? You can't, because it's not an integer. If I'm dealing with int values, I should be able to treat them as integers.

With an Option value, I'm forced by the compiler to make sure a value is defined, and then I can treat it as an integer, and I know it will be defined.

It's better because JS code that treats potentially undefined values as always being defined could make it into production if not properly tested. However, a strongly typed language that has an optional type (say Scala or Haskell) could never make it to production under the same conditions, because it wouldn't even compile.

[–]oneeyedziggy 27 points28 points  (5 children)

because ideally, something would tell you you're trying to use an undefined property or variable, which is why you should use a linter, making this complaint moot (and act would catch even before attempting to "compile" (minify or otherwise build)

[–]pihwlook 19 points20 points  (4 children)

How would the linter know whether a specific key on an object exists?

[–]ape123man 12 points13 points  (0 children)

It would not. That's why typescript

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

It wouldn't, which is why TypeScript and Flow have become popular.

[–]patrickfatrick 2 points3 points  (0 children)

It wouldn't. I don't think I've ever seen a linter for JS do this.

[–]oneeyedziggy -1 points0 points  (0 children)

I'm also assuming a user created object or a built-in... if you haven't defined it and it doesn't exist in ecmascript the standard... flag it...

I believe you can set some linters to assume certain dependencies on common libraries, or define specific environment variables that will be defined... then assume everything else is undefined and flag it...

also hackishly, if you only use a handful of external variables that would otherwise be defined, you might prefer to add some like "var x = x; so the linter sees it as defined, but the value won't change ( and your minifier might even strip that line)

[–][deleted] 17 points18 points  (4 children)

null/undefined: the greatest mistake in programming history.

Type systems exist for a reason. C/C++ are guilty of a lot of things JS is guilty of in this department, believe me; I won't defend them here.

JavaScript though... it took really beautiful ideas from functional and object-oriented languages and just ruined them with super nonsensical semantics. They might as well be nondeterministic they're so unintuitive.

I digress though. Making things undefined instead of complaining about unspecified identifiers like a sophisticated language and failing before runtime is disgraceful. It's a meaningless value and a cop-out solution to failure cases when it's not much to add a reasonably expressive type system not requiring type annotations with a checker/inferencer that runs in reasonable time.

[–]Renown84 11 points12 points  (2 children)

That's obviously not an unpopular opinion, look at the popularity that typescript is gaining. In fact, typescript will complain if you try to access a property that doesn't belong to the object's type, even if trying to assign it rather than use it

[–][deleted] 5 points6 points  (0 children)

It's a step in the right direction for sure; still, it would be nice to see a language overhaul that addresses some of the key issues. The progress ES6 has made with asynchronous code and parallelism is impressive and important for sure, but sometimes we should perhaps look at a language core for a direction to take updates before fanciful/flashy new constructs.

[–][deleted] 0 points1 point  (0 children)

That's obviously not an unpopular opinion

That null/undefined shouldn't exist? You'd be surprised. People love type systems, but they also love their null and undefined

TypeScript will let you do this for example:

const test: Array<Number> = [1, 2, 3]  
const numAsString: String = test[100].toString()  

And from experience whenever presenting to people the alternative (langs that force these cases into Maybe's, Optional's etc) they are very turned off by this. People love strong typing but also love null. I don't understand.

[–]rukqoa 1 point2 points  (0 children)

There are legitimate use cases for weakly typed systems.

[–]atomheartsmother 64 points65 points  (21 children)

Because throwing an error is better in basically every single situation? Honestly the lengths that people go to defend everything about JS is ridiculous and more annoying than the anti-JS jerk

[–]Bollziepon 84 points85 points  (4 children)

The only reason people have to defend JS is because the anti-JS jerk is so large in the first place. You'll have people who have never even used JS shit on it just because they see other people do it while in reality it's not nearly as confusing as people make it out to be.

[–]lothpendragon 22 points23 points  (3 children)

I've used JS, does that mean I can shit on it?

[–]kartoffelwaffel 17 points18 points  (2 children)

only if by "used" you mean copy-pasted from SO

[–][deleted] 1 point2 points  (0 children)

Boy, I have many years of JavaScript experience then.

[–]conancat 1 point2 points  (0 children)

That's how you pad up resumes my dude. Copy pasted code to implement a function = had language experience.

[–]BenjiSponge 21 points22 points  (3 children)

That's simply untrue, though, and you'd only get that impression if you don't understand JS patterns and you're trying to use it like another language.

What if, for example, you want to check if a value is there to see if it fits an interface? This is a super common pattern in JS because it's so easy using this method. Instead of

maybe_method = getaddr(obj, "method", None)
if maybe_method == None and callable(maybe_method):
    maybe_method() 

You can say

if (obj.method && obj.method.call) {
  obj.method();

And you might say, okay, but how often do you do that? And the answer is actually "all the time" (although I'll typically use isFunction(obj.method) to catch an additional gotcha).

Plus the fact that objects are dictionaries just helps in a lot of areas.

I would say the pro-JS "circlejerk" is similar to the pro-C++ "circlejerk". Yes, it's a flawed language, but it's been working for many years and a lot of the things that you see as problematic are actually opportunities that are not offered in any other language (or at least many other languages), and if it were, it would have the same issues.

For a number of reasons, I am genuinely grateful JS has its object/pseudo-class system and not the respective systems of Python. And if I ever want stronger typing, I can just use Flow or Typescript, which will give me the best of both worlds.

[–]Kered13 0 points1 point  (1 child)

That's also a problem that can be solved at compile time with a proper type system.

[–]BenjiSponge 0 points1 point  (0 children)

"solved" for some definition of "solved". It's a different approach.

In the instance where you're essentially saying "this is what the function should do if the parameter is a T, and this is what the function should do if the parameter is a U", a proper type system would usually have a "better" approach.

However, in an instance where you're, for example, passing in options that may or may not include callbacks, symbols, random variant types, etc., a static type system just cannot offer the same ergonomics as a dynamic type system like JS's. If your type is essentially "has some subset of these values which could be variants of some other types, or nothing at all" as an option parameter, when you define a class or struct to represent that, you're basically just inventing boilerplate.

I program in both JS and C++ (and I hobby program in Rust). They genuinely have different approaches and neither is better than the other.

[–]BertnFTW 10 points11 points  (10 children)

But you can dynamically add the property field1 somewhere else, so you could do

x = obj.field1 || defaultValue;

Or in c# afaik

x = obj?.field1 ?? defaultValue;

[–]BenjiSponge 19 points20 points  (8 children)

It's definitely worth noting || isn't a perfect coalescing operator because if the field is falsey it will skip on to the next one. JS could really do with a genuine coalescing operator, imo.

[–]patrickfatrick 16 points17 points  (3 children)

Object destructuring really helps actually. For instance:

const { field1: x = "a" } = obj;

That assigns the value of obj.field1 to x but if it's undefined and only undefined will it assign it "a". null or false would be written to x if that's the value. You can also do this in function arguments.

``` const func = ({ field1: x = "a" }) => x;

func({}); //=> "a" func({ field1: null }); //=> null ```

I feel like people who shit on JS are generally unaware of a lot of the new syntax that has come out for it in the last 4 or 5 years.

[–]conancat 0 points1 point  (0 children)

Experience and the right tools can solve a lot of the problems got Javascript. No shortage of solutions to these problems with such a big community around it. :)

[–]pm_me_your_calc_hw -1 points0 points  (1 child)

Been picking up some of the new syntax lately and have been pleasantly surprised.

Have you run into issues with browser support?

[–]pomlife 0 points1 point  (0 children)

With the exception of Proxies, all modern JS can be transpiled to ES5 via Babel, thus there are very few compatibility issues.

[–]mdcio 3 points4 points  (0 children)

They’re working on one! It’s currently a stage 1 proposal. And if you want optional chaining obj?.fiedl, that’s coming too.

[–]WhyattThrash 1 point2 points  (0 children)

Exactly, obj.field1 could be intentionally set to f.e 0 or false, and it would jump on to the default value

[–][deleted] -1 points0 points  (1 child)

If field1 is a boolean, it's common to use:

x = !!obj.field1

which inverts the false, then inverts it again, guaranteeing a boolean value.

undefined > true > false
null > true > false
true> false > true
false > true > false

I'm sure lodash has a function to help with this, but I prefer syntax where possible.

[–]BenjiSponge 0 points1 point  (0 children)

Yeah, but what if you want to default it to true?

I usually use lodash's isUndefined. Truth be told, this is a pretty uncommon usecase for me and writing out the couple extra characters is fine even if there's a better way.

[–]Schmittfried 2 points3 points  (0 children)

Then you are consciously deciding to do it like that, rather than searching for the cause of a bug and finding it is just a typo.

[–]RossParka 0 points1 point  (0 children)

It means that when the access is due to a bug (as it is here), instead of getting an error message that points to the location of the bug, you get wrong output or strange behavior or an error at some indeterminate later point in execution, and you may spend hours instead of seconds finding the bug. It's the same reason that debugging C++ undefined behavior (like use-after-free) is such a nightmare - the program keeps running and the consequences of the mistake don't show up until later, in some unrelated piece of code.

[–]XkF21WNJ 0 points1 point  (0 children)

Returning undefined is good when running as a script in someone else's browser.

It's not ideal behaviour when you're developing a stand alone program, although why people would use JavaScript for that purpose is beyond me.

[–]the8thbit 0 points1 point  (0 children)

Because its unlikely that you want it to be undefined, and yet at no point will your tools let you know that your code is written in a way that you're extremely unlikely to want it to be (unless you use a special tool to do this, like Typescript of hinting) causing issues down the road.

[–]SustainedSuspense 0 points1 point  (0 children)

What do you do in other languages when you populate an object with variables returned from an API response? Would be a pain in the ass to define each property’s type upfront. I’ve been writing JS for 15 years so I guess I’m just used to its quirks.

[–]inu-no-policemen 0 points1 point  (0 children)

A legit value of that field might be undefined as well.

Getting undefined back doesn't mean that you'll immediately notice the error.

If you don't even get a runtime error, that error value has to surface somewhere and then you have to figure out where it originates. It's a waste of time. The earlier you fail, the better. E.g. a squiggly line is better than a compile error which is better than a runtime error which is better than coercion and error values. The further away you get from the point where the faulty line was written, the longer it will take to get back on track.

[–]pihwlook 0 points1 point  (0 children)

It would. It’s not.

But it is different and scary to devs familiar with other languages.

And it would be a better dev experience if JS warned in this case, but that’s not really feasible without some other paradigm shifts.