Node.js Intro

2022 Version

By 增廣建文

Agenda

  • What's Node.js
  • Installation
  • Basic Syntax
  • First Simple Application

What's Node.js?

  • JavaScript runtime built on Chrome's V8 JavaScript engine
  • npm is it's package manager
    • similar to pip in python
  • Use same JS in frontend

Installation

  • Portable Version

    • Need to set PATH
  • MSI Installer (for Windows)

  • Anaconda's conda-forge channel

  • 檢查是否裝好

    • npm -v
    • node -v

Basic Syntax - Console

log, info, error

console.log("Hello world!");

console.info("Hello world!");

console.error("You made a mistake");

Basic Syntax - Variable

  • 動態型別的語言 (Dynamically Typed Language)
    • var
    • let (建議)
    • const

Var vs Let

  • let定義的變數只能在該區域使用(scope/ function內)
function run() {
  {  // new scope
    var moo = "Mooo"
    let baz = "Bazz";
    console.log(moo, baz); // Mooo Bazz
  }

  console.log(moo); // Mooo
  console.log(baz); // ReferenceError
}

run();

Basic Syntax - if/ else

function testNum(a) {
  let result;
  if (a > 0) {
    result = 'positive';
  } else {
    result = 'NOT positive';
  }
  return result;
}

console.log(testNum(-5));

Basic Syntax - for loop

  • 也有while以及do while
for (let i = 0 ; i < 10 ; i +=1){
    console.log(i);
}

while (condition) {
  // statements
}

do {
  // statements
} while (condition);

Basic Syntax - forEach & for of

  • 可以從array, map. set中逐筆取資料
const numbers = [1, 2, 3, 4, 5];
let sum = 0;
numbers.forEach(num => sum += num);
console.log(sum) // 15

let mArray = [1, 2, 3]
for (let value of mArray) {
  console.log(value); // 1 2 3
}

Basic Syntax - Function

// named function
function sum(a, b){
    return a+b;
}

// 匿名函式
const sum = function(a, b) {
    return a+b;
}

// arrow function
const sum = (x, y) => {
  return x + y
};

More about Arrow Function

部分情況下可以用的簡寫

// no arguments時
const callMe = () => { 
    console.log('John!');
}

// 只有一個argument
const callMe = name => { 
    console.log(name);
}

// function內只有一行
const doubleNum = num => num * 2;

Basic Syntax - Object

基本上就是python的dict,常會搭配array、能方便轉成JSON

const dog = {
      name: 'Tom',
      breed: 'Golden Retriever',
      weight: 60
    }

// get values by key
console.log(dog.breed) // Golden Retriever
console.log(dog[breed]) // Golden Retriever

More about Object

// get values from each key
Object.keys(dog).forEach((key) => {
    console.log(dog[key]);
});

// define function in object
const dog = {
      breaks() {
        console.log('woof'); 
      }
    };
dog.breaks(); // woof

Special Operators - Spread

除了可以用於array外,也能用在object上

const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4];

const person = {
  name: 'Andy',
};
const newPerson = {
  ...person,
  age: 21,
};

Special Operators - Rest

和spread一樣是三個dot,但使用情境僅限array

let set = function(first,second,...food){
  console.log(first); // 阿華要吃的第一樣 - 薯條
  console.log(second); // 阿華要吃的第二樣 - 壽司
  console.log(food);  // 映萱要吃剩下全部 - ["可樂", "雞塊", "牛肉漢堡", "蘋果派"]
}
set('薯條','壽司','可樂','雞塊','牛肉漢堡','蘋果派');

把剩下未用到的值都給rest參數

可以處理不知道有多少arugment的情況

Object Reference

雖然object被設為const,但是裡面的屬性其實可以被改變

const person = {
  name: 'Andy'
};
const secondPerson = person;

person.name = 'Tank';

console.log(secondPerson); // { name: 'Tank' }

Object Copy

剛剛的spread會有比較特別的特性,他會去複製屬性

const person = {
  name: 'Andy'
};
const secondPerson = {
  ...person
};

person.name = 'Tank';  // { name: 'Andy' }

Class

class Person {
  constructor() {
    this.name = 'Andy';
  }
  call=() => {
    console.log(this.name);
  }
}

const myPerson = new Person();

myPerson.call();

Inherit

class Human extends Person {
  constructor() {
      super();
    this.gender = 'male';
  }
  call=() => {
    console.log(this.name);
    console.log(this.gender);
  }
}

