Table of Contents for Programming Languages: a survey
Because it is so well-known, Javascript gets its own chapter.
Tutorials/online books:
- http://dmitrysoshnikov.com/ecmascript/javascript-the-core-2nd-edition/
- https://github.com/getify/You-Dont-Know-JS
- http://eloquentjavascript.net/
- http://learnxinyminutes.com/docs/javascript/
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript
- note: "The article mentions an idiom for iterating over an array. It says 'an even nicer idiom is...' and then it shows it. I just wanted to say that the behaviour of this 'nicer' idiom may not be what's expected - it stops iterating once it hits the first falsy value. I was quite excited actually when I first saw them idiom, until I quickly realized its limitations " -- https://news.ycombinator.com/item?id=9516585
- https://leanpub.com/javascriptallongesix/read
- http://blog.ustunozgur.com/javascript/programming/books/videos/2015/06/17/how_to_be_a_great_javascript_software_developer.html recommends:
- books: JavaScript the Good Parts, JavaScript: the Definitive Guide (reference), Secrets of the JavaScript Ninja, JavaScript Allongé (freely available online), You Don’t Know JS (freely available online], Eloquent JavaScript (annotated version) (freely available online), Mozilla Developer Network's guide on JavaScript.
- libraries: "jQuery, Backbone, underscore and one of React, Angular or Ember"
- read the source code of Backbone and underscore
- also consider the libraries d3, highcharts, moment.js
- later, exercises eg katas. Mb use the book Pro JavaScript Design Patterns.
- "Try to answer questions such as: How does prototypal inheritance work? What defines a closure? How does the meaning of this keyword change? How does one use apply/bind/map/filter/call? Gather a list of common source points JavaScript? developers face and try to explain it in your own words. Explaining these concepts to another person in written or verbal form will help improve your skills immensely. While doing the exercises, try to go over those where you discover “What if?” scenarios. For example, “What is the meaning of “this” if I use bind twice? How does jQuery make sure that the this keyword refers to the jQuery object and not the global object? How does this library achieve a certain feature?” are some common questions you should be asking yourself."
- (some others recommend https://github.com/ramda/ramda too)
- https://duckduckgo.com/?q=es6+cheatsheet&ia=answer&iax=1
- https://github.com/getify/You-Dont-Know-JS
- https://github.com/bpesquet/thejsway/#
- http://superherojs.com/
- https://github.com/mbeaudru/modern-js-cheatsheet
- http://books.goalkicker.com/JavaScriptBook/ (from the discontinued Stack Overflow Documentation project)
- https://medium.freecodecamp.org/here-are-examples-of-everything-new-in-ecmascript-2016-2017-and-2018-d52fa3b5a70e
- https://github.com/leonardomso/33-js-concepts
- https://medium.com/the-node-js-collection/modern-javascript-explained-for-dinosaurs-f695e9747b70
- https://postlight.com/trackchanges/modern-javascript-for-ancient-web-developers
- http://exploringjs.com/
- https://learnjavascript.online/
- https://blog.bitsrc.io/understanding-call-bind-and-apply-methods-in-javascript-33dbf3217be
- https://threejsfundamentals.org/threejs/lessons/threejs-prerequisites.html
- https://javascript.info/
- https://turriate.com/articles/modern-javascript-everything-you-missed-over-10-years
- https://exploringjs.com/impatient-js/
- https://2ality.com/2021/06/temporal-api.html
- https://javascript.info/
Best practices and style guides:
Respected exemplar code:
Retrospectives:
Features
Variadic functions and reified arguments objects
Weak references
(as of this writing, a future feature)
And there is an option to be notified after the garbage-collection of weak references; see https://news.ycombinator.com/item?id=9735973 .
ES6 overview
Opinions
"I tried Javascript and found callbacks to be a crime against humanity, found a better concurrency model in Go, but found rather simple things that it couldn't express and Python could." -- [16]
- "JavaScript? is not particularily good language for supporting large code bases such as ours, mostly due to its dynamic nature. It requires a lot of attention, discipline and testing to maintain a code base and keep it stable. It also required to write quite a lot of run-time checking code to make sure things won't break because of some small programmer mistake (that actually become a performance problem for us at some point)." -- [17]
- "In Node, i was constantly worried about what is async or sync and the dynamic nature of it made my code feel like the wild wild west." [18]
- "Truth be told, I don’t think I miss Python all that much, at least not while I’m doing Clojure. JavaScript? is a different story, JavaScript? is a mess. But still, if you bend it in the right directions, it can be a fairly decent functional language. I noticed that Python can’t, there’s stuff that just doesn’t work that way (lambdas come to mind), and that probably would annoy me nowadays. So I guess I’m not married to any language anymore. That’s a good thing, right? Q: Your most known projects in Github are written in JavaScript?. Do you like coding in JavaScript?? A: JavaScript? is Frankenstein. As Douglas Crockford showed in The Good Parts years ago, there’s a lot of awful bits and you have to subset the language. That’s gotten worse, because they keep adding stuff to it (some of it really cool, some of it to make it look like Java), and they can’t remove the old stuff, so it has become one of those things you have to agree upfront on which parts you’re going to use and which parts are banned, kind of like C++. But, as said, if you pick the right subset it can be good. eslint helps. If you ban this and new and treat objects like maps, most problems go away. If you’re bold you can even pick a set of eslint rules to force immutability. And then there’s Ramda. That’s the secret sauce; it makes data manipulation a joy, in some spots I even like its functions better than their Clojure counterparts. The async stuff is weird. I still can’t make my mind if Promises are better than callbacks, but I got used to them. It’s a lousy way of hiding concurrency, though. I have a fair amount of Node.js projects in GitHub?, yes. That’s the killer thing about Node.js: NPM, its ecosystem, this philosophy of small unix-y modules. You have an idea, you write up a file and you’re two commands away from publishing it and getting feedback. No other language I’ve tried reduces the boilerplate to share your work that much. That’s a real boost for Open Source and Collaboration. I recognize it has some bad side effects (mixed module quality, left-pad, etc.), but those are much less than the advantages. I remember Python dependency and publishing story being way more cumbersome." [19]
- "JS, despite all the paint on the rust, still has the old gotchas. This is still weird. ";" is still auto inserted. "==" still compares like nothing else. Errors come in different so many different forms it's not funny. Basic maths assumptions like commutativity are out of scope. Still no namespaces, but instead we use monstrosity like webpack and incompatible import systems to encapsulate things. Stdlib still doesn't have essential things like hashing, string/date formatting or encoding. Even basic operation like removing an element from an array using an index is a pain." [20]
- https://ashfurrow.com/blog/javascript-is-good-actually/
- vs. Elm: https://news.ycombinator.com/item?id=18229182
- "easy to start with.... Prototyping implementations with this simple language is so easy, you can always just pop up the node REPL or open up your dev tools! and the developer tools...they're amazing and everywhere and get lots of attention from people who build for the web, which is lots of developers! It's so wonderful." [21]
- "The state of npm modules isn’t different from the state of JS itself — the community has diverged around type systems (Flow vs TS), libraries and everything...The outcome of the lack of “community picks” is that while npm has lots of modules in it, and I mean A LOT, most of them simply aren’t mature." [22]
- "NodeJS?. Memory Safety, Thread Safety, No Deadlocks, No build system nonsense, credibly scalable I/O concurrency, and only ~an order of magnitude slower than C? Absolutely worth the nightmare syntax and (then) lack of decent libraries. As soon as npm came on the scene (0.6?) Java ceased to have any reason to exist." [23]
- "Tagged unions still require rather cumbersome JavaScript? syntax to use." -- [24]
Criticisms
- "Implicit coercion is always iffy, and JS's coercion rules are broken along the same lines as PHP's, where == and === have to be carefully managed. There's no actual identity check, only two different strengths of equality." [25]
- "Booleans coercing to strings instead of strings coercing to booleans is weird." [26]
- "There's no operator overloading" [27]
- "There are no metaclass operations. The type model is incomplete; it's not possible to create new first-class types or query type information respecting inheritance. For that matter, there's no blessed way to have inheritance. Makes sense since the language doesn't have classes per se, but it's kinda annoying in an object-based language to not be able to actually examine objects in a unified way." [28]
- "...you can't define non-enumerable properties. Right? You know how you can go for (i in foo) ..., and it'll enumerate the keys of your object as if it were a hashtable. Nice feature, right? And you can add properties to objects; you can go to Object.prototype, which is the root object of the whole system, and add your own function(s) there. But what happens is, you've added a function that's now enumerable in everybody's arrays, and everybody's data structures, so their for..in loops break." [29]
- "Semantics and configurability is what what makes a language great. JS doesn’t have function environments, i.e. every unlexical lookup goes to global/window and that cannot be redirected. It doesn’t have green threads (at least). There are generators, but one cannot just yield without marking all the functions generators too (async/await in modern terms). Stack traces are lost when generators throw(). JS has no good introspection — you can do some Reflect, but cannot e.g. write a module that has a function, that when called enumerates all functions in a caller module and exports them by specific criteria. JS can’t properly substitute objects with proxies. It can catch simple existing key accesses, but can’t enumerate keys or arrays. There was handler.enumerate, but it was deprecated. Vue, which was written by no fools I think, cannot “app.array[3] = v” or “app.newkey = v”. It cannot dynamically watch an entire app too, all structure has to be defined before Vue(). Other problems exist, which are just stupid (null/undefined, in/of, ===, ;, this, etc) or minor design choices like coersion and lexical rules. But these above are stoppers for turning JS into something great. It is now a BASIC-level language with scoping and sugar. Yeah, you can write like that snippet presented in an article, but you are forever stuck with doing all things by hand, from asyncing to orm, from exporting to update scheduling. I don’t find that great in any way, especially when that is the only semantics you have in a browser." wruza
- "The generators or async/await in JS are 'shallow' coroutines because you can only `yield` or `await` in the direct scope of a generator or async function, but the benefit of shallowness is that the control flow is explicit; you don't have to consider whether a function call will suspend the calling context's execution or not. I find the explicit clarity to outweigh the reduced power of shallow coroutines. As an aside, there exist green threads in JS, namely node-fibers, although it's only a Node.js extension. You actually can reflect on module exports and dynamically change them with node modules; you can't do it with ES modules, but that has the significant advantage of enabling static analysis. Proxies absolutely can trap the property assignments you mentioned, but Vue can't take advantage of this because Proxy can't be polyfilled in older browsers. As for the enumerate handler, it would only have worked with for-in loops, which are a legacy feature. The iteration protocols used by for-of are a much more flexible solution. It might seem silly to have both for-in and for-of loops, but the context of the language is that it can't just go and break older websites. Same goes for == and ===, etc. Linters come in very handy for dealing with this. Your criticism is better than most, which usually just point out some "wat" moments with mis-features like implicit coercion, but you didn't really make a case for having to do "all things by hand" in JS." slikts
- " Thanks for node-fibers, that is something I missed and it looks promising, should try it server-side at least. But I'm not sure what you mean by "reflect on module exports", since there seems to be no way to enumerate all functions (in node), except those exported by hand. I workarounded it via "autoexport(module, x => eval(x))" and " @export" tags, but it feels dirty. I also bet that I couldn't make 'in' work for proxy with empty abstract target, but maybe it's just me. Btw, 'in' and 'of' are two separate iterators, one iterates array elems and the other iterates object's keys -- something essential to metaprogramming. My whole point on in/enumerate is that it considered legacy by someone special. And on Vue: I didn't know that, but if Vue can't take advantage of Proxy, can I? " wruza
- "> You actually can reflect on module exports and dynamically change them with node modules; you can't do it with ES modules, but that has the significant advantage of enabling static analysis. And yet even that advantage got thrown out from the language with the introduction of "import()". Apparently static analysis is a non-goal (see discussion in [1]). [1]: https://github.com/tc39/proposal-dynamic-import/issues/35" lambdadmitry
- "JavaScript? is way too dangerous without a linter. For example, while Elixir’s == converts between floats and ints and that’s it, JavaScript’s? is just broken" -- https://elixirforum.com/t/is-elixir-a-good-first-language/6366/9
- "...I miss having an object model that allows customisation of hashing, equality etc" -- [30]
- "...keys have to be strings...One simple example of a key that is hard to use strings with is a composite key of 2 strings. OK, you could concatenate them using a character you know won't be used in either string, but what if it's user input?... It's much easier to have a clear way to customise the hash function IMHO." -- [31]
- "ES6 has a Map constructor [32] that can have any type of value as key."
- ""Any type of value as key" is quite misleading since anything other than primitive types (number, bool, string) is compared by reference. Observe:
> const m = new Map();
undefined
> m.set([1], "one");
Map(1) {Array(1) => "one"}
>m.get([1])
undefined
C++ and Rust don't stand for this nonsense. You can't even define a custom hashing function, which means if you want a useful map for anything other than numbers and strings you have to write a wrapper around `Map` that hashes the objects to numbers or strings, and then you're more or less back to raw objects.
It's definitely better than raw objects because it doesn't mix up data and methods, but it's not much better. " -- [33]
Opinionated comparisons:
- "I've settled into sort of a hierarchy. If I really want to hack out a small prototype quickly, I use NodeJS?. Not having types helps me change things around quickly. If I already have a pretty good idea of my data model but still want to develop quickly, I'll use Go. If it's something 1.0.0+ and I want to make it as reliable as possible, I'd use Rust. My problem seems to be few of my projects ever get to that stage, so I'm mostly writing Go these days... " [34]
- vs .NET: "The Dependency Problem Unlike .NET, JavaScript? does not have a rich set of base class libraries. It has been dependent on the community to fill that gap by writing open source projects and publishing shared packages to NPM. Whereas in the .NET ecosystem, Microsoft provides a rich set of professionally developed and curated first party libraries for many, many scenarios, JavaScript? has no such governance. This model has its benefits as it allows for innovation and creativity at a much faster pace. (In fact, one could argue that it has forced Microsoft to move faster and be more open with .NET Core.) But the downside of that lack of governance has many, many deficiencies. One of which is an explosion of the dependency chain: We all know the pain of managing node_modules. Hundreds of dependencies and often hundreds of megabytes of space. In an ecosystem with strong governance in place, perhaps we would see some of these libraries rolled into a curated and well maintained core set of libraries that is less polluted and less sprawling. This in and of itself may seem like a minor annoyance, but this leads to our next problem... The Security Problem Because there are layers and layers of dependencies deep in the bowels of your code and because of the nature of JavaScript? (e.g. Prototype Pollution), it creates these scenarios where vulnerabilities and even malware can be introduced to your code! The Performance Problem To add insult to injury, JavaScript? isn’t particularly performant. ... While Node.js has an advantage in cold starts, the runtime performance of .NET is among the top 3 in these benchmarks and often 2x faster than Node.js. ... engineers that write poor JavaScript? because now they are reliant purely on the tooling to provide productivity instead of using the tooling to improve productivity and using plain old good practices for organizing code, naming things well, encapsulating logic, and so on. ... I find that in the hands of inexperienced developers, TypeScript? adds to the mess (particularly indiscriminate use of operations like Pick). The current trend of using arrow functions everywhere is absolutely killing me and I’m not the only one. It makes code unreadable when developers think that function is apparently a dirty word. It can actually hamper productivity when the type system is tacked on as an afterthought. " -- [35]
Equality with implicit type conversion
Javascript is often [36] [37] criticized for its '==' operator, which is equality after implicit type conversion. Javascript also has an '===' operator which is a more typical equality.
Links:
Javascript criticism links
Other lists of criticisms
Gotchas
https://t.co/oK7NETXW5A
" ...All too often, people try to do something like declare a var inside a loop, and spin off a closure that captures that var. The reasonable assumption is that the scope of the variable is the body of the loop, and thus every iteration gets its own new copy, and all closures are independent.
In reality, all closures get the same variable, and if it is the loop counter or derived from it, and closures are executed later, then they will all see the same value - the one that said variable had after the last iteration. It's even worse if closures try to write to it.
This is something that is peculiar to JS. In most other languages, there's no similar issue, because either local variables can't be declared inside loops (or, indeed, at all, treating assignment as implicit declaration), as in Python or Ruby; or if they can, then their scope is what you'd expect it to be, as in C#, Java etc.
Writing async code using closures as continuations is not unique to JS, either. All mainstream languages support this now.
z3t4 65 days ago [-]
What you describe is the moment someone would usually go from PITA to Ninja! From programming sequential to async using functions. The WTF code:
for(var i=0; i < textures.length; i++) {
textures[i].onload = function (i) {
pattern[i] = ctx.createPattern(textures[i], 'repeat');
}
}
The enlightenment:
for(var i=0; i < textures.length; i++) {
createPattern(i);
}
function createPattern(i) {
textures[i].onload = function() {
pattern[i] = ctx.createPattern(textures[i], 'repeat');
}
}
In JavaScript? you can now however replace var with let and it will be block scoped and the WTF code will work. It's sad though as people learning JavaScript? now will not get the beauty of it and will be stuck in callback hell. Function scope in JavaScript? pushes you in the right direction. It's like instead of teaching people how to fly, we give them roads so they can walk, like they are used to.
int_19h 62 days ago [-]
It's way more than just async callbacks. For example, you may be using a lazy sequence abstraction, with high-order functions like the usual map/filter/reduce, but with a deferred implementation (i.e. they're only applied, and functions passed as arguments are evaluated, when the next item is read from the sequence). If you create such deferred sequences inside a loop, they will again capture the mutated counter.
" [38]
for (k in ['a', 'b']) {
console.log(k);
}
returns undefined, and print "0", "1" (eg, the keys of the list-like associative array) to the log.
piva00 38 minutes ago
...JavaScript? has a lot of quirks like the '==' vs '===', empty arrays equalling to false or this kind of stuff: http://stackoverflow.com/a/1995298/398142
...
" There's plenty not to like about JavaScript?, but the biggest mess is this:
js> x = 1 1 js> z = x.y js> ...
In other words: it's not an error to access an attribute of an object which isn't there ...
The fact that sometimes unknown things are 'undefined' and sometimes they're 'null' and sometimes they're '"undefined"' and sometimes they're the empty string and sometimes they're 0 really compounds this problem. Python has None, Ruby has nil, and nobody uses random ad-hoc sentinel values because why would you do that? " -- [39]
vs node:
- [40]
- Unexpected values of this [41]
- undefined and null are different; undefined can actually have a value [42]
- Complex but powerful scope and closure rules [43]
- ... ((NaNs?)) "were wreaking havoc with the sort algorithm. They aren’t equal to themselves or anything else, so they broke the sort’s needed transitivity." [44]
"Basic maths assumptions like commutativity are out of scope.
...
[] + {} '[object Object]' > {} + [] 0" [45] [46]
https://lea.verou.me/2020/05/todays-javascript-from-an-outsiders-perspective/ https://news.ycombinator.com/item?id=31526370
Links:
Open Source Implementations
JIT implementations:
Implementations written in Javascript:
- Narcissus (written in JavaScript?)
- MetaES "metacircular interpreter written in JavaScript? at EcmaScript? 5.1 standard"
- js.js "Instead of trying to create an interpreter from scratch, SpiderMonkey? is compiled into LLVM and then emscripten translates the output into JavaScript?."
Minimalistic or embedded implementations:
Lists of implementations:
Random notes:
Popular libraries
Frameworks:
- react
- angular
- ember
- jquery
- backbone
Language upgrades:
Lists:
Things that compile to Javascript
eslint-config-cleanjs
" ... a subset of JavaScript? which would be as close to an idealised pure functional language as possible. The idea is to see if it's possible to banish all the Bad Parts (well, except for the single numeric type being IEEE 754 floating point) and leave a language without the design flaws which have plagued JS from the start, and which aren't easy to design out of the language without becoming a subset of itself.
...
Highlights:
no this and no classes
no null and undefined (implying that all functions must return)
no mutation of any kind
no variable reassignment
no statements, only expressions (including no if)
no CommonJS or AMD, only ES6 modules"
Typescript extension
Tutorials and manuals:
Papers:
Comparisons:
Opinions:
" novaleaf 2 hours ago
I love Typescript, I do all my javascript coding via it.
However, Though I wish it were, Typescript is NOT currently suitable for defining modules for external consumption. The problem comes down to no effective means of publishing the typings of your project and your project's dependencies. For example, if your project uses Promises, you might choose to include a definition of those promises, or (worse) reference a Promise typing you found on DefinitelyTyped?. This will work fine for you, the publisher. But any consumer of your project will be rudely greeted with typing collisions: Things like "The interface 'Promise' already exists in es6.d.ts"
There needs to be a solution to this module publishing problem before people can seriously publish modules (using NPM) using Typescript. Unfortunately, I have been tracking this issue and there is no timeline for resolving it, mostly due to too many different module systems, and handling module publishing being outside the design-scope of Typescript.
reply
lloyd-christmas 1 hour ago
typings[1] does exactly that as long as people start publishing their definitions (which is kind of the point of them). Relying on un-versioned ambient declarations is what breaks things, not having dependencies.
[1] https://github.com/typings/typings/blob/master/docs/registry.md
reply "
" nothrabannosir 3 hours ago
....Compiling TS just removes type annotations...TS does not add any semantic features, it only adds type annotations. Which it checks at compile time, and then removes. That's it. Want to switch away from TS? Compile all your .ts files, save the .js, check that into your repo and continue from there. (As other commenters sort-of pointed out: you will need to keep Babel in your pipeline.)
Eridrus 3 hours ago
Sort of. I don't recall the details from the top of my head and can't easily seem to find details, but I think we're seeing some divergence between Typescript's classes and es6 classes, though I think it's mostly syntactic.
Your code will keep working, but it may diverge a bit from standard JS; however everything will keep working, and I'm sure the expectation would be to move over the es6's syntax unless there were semantic reasons not to.
The Typescript authors are involved in the ECMAScript process, so I'm pretty sure that there won't be any surprising huge rifts at least.
reply
chadaustin 3 hours ago
I don't know why you're being downvoted. You're exactly right - there are a few tiny divergences between ES6 and TypeScript?. In particular, try compiling some classes with Babel's strict compatibility mode and compare the output to TypeScript?'s output. There are several small semantic differences.
Also, there a few constructs in TypeScript? that generate JavaScript? code, like enum and module.
THAT SAID, TypeScript? is basically ES6 + type annotations, and can certainly be used as such.
reply
rtpg 5 hours ago
I can absolutely understand that coming from coffeescript (optional parentheses resolution... yikes).
But TypeScript? is basically "Javascript + types + ES6". They call it an "erasing compiler" because it's not meant to do much but remove types/make ES6 code work with ES5.
There is one gotcha in name resolution when you're working in modules (if you are in a module a.b, and a.c exists, then c will automatically refer to a.c, even if a global c exists). But that usually gets caught by the type system. Lot less issues than coffeescript IMO
reply
wereHamster 5 hours ago
With `--target=es6` I'm not sure if the TypeScript? compiler does any code transformation other than removing type annotations. So the chance of a 'transpiler error' is basically zero.
reply
bd82 4 hours ago
There are a few small things that still require conversion. But that is because Typescript also includes capabilities from ES7 (ES2016).
For example - currently in Typescript but only planned for ES7: https://github.com/jeffmo/es-class-fields-and-static-propert...
or Support ES7: exponentiation operator https://github.com/Microsoft/TypeScript/issues/4812
Typescript seems to be more like ECMAScript.next + types rather than ES6 + types.
reply
joshuacc 3 hours ago
FYI, those are not ES2016 features. ES2016 only includes two small feature enhancements. They might make it into 2017, but if TC39 decides they're not ready, they'll just keep not including the feature in the annual spec revision.
reply
ascetone 4 hours ago
My favourite thing they brought over from ES7 is async/await support via ES6 generators.
reply
"
"TypeScript?'s compiler is an unholy, unsound mess, because JavaScript? is an unholy, unsound mess. Sure the basics of type checking structs and variables is somewhat easy. But what about conditional types? What about weird generic constraints? What about indexing types? There's some really confusing stuff going on underneath the hood.
It's also a mess that *does not have a spec*. Even if you somehow manage to make a type checker that implements most of the features, there's no way to ensure it has 1 to 1 parity with TypeScript?. Plus that parity can be broken at any point." [54]
Type repositories:
Typescript tools
Typescript variants
STS typescript variant
- https://www.microsoft.com/en-us/research/publication/static-typescript/ "Static TypeScript? (STS), a subset of TypeScript?..." for "microcontroller-based embeddable devices"
- "STS eliminates most of the “bad parts” of JavaScript?;following StrongScript? [14], STS uses nominal typingfor statically declared classes and supports efficientcompilation of classes using classic techniques for v-tables." [55]
- "STS is a subset of TypeScript? that excludes many of thehighly dynamic parts of JavaScript?: thewithstatement, theevalexpression, prototype-based inheritance, thethispointer outside classes, theargumentskeyword, and theapplymethod. STS retains features such as dynamic maps,but keeps them separate from the nominal class abstraction.Such restrictions are acceptable because most beginners’programs are quite simple, and there are very few existingJavaScript or TypeScript? libraries in the embedded space, sothe chance of these using JavaScript? features such as monkeypatching is small.Our goal is not to grow STS to the point of supporting allof TypeScript?. Instead, we follow the pragmatic approach ofadding features useful in embedded context, as guided byuser feedback.In contrast to TypeScript?, where all object types are bagsof properties, STS has at runtime four kinds of unrelatedobject types:1.adynamic maptype has named (string-indexed) prop-erties that can hold values of any type;2. afunction(closure) type;3.aclasstype describes instances of a class, which aretreated nominally, via an efficient runtime subtypecheck on each field/method access, as described below;4. anarray(collection) type.In this sense, STS is much closer to spirit to Java and C# intheir treatment of types as “protectors of abstractions”, unlikeJavaScript which allows a much more freeform treatment ofan object’s role. As discussed in Section 3.4, runtime typetags are used to distinguish the different kinds of built-inobject types listed above (as well as primitives like boxednumbers and strings).As in TypeScript?, type casts do not generate any code, andthus always succeed. Instead, STS protects the nominal classabstraction at the point of field/method access. Just asx.fcauses a runtime error in JavaScript? whenx == null,executing(x as T).fwill cause a runtime error in STSifTis? a class with fieldf, and the dynamic type ofxis nota nominal subtype ofT. IfTis? an interface,any, or somecomplex type (eg., union or intersection), then the field willbe looked up by name regardless of dynamic type ofx.Other abstractions are also protected at runtime, as inJavaScript: for example, making a function call on somethingthat is not offunctiontype. Currently, it is an error to dy-namically add a new property to any type other than themaptype. Restrictions to the dynamic JavaScript? semanticsmay be lifted in the future, depending on user feedback. Todate, these restrictions have generated no concerns amongour user community (both educators and developers)." [56]
- "3.4 Virtual Table LayoutSTS? classes are compiled similarly to Java or C# with singleinheritance and static memory layout. The first word of anobject points to a virtual table, and subsequent words areallocated for fields (with fields of the base class, if any, com-ing first). The virtual table contains object size, a staticallyallocated class number (this includes the tags for the builtintypes supported by STS, as well as user-defined types), apointer to the interface table and interface hash (see below),followed by pointers to methods.The first four method slots are pre-allocated for runtimeand GC-related functions related to object marking and de-allocation, which follow C++ calling conventions. The re-maining functions follow STS calling convention. First ofSTS method pointers istoString()if defined7. Next thereare methods from the base class if any, followed by methodsof the current class. If a method is never called with dynamicdispatch (for example, because it is never overridden, ornever called), it is not included in the table (for example, inthe MakeCode? Arcade game engine only 13% of methods usedynamic dispatch).The interface table contains descriptors for all fields andmethods of the class in order of definition. Each descriptorcontains the member index (assigned globally to all membernames in the program) and a function pointer. Field descrip-tors also contain the offset of the field within the class. Thedescriptors are used in member lookup and also when iter-ating over object properties (eg., usingObject.keys()).The descriptors are indexed by a simple hash table, wherethe keys are computed by multiplying the member index bythe interface hash multiplier (computed per-class at compiletime) fetched from the virtual table.STS employs three methods of member lookup:•when the receiver is statically of a class typeC, thecompiler generates a runtime subtype check againstC(which may fail, as the dynamic type may not be asubtype ofC) and then uses a direct offset into eitherthe virtual table for methods, or into the object itselffor field accesses;•otherwise, if the receiver is dynamically of a class type(statically it could be an interface,any, or some morecomplex structural type), the member descriptor islooked up in the interface table using the memberindex and the interface hash key; otherwise, it is a type-specific function for objectsimplemented in the C++ runtime, in particular thedynamic map used when compiling object literals ornew Object()"
Assemblyscript typescript variant
https://github.com/AssemblyScript/assemblyscript
Atscript extension
"To sum up, AtScript? is ECMAScript 6 with types, annotations and introspection." -- https://en.wikipedia.org/wiki/AtScript
"
- Type Annotations: ...
- Field Annotations: Types are not only about contracts (API) but also about structure. In JavaScript? the fields are implicit; they are created as a side effect of assignment. By giving the developer syntax to explicitly declare the fields, and warning upon implicit creation, we allow them to think about types in a more concrete way....
- Metadata Annotations: ...
- Type Introspection with Annotation Support
...
Familiar Syntax: We desire to use a syntax which has been tried, is time tested and is most likely to become the next standard. This means using ':' to indicate type annotations (proposed in ES4 and used by TypeScript?), '@' for metadata annotations (used by java, dart and others) and 'name:Type;' for field annotations (used by java, dart, TypeScript? and others). ... Semantics Agnostic: Type system theory is a complex field with many tradeoffs. It is our explicit goal to leave the semantic discussion as well as the assertion system outside of the scope of AtScript?. Instead we want to provide hooks for others to build runtime type assertion libraries and static analyzer tools with their own semantics. In this way we believe that we can spur innovation in the area of type systems by lowering the barrier to entry.
...
Nominal types were originally proposed for JavaScript? in the ECMAScript 4 specification. Though this was never implemented in JavaScript?, it was adopted and widely used via Adobe's ActionScript?3 and inspired others. e.g. TypeScript? adopted the syntax, but uses a structural type system semantic.
...
One way to take advantage of the types is to have the transpiler, e.g. traceur, generate type assertion statements as shown below. Notice that the transpiler is unaware of the semantics of the types, and it leaves all of that information to a third party rtts library. Leaving the assertions to a pluggable library will allow others to experiment with different type system semantics.
AtScript?: class MyClass? { methodA(name:string):int { var length:int = name.length; return length; } }
ES6: import * as rtts from 'rtts';
class MyClass? { methodA(name) { rtts.types(name, rtts.string); var length = rtts.type(name.length, rtts.int); return rtts.returnType(length, rtts.int); } }
Why use a runtime type system?
It allows free mixing of existing libraries which do not have type annotations with new code which can take advantage of types. The incomplete type information would prevent useful static analysis of code but runtime type checks do not suffer from such limitations. The semantics of types can be left to the implementor of the rtts library. This means that different libraries can choose different strategies of identifying types. e.g. nominal vs structural. Our hope is that developers will be able to experiment with new ways of using types. Runtime type verification can be used to verify that the JSON returned from the server is of a valid structure, something which cannot be done statically. Since the type errors should be caught and resolved before pushing the code to production, all of the assertions can be stripped from production builds, completely removing the cost of these assertions. Prior Art This runtime type system is modeled after Dart’s runtime type assertion. Dart users working on large scale applications point out that optional types and runtime type assertions work well in practice.
Generics ... methodA(names:List<string>):List<int> { ... methodA(names) { rtts.types(names, Array.of(rtts.string)); ...
Field Annotations
As mentioned earlier, explicit field declarations are needed to fully describe the type’s structure. This syntax has been proposed in TypeScript?, and should be familiar to most developers.
AtScript?: class Point { x:int; y:int; }
ES6: class MyClass? { constructor() { this.x = null; auto-generated this.y = null; auto-generated } }
By specifying the fields in this way, the transpiler can generate the constructor which then pre-creates the fields. By creating the fields in the same order it will allow most virtual machines to optimize their hidden class system, thereby enhancing performance. Forgetting to declare a field will still produce working code, but the static analyzer could generate a warning to notify the developer of the potential mistake.
" -- https://docs.google.com/document/d/11YUzC-1d0V1-Q3V0fQ7KSit97HnZoKVygDxpWzEYW0U/edit#
todo continue reading and taking notes starting with section 'Metadata Annotations' in the previous document
Links:
Flow typechecker (made by Facebook)
http://flowtype.org/
Comparisons:
" TypeScript?
- Pessimistically assumes most JS code is statically untypeable
- Only typechecks code that is explicitly annotated
- Designed mainly for IDE tooling; unsound whenever convenient
Flow
" Pros for TS:
- Really slick Visual Studio integration
- Support for typing of third-party js (but all those .ts files are a pain)
- One-stop type checking and transformation
Pros for Flow:
rtype type annotations
https://github.com/ericelliott/rtype
PureScript (typed) extension
http://www.purescript.org/
Coffeescript extension
Preemption by ES6?
Many are of the opinion that ES6, which came after Coffeescript, learned from it and incorporated many of its best parts into Javascript. [57] [58]
ES6
An update to Javascript including:
- Module syntax including imports (see caveats)
- destructured parameters (see [59])
- let and const (see [60])
- string interpolation (see [61])
- classes (see [62])
- concise => anonymous function declaration syntax (see [63])
- promises (see [64])
Links:
ES7
An update to Javascript including:
- await (see [65])
- Object.observes (see [66])
Links:
Internals
Source maps
Not actually part of Javascript, but often used for other languages that compile to Javascript.
A source map file is a JSON containing (much of this is optional):
- the version of the source map file format that this file is
- the object file being mapped
- a list of source file paths, and optionally a common prefix for their path
- a list the same length as the previous one, containing for each source file, the actual text of the source (the contents of that file) (in case it is not stored in an accessible file), or 'null'
- a list of all names found in the source
- the mapping data, see below
The mapping data consists of one 'group' for each line in the object file. Each group consists of 'segments'. Each segment has:
- the starting column of this segment in the object file
- which source code file this part is from (an index into the list of source file paths/source texts)
- which starting line in that source code file corresponds to this segment (note: "Using file offsets were considered but rejected in favor of using line/column data to avoid becoming misaligned with the original due to platform specific line endings")
- which starting column in that source code file corresponds to this segment
- an index into the 'names' list
All items in the segment are optional except for the first one (the starting column in the object file); however, if the second item (the source code file or text) is present, then the third and fourth are too (the source code line and column).
All indices (columns, lines, etc) are zero-based.
Many of the indices are encoded relative to the previous occurence of the given field. For example, line numbers are encoded as deltas from the last mentioned line number.
An encoding called "base 64 VLQ" is used. The spec constrains all numbers to fit within 32 bits.
Source maps can be gzipped.
Optimizing JIT compilers
https://mathiasbynens.be/notes/prototypes#tradeoffs
Variants/subsets/alternate implementations
- http://duktape.org/
- https://github.com/pando-project/jerryscript
- node.js
- https://bellard.org/quickjs/
- https://github.com/dbpokorny/autoclave
- AssemblyScript (a variant of TypeScript? that compiles to WebAssembly?)
- https://github.com/swc-project/swc
- https://en.wikipedia.org/wiki/Source_(programming_language)
- Elk: a tiny JS engine for embedded systems
- "Not supported features: No var, no const. Use let (strict mode only) No do, switch, for. Use while No => functions. Use let f = function(...) {...}; No arrays, closures, prototypes, this, new, delete No standard library: no Date, Regexp, Function, String, Number "
- " This implementation is nuts. A decent chunk (but still very small subset) of ES5 in a single file, under 1400 lines of very readable C code. It includes a mark-and-sweep GC and an FFI. It doesn't have an AST or a bytecode VM. It just interprets directly off of source code. Take a look: https://github.com/cesanta/elk/blob/master/elk.c . The first time I scanned through the file and thought there was nothing there. It does an entire implementation in the same amount of code that most implementations do a parser alone (but yes, it implements way less than a 100% spec compliant parser does). This implementation really sets a new bar for me in terms of compact-but-readable language implementations. Separately, this isn't even Cesanta's only embedded JavaScript? implementation. They also have: https://github.com/cesanta/mjs. mjs is a bit more complete and does have a bytecode VM and thus the source has many more lines of code and files." [68]
- discussion: https://news.ycombinator.com/item?id=28614092
- low.js (embedded port of node.js)
gpu.js
Compiles a subset of JavaScript? to GPU code.
" gpu.js relies on the assumption that the kernel function is using only a subset of legal JavaScript? syntax:
1D, 2D, 3D array of numbers or just numbers as kernel input
1D, 2D, 3D array of numbers as kernel output
Number variables
Arithematic operations (+, -, *, /, %)
Javascript Math operations (Math.floor() and etc.)
for loops (of fixed sizes only!)
if and else statements
No variables captured by a closure" -- http://gpu.rocks/
later revision: " 1D, 2D, 3D array of numbers or just numbers as kernel input or output Number Variables Custom and custom native functions Arithmetic operators (+, +=, -, *, /, %) Some Javascript Math functions like Math.floor() (See the Docs) Math.random() is supported but it isn't perfect (See the Docs) Pipelining (See the Docs) for, while and other loops if and else statements const, let and var variables NO variables captured by a closure "
Links:
strong mode
A Strong Mode for JavaScript (Strawman proposal)
Abandoned. Experience report:
Strong mode disallowed various behaviors that were thought to stand in the way of performance, such as:
- undeclared variables
- the keyword 'var'
- 'delete' (dynamic deletion of properties)
- non-lexical scoping
- accessing non-existent properties on an object
- properties can still be dynamically added, but only via ‘defineProperty’, not via assigning to a non-existent property
- implicit conversions other than ToBoolean? and ToString?
- mutating ‘__proto__’ (dynamic setting of the prototype)
- changing the 'writable' attribute of a non-configurable property from 'true' to 'false'.
- arrays that don't act like convention arrays:
- creating holes on arrays
- having array ‘length’ property get out of sync with their actual length
- array properties which are accessors or inherited
- the implicit 'arguments' variable
- rebinding of function or class names
- calling a function with fewer arguments than it requires
- using 'this' in a constructor other than as the object targeted in a property assignment
- using 'super' in a constructor other than to invoke the super constructor
- not invoking 'super', before any property assignment to 'this', in a class that extends another class
- invoking 'super' more than once in a constructor
- rebinding ‘undefined’ to a value other than ‘undefined’
- use of 'eval'
- fall-thru in 'switch' statements
- "slopply equality" (== and !=; instead, === and !== should be used)
- 'for in' loops ('for of' should be used instead)
Instances of classes defined in strong mode ('strong classes') are sealed.
Some of the reasons that they gave up on this were:
- sealing instances of strong classes made it hard to interoperate with non-strong javascript when it was desired for a strong class to inherit from an ordinary class, or vice versa
- they hadn't yet come up with a way to do scoping checks that didn't mess up mutually recursive classes
- ES6 classes lack property declarations, but in order to seal strong classes, you need to staticly know what properties they will have
- making use of this while still supporting ordinary Javascript would require adding zillion special cases spread throughout a Javascript implementation
Implementation-specific internals
Links: