JS 139 Review Sheet

JavaScript Topics

Hoisting

Hoisting is what happens during the creation phase of a program. Hoisting makes the code act as if all variable, class, and function declarations are moved to the top of their respective scopes (function for functions and var, block for class, let, and const). It doesn’t change the program, just acts as if things are rearranged. Hoisting is a mental model for what’s happening during the creation phase of JS engine operation. In the creation phase, the JS engine goes through the program from top to bottom, finds all declarations, stores the identifiers and designates their scope. Then during execution phase, when the engine executes the code line by line, JS accesses the stored information — allowing you to do things like run functions before the function appears to have been declared. Syntax errors are found during creation phase.

Phases of JS Engine Operation

  • Creation Phase: finds all the variable, function, and class declarations in the program, stores their names, and designates their scope. Also stores the function definition.
  • Execution Phase: runs the code from top to bottom.

Hoisting for var

  • declaration hoisted to top of function scope, identifier stored
  • Initialized to undefined
console.log(foo) // logs undefined
var foo = 2;

Hoisting for let, and const

  • declaration hoisted to the top of block scope (identifier is stored, scope is determined)
  • Not initialized until execution phase. If you try to access it before it’s initialized, you get a reference error. In phase before initialization, the variable is in the temporal dead zone
console.log(foo) // cannot access ‘foo’ before initialization
let foo = 1;

Hoisting for functions

  • Both the name and definition are hoisted to top of function scope
// function declaration
doSomething(); // logs ’function declarations are hoisted with definition!’
function doSomething() {
console.log(’function declarations are hoisted with definition!);
}
// function expression with var keyword
doAnotherThing(); // type error, doAnotherThing is not a function
var doAnotherThing = function() {
console.log(‘this won’t work’);
}
// function expression with let keyword
doAThirdThing(); // reference error, cannot access doAThirdThing before initialization
let doAThirdThing = function() {
console.log(‘this won’t work’);
}

Hoisting with Classes

  • Name is hoisted to top of block scope
  • Not initialized until execution, and lives in temporal dead zone til then
MyClass.doSomething(); // reference error, cannot access class ‘MyClass’ before initializationclass MyClass {
doSomething() {
console.log(‘class definitions are not hoisted, but class names are’);
}
}

Best practices:

  • Don’t nest function declarations within a block. If you need a function within a block, use an expression
  • use let and const instead of var
  • if you have to use var, declare all variables at the top of scope
  • when using let and const, declare as close to first usage as possible
  • declare functions before calling them

What happens when a var variable and a function declaration have the same name? The common wisdom is that functions get hoisted and variable declarations get discarded, BUT variable reassignment still happens in the order of the program. (Basically think of it as creation phase)

bar(); // logs world
var bar = ‘hello’;
function bar() {
console.log(‘world’);
}
// hoisted is
function bar() {
console.log(‘world’);
}
bar();
bar = ‘hello’;

but…

var bar = ‘hello’;
bar(); // Uncaught TypeError: bar is not a function
function bar() {
console.log(‘world’);
}
// hoisted is
function bar() {
console.log(‘world’);
}
bar = ‘hello’;
bar();

The var statement

What makes it different from let & const?

  • var doesn’t let you create constants
  • var is in function scope — let and const are in block scope
  • when declared in global scope, var creates a property in the global object
// example A
> var qux = 5
> qux = 6 // no TypeError
> const qux2 = 5
> qux2 = 6 // TypeError
// example B
function doSomething() {
if (true) {
let foo1 = 'block scope';
var foo2 = 'function scope';
}
console.log(foo2); // logs 'function scope'
console.log(foo1); // reference error, foo1 out of scope
}
// example C
> var foo = 2;
> global.foo
= 2
> let bar = 2;
> global.bar
= undefined

Strict mode

What is it? Strict mode is a mode of JS that helps to:

  • Eliminate silent errors by changing them to throw errors instead (mitigate bugs/make debugging easier)
  • Prevent code that make sit hard for JS to optimize efficiency (code runs faster)
  • Prevents using names and syntax that interfere with future versions of JS (avoid conflicts with future changes to the language)

