A Managed Language vs. A Script-Born Language
C# and JavaScript, side by side — for someone who already thinks in C#. No general "what is a programming language" tour this time (Class 1 already covered that ground with Python) — straight into the comparisons that actually matter coming from C#.
The One-Sentence Version
C# is a statically-typed, compiled, class-based language built around the managed .NET runtime. JavaScript is a dynamically-typed, engine-interpreted-and-JIT-compiled language that started as browser scripting and grew into a general-purpose one, with an object model built on prototypes rather than classes underneath. Same broad job — most of these differences are not arbitrary; they follow from those two starting points.
Ten Places C# and JavaScript Genuinely Diverge
Typing: inferred-static vs. genuinely dynamic
Type systemvar age = 30;
age = "thirty"; // compile errorlet age = 30;
age = "thirty"; // fine, no errorC#'s var is compile-time type inference — the variable's type is fixed the moment the compiler sees the initializer; it's sugar, not dynamic typing. JavaScript's let/const hold no type at all — the value can be any type, and can change type on reassignment. There is no JS equivalent of "the compiler rejects this."
Null: one null vs. a three-way split
JavaScript-specific gotchastring? name = null;
Console.WriteLine(name is null);let name; // undefined
let city = null; // null
// referencing an undeclared name throwsC# (8+) has nullable reference types as an opt-in, compiler-enforced annotation on top of one null. JavaScript has no nullable-type annotations at all, but splits "nothing" into two runtime values plus a third failure mode — an unassigned variable is undefined, a deliberately empty one is null, and an undeclared name throws outright. This course's Lab 04 covers the split in full.
Equality: type-checked and overloadable vs. type-coercing
JavaScript-specific gotcha1 == "1"; // compile error — no implicit conversion1 == "1" // true (coerces)
1 === "1" // false (no coercion)C#'s == either compares matching types directly or calls an overloaded operator you (or the type) defined — mismatched types across most built-ins are a compile error, not a runtime surprise. JavaScript's == silently converts one or both operands before comparing; === is the one that behaves closer to what a C# developer expects from ==. Default to ===.
Classes: real class-based OOP vs. prototypes wearing class syntax
JavaScript-specific gotchapublic class Dog {
public string Name { get; }
public Dog(string name) => Name = name;
}class Dog {
constructor(name) {
this.name = name;
}
}In C#, class is the object model — types, vtables, and inheritance are class-based all the way down. In JavaScript, class is syntax sitting on top of a completely different underlying mechanism: prototype-based inheritance, where instances link directly to another object instead of to a compiler-managed type. It reads similarly; it is not the same model underneath. This course spends several labs specifically on that distinction.
Properties: dedicated syntax vs. get/set functions
Syntaxpublic int Age { get; set; }class Person {
#age;
get age() { return this.#age; }
set age(v) { this.#age = v; }
}C# auto-properties generate a backing field for you. JavaScript has no auto-property equivalent — a real getter/setter pair with a backing field is always written out by hand (here using a genuinely private #field, not just a naming convention).
Interfaces: a compiler-checked contract vs. nothing enforced at all
Type systempublic interface IQuacks { void Quack(); }
public class Duck : IQuacks {
public void Quack() { }
}function makeItQuack(thing) {
thing.quack(); // works if thing HAS a quack method
} // nothing declares this requirement anywhereC#'s interfaces are a compile-time contract — implementing one is declared and checked. JavaScript has no built-in interface concept at all: if an object happens to have the right method, calling it works; if it doesn't, you get a runtime TypeError the moment it's called, not before. (TypeScript adds compile-time structural interfaces back on top of JavaScript, but plain JS has none.)
Value types: C# actually has them; JavaScript doesn't
Language design choicestruct Point { public int X, Y; }
Point a = new Point { X = 1 };
Point b = a; // real copy
b.X = 99; // a.X is still 1let a = { x: 1 };
let b = a; // same object, not a copy
b.x = 99; // a.x is now 99 tooC# lets you define your own value types (struct) that copy on assignment. JavaScript has no user-definable value type at all — only primitives (number, string, boolean, etc.) copy by value; every object, array, and function you create is always a reference, always shared on assignment, with no opt-out. There's no JS equivalent of "make this type copy like an int does."
Async: real thread-pool parallelism vs. one thread, always
Runtimeawait Task.Run(() => HeavyCpuWork());
// can genuinely run on another threadawait someAsyncCall();
// still runs on the SAME single thread as everything elseThis is the one most worth slowing down for. C#'s Task/async/await can genuinely hand CPU-bound work to another OS thread via the thread pool — real parallelism is available. JavaScript is single-threaded, full stop, in every environment (browser tab or Node process) — async/await never creates a new thread; it's sugar over the event loop deciding when a paused function resumes on that same one thread. A CPU-heavy async function in JS still blocks everything else while it runs. This course dedicates several labs specifically to that model.
Execution: IL + CLR JIT vs. an engine doing it all at once
Runtime// csc compiles source -> IL (a .dll/.exe)
// CLR loads the IL and JIT-compiles it to
// native code at (or before) run time// no separate build artifact by default —
// an engine (V8, SpiderMonkey, ...) parses
// source directly, interprets and/or
// JIT-compiles hot paths while runningC# has a distinct ahead-of-time compile step producing an intermediate-language artifact, which the CLR then JIT-compiles at load/run time. Plain JavaScript has no separate compiled-artifact step at all in the way C# does — a JS engine goes from source text to running code itself, typically using a mix of parsing, interpretation, and JIT compilation internally, with no IL-equivalent file ever produced. "Compiled vs. interpreted" undersells how different these two pipelines actually are.
Encapsulation: four real access modifiers vs. one real one
Language design choiceprivate, protected, internal,
protected internal, public
// all compiler-enforcedclass Account {
#balance = 0; // truly private, enforced by the engine
balance = 0; // public — that's the only other option
}C# has a full set of compiler-checked access modifiers, including cross-assembly visibility (internal) with no JavaScript equivalent at all. JavaScript has exactly two real states: public (the default), or truly private via a #field (engine-enforced, added relatively recently to the language). There is no built-in protected or module-internal visibility — older JS code faked "private" with a leading underscore, which was never actually enforced by anything.
Tiny Experiment: Assignment Doesn't Copy the Way a struct Does
Primary objective: confirm JavaScript has no value-type opt-out for objects, directly.
// Paste this into a JavaScript console (browser or node) — do not run it yet.
let pointA = { x: 1, y: 1 };
let pointB = pointA;
pointB.x = 99;
console.log(pointA.x);
If Point were a C# struct, pointB = pointA would copy it, and pointA.x would still be 1 after. Predict what actually prints here, then reveal.
Tiny Experiment: await Doesn't Move Work Off the Thread
Primary objective: confirm that a JavaScript async function still blocks everything else while its own synchronous code runs.
// Paste this into a JavaScript console (browser or node) — do not run it yet.
console.log("before");
async function work() {
let total = 0;
for (let i = 0; i < 500000000; i++) total += i; // synchronous, CPU-bound
console.log("work done");
}
work();
console.log("after calling work()");
In C#, wrapping heavy work in an async method with Task.Run can keep the UI/caller responsive on another thread. Predict the print order here, and whether anything else could run while the loop is grinding.
The Takeaway
Most of your C# instincts about syntax will transfer fine — braces, semicolons, familiar keywords. The instincts worth actively rewiring are the ones this page focused on: type checking happens at compile time in C# and mostly doesn't exist in JS; C#'s object model is class-based while JS's is prototype-based underneath a class-shaped syntax; C# gives you real value types while JS never does; and C#'s async can use real threads while JS's never can. Everything else in this course builds out the JavaScript side of that picture in detail.