const myHuman = new Human();

myHuman.call();

Module

  • fs: handle the file system
  • http: make Node.js act as an HTTP server
  • os, path etc...

Example:

import * as fs from 'fs';

var output = fs.readFileSync('someData.txt')
console.log(output)

Module & Import, Export 1/2

default export

// person.js
const person ={
  name:'Andy'
} 

export default person;

// index.js
import person from './person.js';

Module & Import, Export 2/2

named export

// utility.js
export const clean = () => {}
export const bastData = 10

// index.js
import {baseData} from './utility.js';
import {clean} from './utility.js';
// 如果想要可以renmae
import {clean as c} from './utility.js';  // baseData不變
import * as c from './utility.js';  // 變成c.baseData還有c.clean

Import vs Require

  • Appears after ES6
  • node.js version <= 10
const fs = require('fs');
  • node.js version > 10, default export
import * as fs from 'fs';

IDE & Plugins

  • VScode/ Webstorm
  • Quokka.js

First Project

  1. 初始化專案npm init -y, -y可以跳過設定
  2. 安裝babel,自動將code轉成所有瀏覽器支援的格式
  3. 寫些簡易的code來測試,使用node index.js來執行
// index.js
console.log('hi~')

Package.json

lock版是算過hash值的,可以確保沒有人竄改package.json

{
  "name": "backend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Script

可以改用npm start or npm run start

{
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  // 👇️ rest ...
}

ES6 Module

// config.js
export const num = 100

// index.js
import {num} from "./config.js";

console.log(num)

可以不用babel,但是json內要把type改掉

{
  "type": "module",
  // 👇️ rest ...
}

ES6 Module - Babel 1/3

要先裝babel套件以及轉換語法的plugin@babel/preset-env

  • npm install --save-dev @babel/core @babel/cli @babel/node
  • npm install --save-dev @babel/preset-env

裝好後會看到node_module資料夾

added 197 packages, and audited 198 packages in 14s

16 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

ES6 Module - Babel 2/3

還要去設定轉換格式的檔案

// babel.config.json
{
  "presets": ["@babel/preset-env"],
  "ignore": ["node_modules"]
}

這樣就可以編譯並執行程式 npx babel-node index.js
用npx可以避免要去指定babel-node的絕對路徑
./node_modules/.bin/babel-node

ES6 Module - Babel 3/3

除了直接執行編譯完的結果外,也可以把它變成檔案後輸出
npx babel index.js --out-dir out
為了避免每次都要打一串指令的麻煩,可以把它寫進script
跑start前會先執行prestart

{
  "scripts": {
    "build": "babel *.js -d out",
    "start": "node out/index.js",
    "prestart": "npm run build"
  },
}

Simple Test with Jest

後端通常會用Jest來做testing npm install -D jest

// index.test.js
import {num} from "./config.js";

test('first test', () => {
  // Assert
  expect(num).toBe(100);
});

直接跑npx jest就會自動去找所有match pattern的test case
當然也可以把他加到package.json的script內

Config Jest

先透過npx jest --init產生jest.config.js,問題依照需求作答

✔ Would you like to use Jest when running "test" script in "package.json"? ... yes
✔ Would you like to use Typescript for the configuration file? ... no
✔ Choose the test environment that will be used for testing » node
✔ Do you want Jest to add coverage reports? ... no
✔ Which provider should be used to instrument code for coverage? » babel
✔ Automatically clear mock calls, instances, contexts and results before every test? ... no

輸出的設定檔就會是

module.exports = {
  testEnvironment: "node"
};

Result of First Test

執行npx jest後的結果:

Detail Config

有更多細項可以調整

module.exports = {
  testEnvironment: "node",
  moduleFileExtensions: [
    "js",
    "json",
    "node",
  ],
  testPathIgnorePatterns: [
    "/node_modules/",
    "/out/"
  ]
}

Reference

這是演講稿

常因會有undefined而導致各種奇怪的bug

var的這種特性被稱為hoisting

偷懶可以用npm start

```json // babel.config.json (babel版本要大於v7.8.0) { "presets": [ [ "@babel/preset-env", { "targets": { "firefox": "60", "chrome": "67", }, "useBuiltIns": "usage", "corejs": "3.6.5" } ] ] } ```

babel和babel-node在這基本上是一樣的作用,只是babel會比較輕量化,因為babel-node要多裝@babel/node