What does it do?

  • Changes implicit execution context for functions and implicit global (& undeclared) variables to be undefined rather than the global object
  • Throws an error if you begin a number literal with 0 or JS suspect’s its an octal literal
  • Prevents declaring two function parameters with the same name
  • Prevents using delete operator on variable name
  • Prevents using some keywords as variable names
  • Forbids binding of eval and arguments
  • Forbid access to some properties of arguments object
  • Disables the with statement

How do you enable it? Write "use strict" at the top of your program, or at the top of the function scope. "use strict" a pragma — a language construct that tells a compiler or interpreter to process language differently (like in strict mode for instance). Strict mode can’t be disabled once enabled. Strict mode is implicitly enabled in the body of a class and in modules.

When do you enable it? Always! UNLESS you’re working on old code that’s not using strict mode, in case enabling strict mode breaks it. But if you’re writing new functions into that old code, you should use strict on a function level!

Scope

Types of scope

  • Visibility — matters where something is visible. the visibility scope belongs to the variable itself. local vs global variable. globally? locally?
let global = 'global'if (true) {
let local = 'local block'
let global1 = 'global'
}
function doSomething() { // doSomething in global scope
let local1 = 'local to function block'
if (true) {
let local2 = 'local block'
var local3 = 'local function'
}
}
  • Declared — matters HOW something is declared. function scope? block scope? Classes and variables declared with let and const have block scope. Functions and variables declared with var have function scope.
let block = 'block'
let function = 'function'
if (true) {
let block1 = 'block'
let function1 = 'function'
}
function doSomething() { // doSomething is function slope
let block2 = 'block'
if (true) {
let block3 = 'still block'
var function3 = 'still function'
}
}
  • Lexical — matters where something is accessible lexically. whether something is ‘in scope’ or not. inner scope? outer scope?
let outer = 'outer scope'if (true) {
let inner = 'inner scope of block'
let outer2 = 'outer scope of block'
}
function doSomething() {
let inner1 = 'inner scope of function'
if (true) {
let inner2 = 'inner scope of block'
var mix3 = 'inner scope of function, outer scope of block'
}
}

Closures

What is a closure? A closure is created when a function is defined. Closures are defined by what is in the lexical scope of a function at the time of its declaration. It contains:

  1. References to all the identifiers in the lexical scope of a function at the time of its declaration.
  2. Function definition.
function createCounter() {
let count = 0;
return () => {
count ++;
console.log(count);
}
}
let incrementCount = createCounter();
// count not in scope, but reference to count identifier is in
// closure created by createCounter
incrementCount(); // 1
incrementCount(); // 2
incrementCount(); // 3
let incrementCount2 = createCounter();
incrementCount2(); // 1
incrementCount2(); // 2
incrementCount2(); // 3

What is the relationship between closures and scope? Closures and scope are intimately related. Closures are created with the identifiers in the lexical scope of the function when it is defined, so what variables are in scope at execution depends on the lexical scope of the definition.

Why use closures?

  • Private data/state
  • Partial function application
  • Callbacks
  • And a bunch of other stuff!

Private data

Why have private state?

  • Controls how people can interact with interface
  • Prevent user of object to be dependent on implementation
// using closures
function createHiddenRecord() {
let hiddenRecords = [];
return {
add(secret) {
hiddenRecords.push(secret);
},
show() {
return [...hiddenRecords]
}
}
}
let mySecrets = createHiddenRecord();
mySecrets.add(‘surprise party thursday’);
mySecrets.add(‘i hate you’);
mySecrets.show(); // logs copy of hiddenRecords
console.log(mySecrets.hiddenRecords); // NOPE! undefined.
// I have to use provided interface.
// using IIFEs (see below)
let hiddenRecord = (function() {
let hiddenRecords = [];
return {
add(secret) {
hiddenRecords.push(secret);
},
show() {
return [...hiddenRecords];
}
}
})();
hiddenRecord.add('surprise party thursday');
hiddenRecord.add('i hate you');
hiddenRecord.show();
console.log(hiddenRecord.hiddenRecords); // NOPE! undefined.
// I have to use provided interface.

Partial function application

Partial function application is when, in a function that returns another function, some function arguments are applied when the outer function is invoked, and some applied when you call the returned function. It’s only considered partial function application if the second function can be called with fewer arguments than it would expect. For example:

