Learning Diary - Nelson Chan For when I can't remember everything.

Importing things in Javascript

When I first started learning Javascript, the complexity of importing things is mind-blowing. Nowadays we often rely on a build system to handle everything for us, but it would be a nightmare to figure out how the project and its dependencies are put together. So I tried to figure out how things are the way they are.

Lack of a standard library

I don’t know the entire history of Javascript, but it appears that the main focus of its development had been to build a flexible, minimal core with easy to use syntax, so that it is extensible. The result is that many simple functions are left to be implemented by the programmer. Library or packages became a great way to handle commonly used functions, so NPM became really popular. A convention was evolved such that a package would do one thing as good as possible, and that thing only. That way people using it can get the exact functionality they need. But with this huge amount of packages, a standard way to import them became necessary.

CommonJS and Node.js

CommonJS was one of the first attempt to create an ecosystem of modules for javascript running outside the browser. It is a specification of how modules should be defined, and centers around the require('someModule') syntax. Node.js improved upon that and built npm. The problem is that this method of using a string to identify the module has a prerequisite that the module needed already exist in the current namespace. This would be problematic on the browser as different pieces of script may be downloaded and executed at different times, particularly when using XHR to load modules.

Asynchronous Module Definition (AMD) and RequireJS

AMD tries to improve by encapsulating modules with define(), which registers the function first without needing to execute it. That way they can be executed in the right order once everything is loaded. This made it useful for client-side javascript development.

Webpack

Then one day, Webpack comes along and takes the abstraction up another level. It works by walking through your files, parsing all the require('') tags, building a dependency tree, and then compiling them down to one (or more) file. But what happens when you have typescript, coffeescript and other non-compatible files that needs to be imported? Webpack then has loaders to handle transpiling these back to javascript, and also allow things like babel-loader to add compatibility to new language features. Furthermore, you can add plugins to Webpack, which modifies the operation of Webpack itself, such as webpack-visualizer outputting statistics about the output size and composition. It basically solves the entire process of from development to deployment, and hence became so successful today.

Wiping Free Space

Wiping the free space of a drive is useful when the drive is not encrypted, and you want to make sure deleted files cannot be recovered. Wiping multiple times is mostly useless now, as the original analysis by Peter Gutmann, which found that their expensive equipment can sometimes detect the original value of an overwritten bit, was done on obsolete hardware, whereas modern hard disks are made with much higher densities and tighter tolerances. Filling all empty space with 0s will most likely be enough.

It is important to note that this is not a fault-prove method, as there may still be data left in relocated or bad sectors. Physical destruction or full disk encryption would be the preferred method for better security.

Windows:

cipher /w:C:\

Linux:

cat /dev/zero > zero.file
sync
rm zero.file

The Symbol type in Javascript

Discovering many quirks of javascript, symbols being one of them. Functionally, they look like a const string, and can work mostly like a Ruby symbol, which is used to access Object instance variables and hash values.

# from rubyguides

    class Food
     attr_accessor :protein
     def initialize(protein)
       @protein = protein
     end
    end

    bacon = Food.new(42)
    bacon.protein
    #-> 42
// from javascript.info

const id = Symbol("A description")
let user = { name: 'John', [id]: 42 }

user[id]
//-> 42

The description will only show up when you use toString() on the Symbol, and is not related to accessing it.

However, symbols in JS are default locally scoped, so if the another scope does not have access to the const id, code there will not be able to access this property, even if they create another symbol with the same description. There are ways around that, like getOwnPropertySymbols(), but probably has little practical use.

In order to create a Globally scoped Symbol, another syntax must be used.

const id = Symbol.for("A description")

The same for() function is used to both create and access the symbol. Here the description will globally identify this symbol.

As a side note, Typescript will complain if you define a symbol with let or var, but is fine with const. It make sense as you are not suppose to mutate it after creation, but there seems to be no documentation on this.

Why is it created?

Symbols are unique in that they sit somewhere between a string and an object. Like a string in that they don’t have any properties, like an object in that every one is unique. They can help with encapsulation of data within features, and prevent confusion with string conversions.

Unfortunately, the syntax user[id] seems quite non-standard, and the not very Object-Oriented nature of JS probably limited its use.