cs-tech-primer

Computer Science tech primer for the University of Manitoba.

View on GitHub

JavaScript Quirks

Javascript gets a pretty bad rap, but remains a VERY popular language… albeit with some quirks…

This should help you start quickly, and dodge a lot of the pitfalls.


Running JavaScript

There are many ways to run JavaScript. Using Node is one of the more popular options. You can download Node here.

Once Node is installed, to run your program:

  1. open terminal,
  2. navigate to the directory your program is in, and
  3. type Node filename.js, where filename.js is the name of the program you want to run.

Strict Formatting

It’s recommended to use strict formatting in your JavaScript files. It helps prevent unwanted accidental declarations of variables.

Without strict formatting enabled, you can declare variables anytime you use the = sign.

With strict formatting enabled, in order to be able to declare variables, you have to use one of the following keywords:

You can enable strict formatting by adding the following text to the top of your file, before any code:

'use strict';

Function / Method Arguments

In JavaScript, you can pass as many or as few arguments as you wish to a given function / method. It’s up to that function / method to determine if the number of parameters / arguments match a given case or not.

It’s considered best practice to check the arguments of all functions / methods.

Signatures are just a suggestion in JavaScript. As such, you cannot have multiple versions of a function / method with different signatures. The last function / method found in your code will be the one to run. Hence, if you want to have multiple versions of a given method, with multiple ways to call said method, you need to add conditional cases to check against.

1 Case

If you only have one case, the common thing to do is to include a conditional at the beginning of your function / method:

function factorial(num) {
    if (arguments.length !== 1 || !Number.isInteger(num) || num < 0) {
        throw new Error('Invalid use of factorial() function.  factorial() '
            + 'takes 1 argument as a parameter, which is a positive integer.');
    }

    // ... implementation
}

Multiple Cases

If you have multiple cases, say for a constructor for instance, you’ll have conditionals for each case:

class Wine {
    #_name; // name of the Wine
    #_age; // age of the Wine

    constructor(name, age) {
        if (arguments.length === 0) {
            this.#_name = '';
            this.#_age = -1;
        } else if (arguments.length === 2 && name instanceof String
            && Number.isInteger(age)) {
            this.#_name = name;
            this.#_age = age;
        } else {
            throw new Error('Invalid use of Wine\'s constructor.  It takes '
                + 'either 0 arguments, or 2 arguments (name, age), where name '
                + 'is a String, and age is an integer.');
        }
    }

    // ... the rest of Wine classes implementation
}

Equality ( == vs === )

JavaScript doesn’t use == the same way you’re probably used to. For example, if you typed the following in C, it’d return false, but in JavaScript, this line of code returns true:

'1' == 1; // Returns true in JavaScript

This was a design choice to make the language more accessible to non-programmers, but it had a lot of unintended side effects. As such, it became important to implement a more traditional equality operation. So, in JavaScript, we use === to check for strict equality (taking into account the data type):

'1' === 1; // Returns false in JavaScript

Another important thing to note is that when using ==, the equality is not transitive. Here’s an example:

A == B; // True
B == C; // True
A == C; // Not necessarily true.  Could be false.

Therefore, best practice is to always use === and !== (instead of == and !=).


null vs undefined

In other languages, when you attempt to access something that doesn’t exist, you’ll get null (null pointer exception, etc.). JavaScript is different; instead of null, JavaScript defaults to undefined. The use of null still exists, but it’s a value that the programmer has to explicitly assign to something. This is useful in debugging because if you get null, it’s potentially an expected null.

Another thing to note is that, in JavaScript, null is an object, and undefined is undefined:

console.log(typeof null); // Object
console.log(typeof undefined); // Undefined

Number Primitive

JavaScript doesn’t have primitives for int, long, float, or double. Instead, all of these fall under JavaScript’s Number primitive.

To explicitly check for an integer, you could use the following:

Number.isInteger(num); // Returns a boolean signifying if "num" is an integer.

Division

JavaScript’s default division operator / performs floating point division, not integer division.

console.log(5 / 2); // Prints 2.5, not 2

If you want to do integer division, use the Math.floor() function.

console.log(Math.floor(5 / 2)); // Prints 2

Importing

The require keyword is how you import dependencies to a given file.

const Person = require('./Person');
const assert = require('assert');

The code above imports two dependencies, Person and assert.

In order to be able to import a file you made, you need to add the following line of code (typically at the bottom of your file):

module.exports = Person

Private Variables and Methods

In JavaScript, variables and methods default to being public.

Underscore

The convention to kindly ask people to not use your variable, or method, is to lead its name with an underscore _:

class Example {
    _dontUseMe() {
        // Implementation
    }
}

However, this doesn’t actually stop people from using said variable or method outside your class.

Pound Sign

The use of # in front of the name of a variable, or method, will make that variable or method private and therefore inaccessible from outside your class.

class Employee {
    #_salary; // Private instance variables need to be declared outside methods

    constructor (name, salary) {
        this.name = name
        this.#_salary = salary
    }

    #calculateBonus() {
        // Implementation
    }
}

Note that private instance variables need to be declared outside of methods.


Duck Typing

The term Duck Typing comes from the phrase “If it walks like a duck, and talks like a duck, it’s a duck.” What this means for programming, is that if you have a function or method that’s working on an Object, or array of Objects, you don’t need to check the types of said Objects. Instead, you can just check if all the Objects contain a particular feature (variable, method, etc.).

Here’s an example:

function printArray(list) {
    if (arguments.length !== 1 || !Array.isArray(list)) {
        throw new Error('Invalid use of printArray() function.  It takes 1 '
            + 'argument, an array.');
    }

    for (let i = 0; i < list.length; i++) {
        if ('toString' in list[i] && typeof (list[i].toString) === 'function') {
            console.log(list[i].toString());
        }
    }
}

Abstract Objects / Methods

JavaScript doesn’t have an abstract keyword. Instead, we need to throw errors if someone’s trying to either create an abstract object, or use an abstract method.

Example:

class Shape {
    constructor() {
        if (this.constructor === Shape) {
            throw new Error('Cannot create instance of abstract class Shape.');
        }
    }

    getArea() {
        throw new Error('Cannot call Shape\'s abstract getArea() method.');
    }
}