function createAdder(numToAdd) {
return number => {
return number + numToAdd;
}
}
let addFive = createAdder(5); // five is applied now// then the other number is applied later!
addFive(5) // returns 10
addFive(10) // returns 15

IIFEs

An IIFE or immediately invoked function expression is a function that you define and invoke simultaneously.

Why use IIFEs?

  • Create private scopes in a larger program (IIFE’s are great for adding code to complicated and messy program without worrying about any conflicts. Blocks can also be used to do this in ES6.)
// Private SCOPE with IIFEs and blocks
// messy code
console.log((function() {
let array = [1,2,3];
calculateSomethingIndependent(array);
})())
// more messy code{
let array = [1,2,3];
let solution = calculateSomethingIndependent(array);
console.log(solution);
}
// more messy code
  • Create private data, inaccessible to other parts of program
let hiddenRecord = (function() {
let hiddenRecords = [];
return {
add(secret) {
hiddenRecords.push(secret);
},
show() {
return [...hiddenRecords];
}
}
})();
hiddenRecord.add('surprise party thursday');
hiddenRecord.add('i hate you');
hiddenRecord.show();
console.log(hiddenRecord.hiddenRecords); // NOPE! undefined.

Shorthand notation (understanding, not using)

Object destructuring:

let { qux, foo, bar } = obj; // or
let { foo, bar, qux } = obj; // or
let { qux: myQux, foo, bar } = obj;
// is the same as
let obj = {
foo: “foo”,
bar: “bar”,
qux: 42,
};
let foo = obj.foo;
let bar = obj.bar;
let qux = obj.qux;
// MORE OBJECT DESTRUCTURINGfunction xyzzy({ foo, bar, qux }) {
console.log(qux); // 3
console.log(bar); // 2
console.log(foo); // 1
}
let obj = {
foo: 1,
bar: 2,
qux: 3,
};
xyzzy(obj);// MORE OBJECT DESTRUCTURING({ foo, bar, qux } = obj);

Array Destructuring

let foo = [1, 2, 3, 4];
let [ bar, ...qux ] = foo; // rest syntax groups 2, 3, & 4
console.log(bar); // 1
console.log(qux); // [2, 3, 4]
// MORE ARRAY DESTRUCTURINGlet [ first, , , fourth, fifth, , seventh ] = bar;

Spread Syntax with objects:

let foo = { qux: 1, baz: 2 };
let bar = { ...foo };
console.log(bar); // { qux: 1, baz: 2 }
console.log(foo === bar); // false — bar is a new object
// MORE SPREAD SYNTAX
let foo = { qux: 1, baz: 2 };
let xyz = { baz: 3, sup: 4 };
let obj = { ...foo, ...xyz };
obj; // => { qux: 1, baz: 3, sup: 4 }
// Insert an array into another array
let foo = [1, 2, 3]
let bar = […foo, 4, 5, 6, …foo];
bar; // => [1, 2, 3, 4, 5, 6, 1, 2, 3]

*Note that spread syntax with objects only returns the properties that Object.keys would return. That is, it only returns enumerable “own” properties. That means, in part, that it’s not the right choice when you need to duplicate objects that inherit from some other object. It also means that you lose the object prototype.

Rest syntax collects multiples items into an array or object. NOTE: The rest element must be the last item in any expression that uses rest syntax.

let foo = {bar: 1, qux: 2, baz: 3, xyz: 4};
let { bar, baz, …otherStuff } = foo;
console.log(bar); // 1
console.log(baz); // 3
console.log(otherStuff); // {qux: 2, xyz: 4}
// MORE REST SYNTAXlet foo = [1, 2, 3, 4];
let [ bar, …otherStuff ] = foo;
console.log(bar); // 1
console.log(otherStuff); // [2, 3, 4]
// MORE REST SYNTAXfunction maxItem(first, ...moreArgs) {
let maximum = first;
moreArgs.forEach(value => {
if (value > maximum) {
maximum = value;
}
});
return maximum;
}
console.log(maxItem(2, 6, 10, 4, -3));

Modules (CommonJS)

The benefits of using modules

  • Multiple developers can work on separate parts of program without fear of conflicts!
  • No need to chase down all the function calls?
  • Keeps a program’s concerns separate in the face of bug fixes and enhancements
  • make it easier to work with private data
  • you can easily use a module outside of original program, without having to disentangle it from the other code.

