node_03 - JavaScript Requirements¶
Node runs JavaScript but not the JavaScript you're used to from the browser
If you're coming from frontend development , you know document.getElementById and fetch and addEventListener. None of that exists here. What you need is the core language: variables , functions , objects , arrays , callbacks , Promises. The DOM APIs are gone. The window object is gone. What remains is ECMAScript - the language spec itself , stripped of browser wrappers
what you need to know¶
These are non-negotiable fundamentals. If you don't know these , the rest of Node will make no sense and you'll be that person asking why document is undefined on Stack Overflow
variables - let , const , and the ghost of var¶
// const - can't reassign (but object properties can change)
const PI = 3.14159
// PI = 3 // TypeError: Assignment to constant variable
const user = { name: 'ali' }
user.name = 'mahmoud' // This works - const prevents reassignment, not mutation
// let - block-scoped , can reassign
let counter = 0
counter = 1 // fine
// var - avoid unless you like function-scoped confusion
// var leaks out of blocks
if (true) {
var x = 10
}
console.log(x) // 10 - var is NOT block-scoped
arrow functions - the shorthand that changes this¶
// Traditional function
function add(a , b) {
return a + b
}
// Arrow function - shorter , no own `this`
const add = (a , b) => a + b
// Arrow functions don't bind their own `this`
// This matters in callbacks and event handlers
const server = http.createServer((req , res) => {
// Arrow function inherits `this` from surrounding scope
// No .bind() needed , no `const self = this` hack
})
template literals - backticks save lives¶
const name = 'mahmoud'
const age = 27
// Old way - string concatenation nightmare
const old = 'Hello , my name is ' + name + ' and I am ' + age + ' years old'
// New way - template literals
const message = `Hello , my name is ${name} and I am ${age} years old`
Template literals also handle multiline strings without \n everywhere:
const html = `
<div>
<h1>${title}</h1>
<p>${content}</p>
</div>
`
destructuring - pulling data out of objects and arrays¶
const config = {
host: 'localhost',
port: 3000,
ssl: false
}
// Old way
const host = config.host
const port = config.port
// Destructuring - clean , one line
const { host , port , ssl } = config
// Arrays too
const colors = ['red' , 'green' , 'blue']
const [first , second] = colors
// first = 'red', second = 'green'
spread operator - copy and merge without mutation¶
const defaults = { host: 'localhost' , port: 3000 }
const overrides = { port: 443 , ssl: true }
// Merge - later keys override earlier ones
const config = { ...defaults , ...overrides }
// { host: 'localhost', port: 443, ssl: true }
// Copy an array without reference
const original = [1 , 2 , 3]
const copy = [...original]
copy.push(4) // original unchanged
callbacks - the foundation of async Node¶
const fs = require('fs')
// Callback pattern - Node's original async primitive
// First argument is always an error (or null)
// Second argument is the result
fs.readFile('/etc/passwd' , 'utf8' , (err , data) => {
if (err) {
console.error('Failed to read file:' , err.message)
return
}
console.log('File contents:' , data)
})
Every async function in Node uses either callbacks , Promises , or async/await. Know callbacks because you'll see them in older codebases and some core modules still use them
Promises - callbacks without the nesting¶
const fs = require('fs/promises')
// Promise-based API - no callback nesting
fs.readFile('/etc/passwd' , 'utf8')
.then(data => console.log('File:' , data))
.catch(err => console.error('Error:' , err.message))
// async/await - Promises but looks synchronous
async function readConfig() {
try {
const data = await fs.readFile('/etc/config.json' , 'utf8')
return JSON.parse(data)
} catch (err) {
console.error('Config load failed:' , err.message)
return {}
}
}
what you do NOT need¶
These are browser APIs that don't exist in Node. Trying to use them will crash your process:
// These will throw ReferenceError in Node
// document.getElementById('app') // not defined
// window.addEventListener('load', fn) // not defined
// localStorage.getItem('token') // not defined
// fetch('/api/data') // actually works in Node 18+ (experimental)
// alert('hello') // definitely not defined
// What Node has instead:
// global - analogous to window (but not the same)
// process - process info and control
// require() - module loading
// __dirname - current file's directory
fetch is available in Node 18+ as an experimental feature (and stable in Node 21+), but it's built on Node's http module , not the browser's. Same API surface , different implementation under the hood
security note on eval and friends¶
The stuff you can do in Node that you can't in a browser includes eval , new Function() , and vm.runInThisContext(). These are dangerous in the browser. In Node , they're catastrophic:
// NEVER do this with untrusted input
const userInput = req.body.expression
// eval - full code execution
const result = eval(userInput) // RCE vector
// Function constructor - same thing , different syntax
const fn = new Function('return ' + userInput)
fn() // also RCE
// vm.runInThisContext - named misleadingly , NOT a sandbox
const vm = require('vm')
vm.runInThisContext(userInput) // has access to global , require , process
The vm module is explicitly documented as NOT a security boundary. You cannot run untrusted code safely in Node's default runtime. If you need to sandbox user code , use a separate process or a real sandbox (gVisor , Firecracker)
prerequisites¶
node_02 - Getting Started with Node
next -> node_04_vs_browser.md