Variable Scope: Local, Global and Enclosing Names

Keep one student’s temporary calculation local

A school report has a school name, but each total calculation needs temporary marks. Those local values should not interfere with another report’s variables just because the names happen to be similar.

Scope is the region where a name can be resolved. A local name belongs to a function or block; an outer name can be visible to code nested inside its scope.

  1. Locate a name

    Distinguish function, block and outer bindings.

  2. Trace shadowing

    Explain which same-named value a function reads.

  3. Reduce hidden state

    Prefer explicit inputs when shared mutable globals would make behavior surprising.

Resolve a name from the inside outward

  1. Function local

    The function creates its own student name and mark.

    Maya; 40
  2. Outer scope

    The school name is visible from the surrounding scope.

    River School
  3. Outside again

    The outer student name was not reassigned.

    Ravi
Read these stages alongside the complete example. They explain the relationship; they do not execute code.

A repeated name can describe a different binding

JavaScript let and const belong to their enclosing block, including a function body. A function can read names from its surrounding lexical scope when no nearer declaration supplies that name.

A nearer declaration with the same name shadows the outer one. This does not make the two variables automatically connected. Excessive shadowing is confusing; use distinct names when it obscures meaning.

Before starting, review function inputs and results. These examples use JavaScript; other languages can use different syntax and rules.

Trace local and outer student names

Inside report, studentName resolves to Maya. The school name comes from the outer scope. After the function call, the outer studentName remains Ravi.

The local mark is not available by that name outside the function. Passing or returning a value is a clearer way to share it deliberately.

JavaScript · local teaching example

const schoolName = 'River School';
const studentName = 'Ravi';
function report() {
  const studentName = 'Maya';
  const mark = 40;
  return schoolName + ': ' + studentName + ' ' + mark;
}
console.log(report());
console.log(studentName);

Expected output

River School: Maya 40
Ravi

A block can also have its own value

The braces create a block scope for this let declaration. The inner count shadows the outer count while execution is inside the block.

Leaving the block restores access to the outer binding. It does not assign the inner value back to it.

JavaScript · local teaching example

let count = 10;
{
  let count = 2;
  console.log(count);
}
console.log(count);

Expected output

2
10

Apply the rule to a small change

Rewrite the report so the student name is supplied as a parameter. Call it for Maya and Asha.

Worked answer

The caller supplies the changing student name explicitly. The function can still read the fixed school name, but its student-specific behavior no longer depends on an outer student binding.

JavaScript · local teaching example

const schoolName = 'River School';
function report(studentName) {
  return schoolName + ': ' + studentName;
}
console.log(report('Maya'));
console.log(report('Asha'));

Expected output

River School: Maya
River School: Asha

Recognize mistakes and limits

Assigning to an existing outer JavaScript let without a nearer declaration changes that shared binding. Such hidden changes make call ordering matter. Prefer explicit inputs and results unless shared state is a deliberate design.

Python has global and nonlocal declarations for assignments: global targets a module-level binding, while nonlocal targets an existing enclosing function binding. They are Python syntax, not JavaScript keywords. Reading an outer name and assigning one have different scope implications.

JavaScript var has function-scoped behavior unlike let’s block scope. Reads before a let or const declaration is reached can throw. Do not try to infer those rules only from indentation.

For the language or tool rules used here, see JavaScript functions and scope.

Check the core idea

JavaScript · code for this question

const schoolName = 'River School';
const studentName = 'Ravi';
function report() {
  const studentName = 'Maya';
  const mark = 40;
  return schoolName + ': ' + studentName + ' ' + mark;
}
console.log(report());
console.log(studentName);
Why does the final output still show Ravi?
Choose one answer. You can try again.

Answer and explanation

The function’s local declaration shadows rather than reassigns the outer binding.

Yes. The two names refer to distinct bindings.

Apply the idea to a new situation

What does Python nonlocal target for assignment?
Choose one answer. You can try again.

Answer and explanation

An existing binding in an enclosing function scope.

Yes. It is used for a particular outer-function assignment.

Know which binding each read and write uses

Scope controls name resolution. Local and block declarations can shadow outer names; shared outer writes can create hidden dependencies. Language-specific global and nonlocal rules should be read in that language’s context.

Connect this idea with functions retaining access to outer bindings and clear names and responsibilities.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Recursion: Base Cases and Smaller Problems →