CommonJS variables

Your main program, and all your code, is part of a CommonJS module, so it provides several variables that you can use in your code:

  • module: an object that represents the current module
  • exports: the name(s) exported by the module (same as module.exports)
  • require(moduleNameOrPath): the function that loads a module
  • __dirname: the absolute pathname of directory that contains the module
  • __filename: the absolute pathname of the file that contains the module

To create CommonJS modules, create a file with the code you want to modularize. Then write module.exports = plus the identifiers of the things you want to export the bottom of your code. You can import the module into your main program/another file using require('path/.fileName'), or you can install an NPM module and just import with require('fileName'). The names exported from the module are then available in the program and can be assigned to variables.

function print(string) {
console.log(string);
}

module.exports = print;

// new file
const print = require("./print.js");
print("You rock!");

// node
$ node main.js
You rock!
// example with multiple exports!

let punctuation = "!!!";

function print(string) {
console.log(`${string}${punctuation}`);
}

function setPunct(newPunctuation) {
punctuation = newPunctuation;
}

module.exports = {
print,
setPunct,
};

// new file
const { logIt, setPrefix } = require("./logit");
print("You rock"); // You rock!!!
setPunct("???");
logIt("You rock"); // You rock???

JS modules in browser

CommonJS doesn’t work asynchronously, so it doesn’t work with the browser. Thus JS Modules were created. They can be used with export and import keywords like below:

// foo.js
import { bar } from "./bar";

let xyz = 1; // not exported

export function foo() {
console.log(xyz);
xyz += 1;
bar();
}
// bar.js
export let items = [];
export let counter = 0;

export function bar() {
counter += 1;
items.push(`item ${counter}`);
}

export function getCounter() {
return counter;
}
// main.js
import { foo } from "./foo";
import { bar, getCounter, items, counter } from "./bar";

foo();
console.log(items); // ["item 1"]
console.log(getCounter()); // 1
console.log(counter); // 1

bar();
console.log(items); // ["item 1", "item 2"]
console.log(getCounter()); // 2
console.log(counter); // 2

Exceptions

What are exceptions? Exceptions are an event that happens during program execution that creates an anomalous condition in the code, and usually (unless caught and handled) forces the program to terminate. Exceptions are unlike silent errors, which create bugs in the program that Javascript ignores. They are also more specific than general errors, and generally object of the Error type. Examples include ReferenceError, TypeError, and SyntaxError.

What’s with the terms raise, throw, re-throw, and catch? Raise and throw can be used interchangeably to talk about triggering an exception that logs and error message and terminates the program. Re-throw means to throw a new exception within the catch statement of a try-catch block. Catch means to detect when an exception is about to be thrown and attempt to handle/recover from it without terminating the program.

How to use a throw statement

ex 1//
function divide(dividend, divisor) {
if (divisor === 0 || divisor === -0) {
throw new Error("Can't divide by zero");
}
return dividend/divisor;
}
divide(10, 0) // Error: Can't divide by zero
// ex 2
class DivideByZeroError extends Error {}

function divide(dividend, divisor) {
if (divisor === 0) {
throw new DivideByZeroError("Divide by zero!");
}

return dividend / divisor;
}

let result = divide(1, 0); // DivideByZeroError: Divide by zero!
console.log(result); // not run

How to use a Try/Catch block? If you are looking for a particular type of exception, you can try to run the program with a try catch statement. The catch block will detect when an exception is about to be thrown, and identify the type of error and try to handle it. In the example below, the catch statement checks if the exception is an instance of the DivideByZero type, and ignores it so the program can continue to execute.

class DivideByZeroError extends Error {}

