Python vs JavaScript Variables
How variable binding, scoping, and string interpolation differ between Python and JavaScript.
Declaration and Reassignment
Python uses bare assignment. A name springs into existence the moment you bind it to a value, and you can rebind it to a completely different type on the next line. There is no declaration keyword. JavaScript, by contrast, introduced three declaration keywords over the years: var (function-scoped, hoisted), let (block-scoped), and const (block-scoped, non-reassignable). The distinction matters because var leaks out of if-blocks and for-loops, a behavior that tripped up enough developers to motivate the ES6 additions. Python sidesteps the whole conversation: every variable is block-scoped to the enclosing function (or module), and there is no concept of hoisting. A name simply does not exist until execution reaches its assignment. One consequence of Python's approach is that you cannot declare a variable without assigning it. JavaScript's let x; creates an undefined binding, which has no Python equivalent. If you need a placeholder in Python, you assign None explicitly. The mental model shift is small but worth internalizing: Python treats names as labels stuck onto objects, while JavaScript treats variables as boxes that hold values (or references).
Basic variable assignment
name = "Ada"
age = 30
name = "Grace" # rebind freelylet name = "Ada";
let age = 30;
name = "Grace"; // reassignment OK with letConstants
MAX_RETRIES = 5 # convention only, not enforced
# MAX_RETRIES = 10 # nothing stops youconst MAX_RETRIES = 5;
// MAX_RETRIES = 10; // TypeError at runtimeCan you write this from memory?
Write an expression that splits `item` by commas into a list of strings
Type Coercion and Dynamic Typing
Both languages are dynamically typed, but they handle type mismatches differently. Python raises a TypeError when you try to add a string and an integer. JavaScript silently coerces one operand to match the other, producing results that have become a running joke in the developer community ("1" + 2 === "12"). This design choice reflects JavaScript's origins as a forgiving browser scripting language: Brendan Eich prioritized keeping pages running over stopping execution. Python's philosophy -- errors should never pass silently -- takes the opposite stance. The practical fallout is that Python developers moving to JavaScript need to adopt defensive habits: strict equality (===) instead of loose equality (==), explicit Number() or String() conversions, and linter rules that flag implicit coercion. Going the other direction, JavaScript developers arriving in Python discover that the language simply refuses to guess what you meant, which eliminates an entire category of bugs at the cost of occasional verbosity.
String plus number
# "Age: " + 30 # TypeError
"Age: " + str(30) # => "Age: 30""Age: " + 30 // => "Age: 30" (coerced)
"Age: " + String(30) // explicitEquality comparison
1 == True # => True (bool is int subclass)
1 == "1" # => False1 == true // => true (coerced)
1 === true // => false (strict)
1 == "1" // => true (coerced)String Interpolation
Python f-strings (3.6+) and JavaScript template literals (ES6) solve the same problem -- embedding expressions inside strings -- but the delimiter syntax differs. Python prefixes the string with f and uses curly braces: f"Hello {name}". JavaScript wraps the whole string in backticks and uses dollar-brace: `Hello ${name}`. Both support arbitrary expressions inside the interpolation brackets, including method calls, ternaries, and arithmetic. One difference that catches people: Python f-strings can include format specifiers after a colon (f"{price:.2f}"), while JavaScript template literals rely on external calls like toFixed(2). Another gap is multi-line strings. JavaScript template literals preserve newlines by default, making them handy for HTML fragments. Python's triple-quoted strings ("""...""") serve the same role, but they are a separate feature from f-strings, meaning you combine them as f"""...""". The two approaches end up roughly equivalent in power, though the ergonomics of formatting numbers and dates still tilt toward Python.
Basic interpolation
name = "Ada"
print(f"Hello, {name}!")
# => Hello, Ada!const name = "Ada";
console.log(`Hello, ${name}!`);
// => Hello, Ada!Expression inside interpolation
price = 19.995
print(f"Total: {price:.2f{'}'}")
# => Total: 20.00const price = 19.995;
console.log(`Total: ${price.toFixed(2)}`);
// => Total: 20.00Can you write this from memory?
Write an expression that splits `item` by commas into a list of strings
Scope Rules
Python has four scope levels: local, enclosing function, global, and built-in (the LEGB rule). JavaScript with let and const has block scope inside any curly-brace block, plus function scope and global scope. The difference shows up most clearly in loops. A Python for-loop variable leaks into the enclosing function scope -- after for i in range(5), the name i is still accessible and holds 4. JavaScript's let in a for-loop creates a fresh binding per iteration, which is why closures inside loops work as expected with let but produced the classic "all callbacks print 5" bug with var. Python developers writing closures over loop variables need the same awareness: capturing a loop variable by reference in a lambda grabs the final value unless you use the default-argument trick (lambda i=i: i). The scope differences also surface in variable shadowing. Python disallows accidental shadowing -- assigning to a variable inside a function creates a local, even if a global of the same name exists, and reading it before assignment raises UnboundLocalError. JavaScript happily shadows with a new let declaration in any inner block.
Loop variable leaking
for i in range(3):
pass
print(i) # => 2 (still accessible)for (let i = 0; i < 3; i++) {}
// console.log(i); // ReferenceErrorClosure in a loop
fns = [lambda i=i: i for i in range(3)]
print(fns[0]()) # => 0const fns = [];
for (let i = 0; i < 3; i++) {
fns.push(() => i);
}
console.log(fns[0]()); // => 0