在程式設計中，**變數遮蔽**（英語：，或稱變數隱藏）指的是：當新變數與舊有變數同名，此名稱暫時不再可用於存取舊有變數。

通常此新變數會被定義於一作用域（決策塊、方法或內部類）之中。 以程式語言在解析識別碼（即名稱，而不只是變數）的角度來看，這就是名稱遮蔽，而我們稱此舊有的變數被新變數遮蔽（be *shadowed* by）掉，而新變數遮蔽了（mask）舊有的變數。因為被遮蔽的名稱，在後續使用上可能不便於判定它指向哪個變數，所以此設計有時會迷惑使用者。判定方法端看該程式語言如何進行名稱解析。

ALGOL 是最早使用變數遮蔽此一設計的語言之一：它是第一個使用程式塊來建立作用域的語言。此設計也被其所衍生出的語言認可並採用，其中就包含了 C、 C++ 和 Java。

C# 則打破了這種傳承，而允許在內部類與外部類之間、方法與其包含的類之間執行變數遮蔽；但不允許在 if 塊與其內的方法之間、或switch块中的 case 陳述式之間進行變數遮蔽。

有些語言有更多種遮蔽變數的模式，例如 Kotlin 可以用函數內的變數遮蔽傳入的參數，以及內部代碼塊的變數遮蔽外部代碼塊的變數。而Java不允許這兩種模式。但是，此二語言都可以用傳遞給函數／方法的參數來遮蔽類字段（Class Field）。Rust 則允許相同代碼塊中，透過重複聲明的方式來遮蔽變數。

有些語言則是完全不允許變數遮蔽，譬如 CoffeeScript 。

範例
--

### Lua

以下 Lua 代碼在多個代碼塊中，示範如何遮蔽變數。

```
v = 1 -- 一個全域變數

do
  local v = v + 1 -- 一個遮蔽了變數 v 的新區域變數
  print(v) -- 印出 2

  do
    local v = v \* 2 -- 另一個新的區域變數，遮蔽了區塊外部的變數 v
    print(v) -- 印出 4
  end

  print(v) -- 印出 2
end

print(v) -- 印出 1

```

### Python

以下 Python 代碼提供了另一個變數遮蔽的例子：

```
x = 0

def outer():
    x = 1

    def inner():
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# 印出結果：
# inner: 2
# outer: 1
# global: 0

```
由於 Python 中，變數不必聲明即可賦值，故 Python 3 中引入關鍵字 `nonlocal`，用於避免變數遮蔽，而得以對非區域變數賦值：

```
x = 0

def outer():
    x = 1

    def inner():
        nonlocal x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# 印出結果：
# inner: 2
# outer: 2
# global: 0

```
而關鍵字 `global` 的功能則是避免變數遮蔽，兼賦值給全域變數：

```
x = 0

def outer():
    x = 1

    def inner():
        global x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# 印出內容：
# inner: 2
# outer: 1
# global: 2

```

### Rust

Rust 語言尤其不同，其允許在同一個代碼塊中多次宣告同名變數：後宣告者遮蔽先宣告者。

```
fn main() {
 let x = 0;
 
 {
 let x = 1;
 println!("Inner x: {}", x); // 印出 1
 }
 
 println!("Outer x: {}", x); // 印出 0
 
 // 同作用域的遮蔽
 let x = "Rust";
 println!("Outer x: {}", x); // 印出 'Rust'
}

//# Inner x: 1
//# Outer x: 0
//# Outer x: Rust

```

### C++

```
#include <iostream>

int main()
{
 int x = 42;
 int sum = 0;

 for (int i = 0; i < 10; i++) {
 int x = i;
 std::cout << "x: "<< x << '\n'; // 印出 i 的值，從 0 到 9
 sum += x;
 }

 std::cout << "sum: "<< sum << '\n';
 std::cout << "x: "<< x << '\n'; // 印出 42

 return 0;
}

```

### Java

```
public class Shadow {
 private int myIntVar = 0;

 public void shadowTheVar() {
 // 因為此變數名稱與上文的物件名稱相同，
 // 所以在此方法中，它遮蔽了上述變數
 int myIntVar = 5;

 // 若只使用 'myIntVar' 來指涉變數，會指向此方法中定義的變數
 // （遮蔽了另一個同名的變數）
 System.out.println(myIntVar); // 印出 5

 // 若我們想要存取此 class 中被定義的、現正被遮蔽的 myIntVar，
 // 我們須以如下方式指涉它：
 System.out.println(this.myIntVar); // 印出 0
 }

 public static void main(String[] args){
 new Shadow().shadowTheVar();
 }
}

```

### JavaScript

ECMAScript 6 引入了以代碼塊為作用域的 `let` 與 `const` 關鍵字，從而允許變數遮蔽。

```
function myFunc() {
 let my\_var = 'test';
 if (true) {
 let my\_var = 'new test';
 console.log(my\_var); // new test
 }
 console.log(my\_var); // test
}
myFunc();

```

參見
--

* 函式多載／重载
* 多型
* 名稱綁定

參考資料
----

1. ↑ .  [2021-10-04]. （原始内容存档于2020-11-28）.
2. 1 2 . doc.rust-lang.org.  [2022-12-04]. （原始内容存档于2022-12-08）.
3. ↑ . GitHub.  [2021-10-04]. （原始内容存档于2021-10-04）.
4. ↑ . GeeksforGeeks. 2021-03-05  [2022-12-04]. （原始内容存档于2022-12-04） （美国英语）.