function divide(dividend, divisor) {
if (divisor === 0) {
throw new DivideByZeroError("Divide by zero!");
}

return dividend / divisor;
}
function divideTenBy(divisor) {
try {
return divide(10, divisor);
} catch (error) {
if (error instanceOf DivideByZeroError) {
console.log('Can't divide by zero'); // doesn't terminate prog
} else {
throw error;
}
}
}
divideTenBy(10); // 1
divideTenBy(0); // Can't divide by zero!
divideTenBy(5); // 2

What happens in the program when an exception is thrown? When an exception is thrown, the JS Engine searches up through the levels of execution to find a try statement. If it finds one, it jumps to the catch block of the try statement and runs the code. If the catch block ignores the error, the code continues as normal from the end of that catch block. If the catch block re-throws an error, JS again searches for the next possible try block. If it doesn’t find a try block, the program terminates.

When to throw exceptions? When an event occurs that should NOT be ignored, and you can’t handle it in your local code. It should not be a normal part of your program flow control, but truly an exceptional condition.

What to write in a catch statement? You can: ignore the exception, log an error message, return null or undefined to calling function, re-throw an exception, flag the error to test after the program finishes execution.

Pure functions and side effects

What’s a pure function? A function with no side effects, that will return the same answer every time given the same arguments

Examples of side effects:

  • Reading from or writing to any file or server, including console, readline-sync, or Date(), or Random()
  • Mutating something
  • Reassigning non-local variable
  • Raising exception
  • Calling another function with side effects!!

Asynchronous programming (setTimeout, setInterval)

How to use setTimeout()

setTimeout() takes two arguments: a callback function and a wait time in milliseconds. setTimeout sets a timer of the given length, then waits to execute the callback function until after the wait time has passed. It will only execute the code when the engine is not otherwise occupied, so even if you set your wait time to 0, it may hold off on execution until the rest of your program is stopped — either for another timeout, or some other reason!

// hello world
setTimeout(function() {
console.log('!');
}, 3000);

setTimeout(function() {
console.log('World');
}, 1000);

console.log('Hello');
// countdown
let countdown = function() {
let timer = 1000;
for (let count = 10; count >= 0; count -= 1) {
setTimeout(() => console.log(count), timer);
timer += 1000; // otherwise it will log them all after 1 sec
}
}

How to use setInterval() setInterval() is like setTimeout() in that it takes a callback and a wait time in ms, and executes the callback after a set wait time. However, setInterval() actually calls the callback over and over at regular intervals based on the wait time argument. setInterval() returns an identifier which can be used to stop the repetitive execution — sort of like a password. To stop the execution, call clearInterval(returnValOfInterval).

let takeAChance = () => console.log('take a chance take a chance take a take a chance chance');let abba = setInterval(takeAChance, 2000);// wait some time
clearInterval(abba); // stops printing

// counting up to 1000
function startCounting() {
let count = 0;
let counterId = setInterval(function() {
count += 1;
console.log(count);
}, 1000);
return counterId;
}
function stopCounting(counterId) {
clearInterval(counterId);
}
let counterId = startCounting();
// some time later
stopCounting(counterId);

What are asynchronous functions? Functions that execute without blocking execution for the rest of the program. They run concurrently with other operations so the operations don’t have to wait for the others to finish.

Testing With Jest

Testing terminology

  • Test Suite: The entire set of tests for a given project.
  • Test/spec: The specific situation or context you’re trying to test (test('calling returnHello returns "hello"'), etc.)
  • Assertion/expectation: A verification that your program works as expected. Tells you if your expectations matched your results. There can be multiple assertions within on test. (expect(returnHello()).toBe('hello'), etc.)

Jest

Benefits of testing? Prevents regression — when previously working code breaks with a change to environment.

What is Jest? Jest is a testing library.

Setting up jest:

> npm install jest -g
> touch jest.config.js
> jest temp.test.js

What does a Jest test file look like?

const Car = require("./car");

describe("The Car class", () => {
test("has four wheels", () => {
let car = new Car();
expect(car.wheels).toBe(4);
});
});

Expect and matchers

describe() groups your tests. describe takes as arguments a string description and a callback function that contains any and all tests for this test grouping. This method is OPTIONAL and doesn’t return a useful value.

test() is where you write your assertions. It takes as arguments a string description of the test, and a callback function that contains any and all assertions needed for this test, as well as any object instantiation necessary to set up the assertions. Test doesn’t return a useful value. xtest() and test.skip() can be affixed to the test function to skip that particular test during execution of the test file.

expect() takes one argument: the value that we want to assert (actual value), and returns an object that includes a variety of matcher methods.

Matchers compare the actual value passed into expect with the expected value. They take one argument (the expected value), compare the actual and expect, and inform Jest of the results (without returning a boolean). Jest takes care of treating that result as a success or failure.

Matcher method examples:

  • toBe() fails unless actual value === expected value
test("has four wheels", () => {
let car = new Car();
expect(car.wheels).toBe(4);
});
  • toEqual() tests for object equality (equal state: e.g. {a: 1} is equal to {a: 1})
test('two new cars are equal objects', () => {
let car1 = new Car();
let car2 = new Car();

expect(car1).toEqual(car2);
});
  • toBeUndefined() fails unless the actual value is undefined. Same as toBe(undefined)
test('does not have doors', () => {
let car = new Car();
expect(car.doors).toBeUndefined();
});
  • toThrow() fails unless the expression passed into expect throws an error. Argument passed to expect must be a function — not a function invocation.
test('raises a TypeError when called drive on it', () => {
let car = new Car();
expect(() => car.drive()).toThrow(TypeError);
});
  • toBeNull() fails unless the actual value is null. Same as toBe(null)
test('a new car has no mileage info', () => {
let car = new Car();
expect(car.mileageInfo).toBeNull();
});
  • toBeTruthy() fails unless the actual value is truthy.
test('wheels is truthy', () => {
let car = new Car();
expect(car.wheels).toBeTruthy();
});
  • toContain() fails unless the given array includes a value. Also finds substrings in strings.
test('array contains car', () => {
let car = new Car();
let arr = [1, 2, 3];
arr.push(car);

expect(arr).toContain(car);
});
// ortest('string contains "car"', () => {
let man = "His scars have healed";

expect(man).toContain("car");
});
  • .not allows you to opposite any of these matchers, like a ! operator
