The usual distinction between high-level and low-level languages is that high-level languages provide more abstractions. Assembly language is clearly a low-level language, and provides few very abstractions over the raw binary format consumed by the processor. Javascript is very high-level because it abstracts away almost all the details about the particular machine and operating system that it runs on, making it much easier to write actual end-user applications in (but impossible to write, for example, device drivers in).
One particular place where the distinction is particularly obvious is how different languages handle memory. There seem to be three basic strategies for handling memory:
- Implicit or explicit memory management (assembly language, C, C++) - calls to malloc() and free() (or equivalent) are inserted into the code by the programmer or by the compiler.
- Garbage collection with explicit state (C#, Java, Python, millions more) - no malloc() or free() are possible - all memory is "owned" by the garbage collector. Memory management is abstracted away.
- Referential transparency (Haskell) - there is no state, the concept of memory itself is abstracted away.
The boundaries can blur a little - you can plug a garbage collector into C++ for example, and high-level languages often provide "unsafe" functionality that allows one to escape to lower-level features.
[...] are currently two major ways in which programming languages deal with memory. One is explicit deallocation (as used by C, C++ [...]