← all labs

Orientation Bonus Class

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#.

What this class produces: no files, no commit — reading and two tiny runnable experiments. This is a personal supplement, not part of the main numbered sequence.
Not part of the Class 1/2/Lab 03+ numbering — this is a standalone bonus lesson for the C#-background angle specifically. Skip it entirely if you're following the main sequence and don't need the C# framing.

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 system
C#
var age = 30;
age = "thirty"; // compile error
JavaScript
let age = 30;
age = "thirty"; // fine, no error

C#'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 gotcha
C#
string? name = null;
Console.WriteLine(name is null);
JavaScript
let name;        // undefined
let city = null; // null
// referencing an undeclared name throws

C# (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 gotcha
C#
1 == "1"; // compile error — no implicit conversion
JavaScript
1 == "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 gotcha
C#
public class Dog {
    public string Name { get; }
    public Dog(string name) => Name = name;
}
JavaScript
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

Syntax
C#
public int Age { get; set; }
JavaScript
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 system
C#
public interface IQuacks { void Quack(); }
public class Duck : IQuacks {
    public void Quack() { }
}
JavaScript
function makeItQuack(thing) {
  thing.quack(); // works if thing HAS a quack method
}                // nothing declares this requirement anywhere

C#'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 choice
C#
struct Point { public int X, Y; }
Point a = new Point { X = 1 };
Point b = a; // real copy
b.X = 99;    // a.X is still 1
JavaScript
let a = { x: 1 };
let b = a;   // same object, not a copy
b.x = 99;    // a.x is now 99 too

C# 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

Runtime
C#
await Task.Run(() => HeavyCpuWork());
// can genuinely run on another thread
JavaScript
await someAsyncCall();
// still runs on the SAME single thread as everything else

This 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
C#
// csc compiles source -> IL (a .dll/.exe)
// CLR loads the IL and JIT-compiles it to
// native code at (or before) run time
JavaScript
// no separate build artifact by default —
// an engine (V8, SpiderMonkey, ...) parses
// source directly, interprets and/or
// JIT-compiles hot paths while running

C# 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 choice
C#
private, protected, internal,
protected internal, public
// all compiler-enforced
JavaScript
class 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.

← All labs