test('car has wheels', () => {
let car = new Car();
expect(car.wheels).not.toBeUndefined();
});

SEAT approach

  • S: Set up the necessary objects to test
describe('TestObject works', () => {
let testObj;
beforeEach(() => {
testObj = new TestObject();
}
}
  • E: Execute the code against the objects we’re testing
describe('TestObject class', () => {
test('myFunction shows all objects in testObj', () => {
testObj.push(importantValue);
// ...
}
}
  • A: Assert the results of execution
describe('TestObject class', () => {
// ...
test('doSomething method does something', () => {
expect(testObj.doSomething()).toBe('done');
}
}
  • T: Tear down and clean up lingering artifacts
afterEach(() => {
cleanUpFiles();
}

Understanding code coverage

In your terminal, run jest -coverage fileName.test.js. Check out your “% Lines” column to see the most useful data: this is the percentage of program lines that got executed at least once during testing.

Packaging Code

Project directory layout

What’s a project? A project is a collection of files to develop, build, test, and distribute software. It includes source code, test files, databases, configuration files, and more.

Node Project layouts: Some specific files and directories must be present. Some kinds of data must be in specific locations. Some data must use well-defined formats. Standard layouts help developers find the files and data that they need when moving from project to project.

npm standard layout involves:

  • test code in a test directory
  • code files in the lib directory
  • web-based programs generally require “assets” like images, JavaScript, and CSS (stylesheets) — these often reside in an assets directory with a subdirectory for each file type: images, javascript, and stylesheets
  • all browser-related code in a folder of its own, typically named client

npm and npx

npm manages your packages. It comes bundled with node.

how to install different npm packages

> npm install packageName --global
// or
> npm install packageName --save

The above downloads + installs package in node_modules. The npm command looks for an existing directory by that name in the current folder. If it doesn't find one, it looks in the parent directory. If it doesn't find it there, it keeps searching up the directory hierarchy until it finds one. If it doesn't find one, it creates one in the directory where you ran the npm install command. Never nest your project directory inside a directory that already contains a node_modules. You want to install all of your dependencies inside your project directory, not above it.

how to require/import different npm packages (works just like modules)

const _ = require('lodash');
console.log(_.chunk([1, 2, 3, 4, 5, 6, 7, 8], 2));
// or
const chunk = require('lodash/chunk');
console.log(chunk([1, 2, 3, 4, 5, 6, 7, 8], 2));
// or
const chunk = require('lodash').chunk;
console.log(chunk([1, 2, 3, 4, 5, 6, 7, 8], 2));

why store locally? Ensures that projects that need specific versions of packages have them locally available. If another project needs a different version of a package, you can install that version in its node_modules directory. Since each package is local to the project, each project is free to use the version it needs.

how to local npm executable package

package.json and package-lock.json

What is package.json? A file that lists the packages a project needs, and the versions of each package. It’s essentially a configuration file written in JSON format. It’s great for tracking dependencies.

how to initialize a package.json file

> npm init

how to add dependencies to package.json? Add a dependencies section to the file with any dependencies you want to add. Then simply run npm install without any arguments to install and up-to date version of those dependencies. This command also creates a package-lock.json file which shows the correct version of the dependencies that were just installed. The next time we run npm install it'll look at package-lock.json and install the versions specified there.

{
"name": "todolist",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"lib": "lib",
"test": "test"
},
"dependencies": { // add this section to file
"express": "4",
"http-errors": "1",
"morgan": "1.9"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

what is package-lock.json? A file that shows the precise versions of the packages that npm has installed AND the precise versions of the dependencies of each package.

{
"name": "todolist",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
// a whole bunch of omitted dependencies
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
// A long list of requires
"vary": "~1.1.2"
}
},
// a whole bunch of omitted dependencies
"http-errors": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"dependencies": {
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}
}
},
// a whole bunch of omitted dependencies
"morgan": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
"requires": {
"basic-auth": "~2.0.0",
"debug": "2.6.9",
"depd": "~1.1.2",
"on-finished": "~2.3.0",
"on-headers": "~1.0.1"
}
},
// a whole bunch of omitted dependencies
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}

how to add a single dependency?

  • npm install packageName --save (save option tells npm to save the package to the dependencies list in package.json)
  • add dependency to package.json directly

what’s a devDependency? a package that you only use during development. should be separated from other dependencies, in package.json. Easiest way to do this is to say npm install packageName -s-dev

how to delete a dependency?

> npm uninstall lodash --save // to also remove from package.json
// or
> npm uninstall lodash --save-dev // to remove from package.json dev
// or
> npm prune // removes the actual node_modules files for
// dependencies that you manually removed from package.json

WHAT THE HECK IS NPX npx runs local executable packages like eslint. npx checks for a local installation first, and if it can’t find the package (eslint) locally or globally, it downloads and uses a temporary version of the named package. npm can only run pre-installed packages.

> eslint fileName.js // will use the globally installed version
> npx eslint fileName.js // will look first for local version

Transpilation

Transpilation is the process of translating a piece of source code into another language, or another version of a coding language. Often it means taking a superset of javascript and translating it into plain javascript, or converting ES6 javascript features into ES5 or earlier version syntax so it can run in most browsers (users don’t always update their browsers quickly). Babel is one of the most popular programs designed to do transpilation. Babel will put its transpiled code into a dist folder.

npm scripts

What’s a script? A script is a piece of code that helps to automate repetitive tasks. To use npm scripts locally, you must create a “scripts” object in your project’s package.json file. This object’s key-value pairs consist of the name of your script (e.g. ‘babel’) as key, and the script itself (e.g. ‘npx babel lib — out-dir dist — presets=@babel/preset-env’) as value. Npm scripts generally consist of terminal commands like the examples above. To run the script, simply type npm run scriptName into your cli. It will output the project name, script name, and directory name, followed by the script itself, and the results of executing that script.

Should I use npm vs npx for scripts? When used in scripts, npm uses locally installed packages rather than global packages, so it’s not necessary to use the npx command inside your script. However, if you’re not working with a pre-installed package, it may be useful to use npx, which will locally install a package for one-time use.

"babel": "npx babel lib --out-dir dist --presets=@babel/preset-env"// or"babel": "babel lib --out-dir dist --presets=@babel/preset-env"

packaging projects

How to prepare to package an environment?

  • Create a package.json file
  • Provide values for the name, version, and main fields: (name is the name of your package, version is the initial module version (1.0.0), main is the name of the file that Node will load when someone imports your package)
  • Publish your node package (npm publish --access public)

What do I use as my main file? You can create a file index.js, which requires and exports any modules needed within your main code.

module.exports = {
TodoList: require('./dist/todolist'),
Todo: require('./dist/todo'),
};

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store