English
Appearance
English
Appearance
Today you will learn to:
let and const, and name them well;typeof;Where this fits in the sample check rule - today we can write the highlighted lines:
function rule(doc) {
const library = require("library.js");
let name = String(doc.inventory?.extended_info?.json?.data?.system?.name
|| doc.status.device_alias);
let serial = doc.overview.serial_number;
let model = doc.overview.model;
let version = doc.overview.version;
if (["XR-210", "XR-220"].includes(doc.overview.model)
&& !library.version_below(doc.overview.version, "4.2.1.7")) {
emit(doc.status.device_alias, { /* ... alert details ... */ });
}
}A variable is a name for a value in memory. Creating it has two parts:
let serial;serial = "SN4X7Q92";Usually we do both at once:
let serial; // declared, but has no value yet
console.log(serial); // undefined
serial = "SN4X7Q92"; // now defined
console.log(serial);
let model = "XR-220"; // declared and defined at once
console.log(model);let serial; // declared, but has no value yet
console.log(serial); // undefined
serial = "SN4X7Q92"; // now defined
console.log(serial);
let model = "XR-220"; // declared and defined at once
console.log(model);Animations
Variables in memory: 1. Declaring and assigning, 2. Copying a value
let and const | Keyword | Can it get a new value? | Use it for |
|---|---|---|
const | no | values that do not change - most of your variables |
let | yes | counters, results built step by step |
var | yes | old code only - do not use it in new code |
const model = "XR-220";
let checkedSystems = 0;
checkedSystems = checkedSystems + 1; // fine, it is a let
console.log(checkedSystems);
model = "XR-210"; // error: model is a const
console.log("this line never runs");const model = "XR-220";
let checkedSystems = 0;
checkedSystems = checkedSystems + 1; // fine, it is a let
console.log(checkedSystems);
model = "XR-210"; // error: model is a const
console.log("this line never runs");Start with const
Write const by default. Change it to let only when you really need to give the variable a new value. Good rule guidelines ask the same for global variables: "they must be declared as const".
Animation
Variables in memory: 5. const
MDN: Strict mode
Without let, const or var, JavaScript silently creates a global variable. A typo in a name then creates a new variable instead of an error:
let firmwareLevel = "4.2.1.7";
firmwarelevel = "4.3.0.0"; // typo: small "l" - a new global variable!
console.log(firmwareLevel); // still 4.2.1.7let firmwareLevel = "4.2.1.7";
firmwarelevel = "4.3.0.0"; // typo: small "l" - a new global variable!
console.log(firmwareLevel); // still 4.2.1.7Put "use strict"; as the first line of a file (or function), and the same mistake becomes an error:
"use strict";
let firmwareLevel = "4.2.1.7";
FirmwareLevel = "4.3.0.0"; // typo: capital "F" - now an error
console.log(firmwareLevel);"use strict";
let firmwareLevel = "4.2.1.7";
FirmwareLevel = "4.3.0.0"; // typo: capital "F" - now an error
console.log(firmwareLevel);INFO
Modules and classes are always in strict mode. In the practice files we start with "use strict";.
Animation
Variable scope: 4. No declaration and "use strict"
Rules (otherwise: syntax error):
_ and $; no spaces, no -;let, const, if, function, class, ...);model, MODEL and Model are three variables.Conventions (good practice):
camelCase for variables: serialNumber, firmwareLevel, isAffected;firmwareLevel, not x or data2;is, has, can: isEnabled, hasCert;UPPER_SNAKE_CASE for fixed settings: const MIN_FIRMWARE_LEVEL = "4.2.1.7";JSON keys vs variable names
The keys in the report JSON use snake_case (firmware_level, remote_login_enabled). That is the data's naming - keep the keys as they are. Your own variables use camelCase: const firmwareLevel = doc.inventory.details.firmware_level;
Check yourself: which of these names are valid?
serial_number, 2ndNode, firmware-level, $total, const, isRemote, MODEL
Valid: serial_number, $total, isRemote, MODEL. Not valid: 2ndNode (starts with a digit), firmware-level (- is minus), const (reserved word).
MDN: JavaScript data types and data structures
A value in JavaScript is either a primitive (a single simple value) or an object (a structure with properties - like doc). The primitive types:
| Type | Examples | In the report data |
|---|---|---|
number | 42, 9.1, -3, 1_000_000 | counts, sizes (after conversion) |
string | "XR-220", 'yes', `text` | almost every value |
boolean | true, false | "yes" / "no" after conversion |
undefined | undefined | a key that is not in the document |
null | null | "no value", written on purpose |
(Two more, bigint and symbol, are rarely needed.)
typeof MDN: typeof
console.log(typeof 42); // number
console.log(typeof "4.2.1.7"); // string
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof null); // object - a famous old bug in JavaScript
console.log(typeof doc); // objectconsole.log(typeof 42); // number
console.log(typeof "4.2.1.7"); // string
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof null); // object - a famous old bug in JavaScript
console.log(typeof doc); // objectWhole numbers and decimals are the same type, number:
const nodes = 2;
const ratio = 0.75;
const capacityBytes = 2_199_023_255_552; // "_" only makes it readable
console.log(nodes, ratio, capacityBytes);
console.log(0.1 + 0.2); // not exactly 0.3!
console.log(10 / 0); // Infinityconst nodes = 2;
const ratio = 0.75;
const capacityBytes = 2_199_023_255_552; // "_" only makes it readable
console.log(nodes, ratio, capacityBytes);
console.log(0.1 + 0.2); // not exactly 0.3!
console.log(10 / 0); // InfinityDecimals are not exact
0.1 + 0.2 gives 0.30000000000000004. Numbers are stored in binary, so most decimals are approximations. Round results before you show them.
Animations
Numbers in memory: 1. Bits and integers, 2. Fractions and 0.1, 4. 0.1 + 0.2, 6. NaN and Infinity
MDN: String, Template literals
Three kinds of quotes:
const a = "double quotes";
const b = 'single quotes';
const c = `backticks: a template string`;
console.log(a);
console.log(b);
console.log(c);const a = "double quotes";
const b = 'single quotes';
const c = `backticks: a template string`;
console.log(a);
console.log(b);
console.log(c);Escape sequences - special characters inside a string:
console.log("Name:\tLAB-DEV-01"); // \t tab
console.log("line 1\nline 2"); // \n new line
console.log("the \"Version\" column"); // \" a quote inside a string
console.log("C:\\checks\\rules"); // \\ a backslashconsole.log("Name:\tLAB-DEV-01"); // \t tab
console.log("line 1\nline 2"); // \n new line
console.log("the \"Version\" column"); // \" a quote inside a string
console.log("C:\\checks\\rules"); // \\ a backslashTemplate strings (backticks) can span lines and insert values with ${...} - very handy for alert messages:
const name = "LAB-DEV-01";
const model = "XR-220";
const firmwareLevel = "4.2.1.7";
const message = `System ${name} (${model}) runs firmware level ${firmwareLevel}.`;
console.log(message);const name = "LAB-DEV-01";
const model = "XR-220";
const firmwareLevel = "4.2.1.7";
const message = `System ${name} (${model}) runs firmware level ${firmwareLevel}.`;
console.log(message);undefined and null undefined - "nothing was put here": a declared variable without a value, or a key that is not in the document.null - "no value", set on purpose.console.log(doc.overview.model); // the key exists
console.log(doc.overview.nickname); // no such key: undefinedconsole.log(doc.overview.model); // the key exists
console.log(doc.overview.nickname); // no such key: undefinedThis is why rules check for missing data
Reading a missing key gives undefined. Reading a key of undefined is an error - that is what ?. in the sample rule protects against (session 6).
Animation
Variables in memory: 4. Types of values
// a one-line comment
/*
a comment over
several lines
*/
/**
* A documentation comment (JSDoc) - describes the code below it.
* A rule template usually starts with one.
*/Write comments that explain why, not what: // rule guidelines require const globals is useful; // set x to 5 is not.
= puts the value on the right into the variable on the left. Short forms:
let alerts = 0;
alerts = alerts + 1; // 1
alerts += 2; // same as alerts = alerts + 2 -> 3
alerts -= 1; // 2
alerts *= 10; // 20
alerts++; // add 1 -> 21
alerts--; // subtract 1 -> 20
console.log(alerts);let alerts = 0;
alerts = alerts + 1; // 1
alerts += 2; // same as alerts = alerts + 2 -> 3
alerts -= 1; // 2
alerts *= 10; // 20
alerts++; // add 1 -> 21
alerts--; // subtract 1 -> 20
console.log(alerts);Animations
Variables in memory: 3. x = x ** 2
How an expression is evaluated: 3. Assignment is an expression, 4. i++ and ++i
| Operator | Meaning | Example | Result |
|---|---|---|---|
+ - * / | as in math | 7 / 2 | 3.5 |
% | remainder | 7 % 2 | 1 |
** | power | 2 ** 10 | 1024 |
A disk-space example - bytes to TiB (1 TiB = 10244 bytes):
const capacityBytes = 2_199_023_255_552;
const usedBytes = 1_517_325_046_333;
const capacityTiB = capacityBytes / 1024 ** 4;
const usedPercent = usedBytes / capacityBytes * 100;
console.log(capacityTiB); // 2
console.log(usedPercent); // many decimals
console.log(Math.round(usedPercent)); // 69
console.log(usedPercent.toFixed(1)); // 69.0 - a string with 1 decimalconst capacityBytes = 2_199_023_255_552;
const usedBytes = 1_517_325_046_333;
const capacityTiB = capacityBytes / 1024 ** 4;
const usedPercent = usedBytes / capacityBytes * 100;
console.log(capacityTiB); // 2
console.log(usedPercent); // many decimals
console.log(Math.round(usedPercent)); // 69
console.log(usedPercent.toFixed(1)); // 69.0 - a string with 1 decimalMDN: Operator precedence
As in math: ** first, then * / %, then + -. Use parentheses when in doubt:
console.log(2 + 3 * 4); // 14
console.log((2 + 3) * 4); // 20
console.log(100 - 60 / 2); // 70console.log(2 + 3 * 4); // 14
console.log((2 + 3) * 4); // 20
console.log(100 - 60 / 2); // 70Check yourself: what does 10 + 20 / 10 * 2 ** 2 print?
18: first 2 ** 2 = 4, then 20 / 10 = 2, 2 * 4 = 8, then 10 + 8.
Animations
How an expression is evaluated: 1. Precedence, 2. Associativity, 5. Evaluation order (advanced)
+ with a string joins (concatenates). The other arithmetic operators convert strings to numbers:
console.log("9" + 1); // 91 - joined
console.log("9" - 1); // 8 - subtracted
console.log("3" * "4"); // 12
console.log(1 + 2 + "3"); // 33 - left to right: 3, then "3" + "3"
console.log("1" + 2 + 3); // 123console.log("9" + 1); // 91 - joined
console.log("9" - 1); // 8 - subtracted
console.log("3" * "4"); // 12
console.log(1 + 2 + "3"); // 33 - left to right: 3, then "3" + "3"
console.log("1" + 2 + 3); // 123WARNING
"9" + 1 is "91", not 10. When the values come from JSON, convert them first, then calculate.
Animation
Types, comparisons and logic: 1. The + operator
const version = doc.overview.version;
const enabled = doc.inventory.details.remote_login_enabled;
const epoch = doc.status.epoch;
console.log(version, typeof version);
console.log(enabled, typeof enabled);
console.log(epoch, typeof epoch);const version = doc.overview.version;
const enabled = doc.inventory.details.remote_login_enabled;
const epoch = doc.status.epoch;
console.log(version, typeof version);
console.log(enabled, typeof enabled);
console.log(epoch, typeof epoch);MDN: Number() constructor, parseInt(), parseFloat()
console.log(Number("42")); // 42
console.log(Number(" 42 ")); // 42 - spaces around are ignored
console.log(Number("")); // 0 - careful!
console.log(Number("4.2.1.7")); // NaN - Not a Number
console.log(parseInt("4.2.1.7")); // 4 - reads digits until it cannot
console.log(parseFloat("4.2.1.7")); // 4.2
console.log(parseInt("8 nodes")); // 8console.log(Number("42")); // 42
console.log(Number(" 42 ")); // 42 - spaces around are ignored
console.log(Number("")); // 0 - careful!
console.log(Number("4.2.1.7")); // NaN - Not a Number
console.log(parseInt("4.2.1.7")); // 4 - reads digits until it cannot
console.log(parseFloat("4.2.1.7")); // 4.2
console.log(parseInt("8 nodes")); // 8Number(x) - the whole string must be a number, otherwise NaN.parseInt(x) / parseFloat(x) - read from the start as far as they can.NaN is a number that means "not a valid number". Check it with Number.isNaN(value).A version is not a number
"4.2.1.7" has four parts. Number gives NaN, parseFloat gives 4.2 - both lose information. We compare versions part by part in session 5.
Animation
Types, comparisons and logic: 2. String to number
const nodes = 2;
console.log(String(nodes)); // 2 (a string)
console.log(nodes + ""); // 2 (a string)
console.log(`${nodes}`); // 2 (a string)
console.log(typeof String(nodes)); // stringconst nodes = 2;
console.log(String(nodes)); // 2 (a string)
console.log(nodes + ""); // 2 (a string)
console.log(`${nodes}`); // 2 (a string)
console.log(typeof String(nodes)); // stringThe sample rule wraps the name in String(...), so the result is always a string, whatever the JSON contains.
Compare with the text you expect - === means "is exactly equal to" (more in session 3):
const enabledText = doc.inventory.details.remote_login_enabled; // "yes"
const isEnabled = enabledText === "yes";
console.log(isEnabled); // true
console.log(typeof isEnabled); // booleanconst enabledText = doc.inventory.details.remote_login_enabled; // "yes"
const isEnabled = enabledText === "yes";
console.log(isEnabled); // true
console.log(typeof isEnabled); // booleanThe report EPOCH is the number of seconds since 1 January 1970 (UTC), as a string. JavaScript dates count milliseconds:
const epochSeconds = Number(doc.status.epoch);
const reportDate = new Date(epochSeconds * 1000);
console.log(epochSeconds);
console.log(reportDate.toISOString());const epochSeconds = Number(doc.status.epoch);
const reportDate = new Date(epochSeconds * 1000);
console.log(epochSeconds);
console.log(reportDate.toISOString());Open your copy of the Homework repository in VSCode (first time: see How to use these pages; before the session: git pull --no-rebase --no-edit upstream main) and run the tasks from the terminal, in the repository root:
node 02-variables-and-types/practice/task1-rule-variables.jsEach file explains the task at the top. Write your code between the your code lines; the checks at the bottom print OK or FAIL.
| Task | File | What you do |
|---|---|---|
| 1 | task1-rule-variables.js | Variables for the data of the sample rule and an alert message with a template string |
| 2 | task2-predict-types.js | Predict the result of 12 expressions, then check yourself |
| 3 | task3-capacity.js | Bytes from the JSON (strings) to TiB and used percent |
| 4 | task4-report-age.js | How many days and hours ago a report was sent |
| Topic | Remember |
|---|---|
| Variables | const by default, let when the value changes, never var; "use strict"; |
| Names | camelCase, meaningful; JSON keys stay as they are |
| Types | number, string, boolean, undefined, null; check with typeof |
| Strings | "...", '...', `...${value}...`; escapes \n, \t, \" |
| Operators | + - * / % **, +=, ++; precedence like in math |
+ with strings | joins: "9" + 1 is "91" |
| Converting | Number(), parseInt(), parseFloat(), String(); NaN; === "yes" |
02-variables-and-types/homework.js (in the homework repository): a remote login summary from the remote_login_* values.OK.Next session: comparisons, logical operators, conditions and loops - and the if of the sample rule.