JavaScript的作用域問題是一個比較複雜的問題也是在編寫程序過程中比較容易出錯的地方,特別是從C/C++這些具有塊級語言功能的語言轉過來的工程師。
JavaScript在ES6時代之前是沒有塊級作用域的概念只有全局作用域和函數級作用域這兩種作用域。同時由於JavaScript定義變量的特殊性導致編寫程序的時候容易出現一些問題造成不少的麻煩。
JavaScript定義變量的特殊性:
(1)使用var定義變量的時候會產生變量提升。
(2)使用var定義變量的時候,允許在同一作用域下多次使用var來進行聲明變量,但是隻有第一次是進行聲明變量,剩下的是進行賦值操作。
(3)在對值進行賦值的時候,先會在當前作用域下查看是否有此變量的聲明,如果有進行變量的賦值,如果沒有就向上一級作用域進行查找,查找到就對其進行賦值,如果沒有查找到將在全局的作用域下進行賦值。
<!DOCTYPE html>
<html>
<head>
<title>變量的作用域問題</title>
<meta charset="utf-8">
</head>
<body>
<script>
var name="apple"
var name="watermelon"
function say(){
console.log(name);
if(1){
var name="banana"
}
console.log(name);
name="orange";
console.log(name);
}
say();
console.log(name);
</script>
</body>
</html>
上述程序輸出的結果依次是:
undefined
banana
orange
watermelon
當對程序進行修改之後
<!DOCTYPE html>
<html>
<head>
<title>變量的作用域問題</title>
<meta charset="utf-8">
</head>
<body>
<script>
var name="apple"
var name="watermelon"
function say(){
console.log(name);
if(1){
name="banana";
age=24
}
console.log(name);
name="orange";
console.log(name);
}
say();
console.log(name);
console.log(age);
</script>
</body>
</html>
程序輸出的結果依次是:
watermelon
banana
orange
orange
通過上述的兩個程序可以看出這兩個函數在作用域上的區別:由於第一個程序中是使用var聲明的變量所以會在函數作用域中創建name變量,不使用var聲明變量將會使用上一級的name變量。
通過說明ES5前作用域的問題,暴露了ES5編寫程序的致命問題,如果忘記使用var在函數作用域中聲明變量將會使得全局作用域下同名變量被修改。這樣的話是不利於編寫大規模的程序,同時也是由於這個原因也需要提供塊級作用域,以避免函數作用域中和全局作用域中變量污染 和變量不夠的問題,所以ES6提出了let和const的塊級作用域的函數的聲明以解決var聲明的問題。
(1)沒有變量的提升
(2)同一作用域下不能進行多次對同一變量的聲明
(3)提供塊級作用域
<!DOCTYPE html>
<html>
<head>
<title>變量的作用域問題</title>
<meta charset="utf-8">
</head>
<body>
<script>
let name="apple"
function say(){
console.log(name);
if(1){
let name="banana";
}
console.log(name);
name="orange";
console.log(name);
}
say();
console.log(name);
console.log(age);
</script>
</body>
</html>
當程序這樣編寫的時候會輸出:
apple
apple
orange
orange
出現這樣的情況是由於let聲明的name函數只在if形成的塊級作用域中有效。