共计 2618 个字符,预计需要花费 7 分钟才能阅读完成。
JavaScript 的作用域是一个十分根底且重要的概念,对于初学者来说,常常会感觉有些凌乱搞不清楚。本文会具体介绍 JavaScript 作用域,包含全局作用域、函数作用域和块级作用域,以及 ES6+ 新增的 let
、const
和block scope
等个性,让你彻底搞懂作用域。
1. JavaScript 作用域简介
在 JavaScript 中,作用域是指在代码中定义变量的区域。这个区域定义了变量的可见性和生命周期。JavaScript 有两种次要的作用域类型:全局作用域和函数作用域。
var globalVar = "I'm global!"; // 全局作用域
function someFunction() {
var functionVar = "I'm local!"; // 函数作用域
console.log(globalVar); // 能够拜访全局变量
console.log(functionVar); // 能够拜访函数内的变量
}
console.log(globalVar); // 能够拜访全局变量
console.log(functionVar); // 报错,不能拜访函数内的变量
2. 全局作用域
在代码的任何中央都能拜访到的变量被定义在全局作用域。在浏览器环境中,全局变量被定义在 window
对象上。
3. 函数作用域
在函数外部定义的变量只能在函数外部拜访,这就是函数作用域。这意味着,如果你在一个函数外部定义了一个变量,那么这个变量在函数内部是不可见的。
4. ES6+ 块级作用域
ES6 引入了两种新的申明形式:let
和 const
,它们与var
相比,最大的区别就是它们具备块级作用域。块级作用域是指变量在最近的 {}
代码块内无效。
if (true) {
var varVar = "I'm var!"; // 函数作用域
let letVar = "I'm let!"; // 块级作用域
const constVar = "I'm const!"; // 块级作用域
}
console.log(varVar); // 输入 "I'm var!"
console.log(letVar); // 报错,letVar 未定义
console.log(constVar); // 报错,constVar 未定义
5. let
和const
let
和 const
都是块级作用域,它们的作用范畴被限度在最近的一对花括号 {}
内。let
容许你从新赋值,而 const
定义的是一个常量,一旦赋值就不能扭转。
let letVar = "I'm let!";
letVar = "I've changed!"; // 没有问题
const constVar = "I'm const!";
constVar = "I'm trying to change!"; // 报错,不能扭转 const 变量的值
6. 作用域链
当你在一个函数外部尝试拜访一个变量时,JavaScript 会首先在以后的作用域查找。如果没有找到,它会去外层的作用域查找,直到找到为止。这就是作用域链。
var outerVar = "I'm outer!";
function innerFunction() {console.log(outerVar); // 输入 "I'm outer!"
}
innerFunction();
7. 闭包
闭包是 JavaScript 中一个重要的概念。当一个函数可能记住并拜访所在的词法作用域,即便该函数在词法作用域内部执行,这就产生了闭包。
functionouterFunction() {
var outerVar = "I'm outer!";
function innerFunction() {console.log(outerVar); // 输入 "I'm outer!"
}
return innerFunction;
}
var myFunction = outerFunction();
myFunction(); // 即便在 outerFunction()执行完后,innerFunction()依然能够拜访 outerVar,这就是闭包
8. 作用域深度解读
了解了 JavaScript 作用域的根底后,咱们通过一些小栗子来坚固下这个概念。
练习 1:变量晋升
思考以下代码,试着预测输入后果,并解释起因。
var name = "John";
function sayName() {console.log(name);
var name = "Jane";
console.log(name);
}
sayName();
输入后果为:
undefined
Jane
这是因为 JavaScript 存在变量晋升(hoisting)的机制,var name = "Jane";
会被晋升到 sayName
函数的顶部,但只晋升变量名,不晋升赋值操作。所以第一个 console.log(name);
打印出的是undefined
。
练习 2:应用闭包创立公有变量
闭包能够用来创立公有变量,从而实现封装和数据暗藏。思考下如何应用闭包来创立公有变量。
function createCounter() {
var count = 0;
return function() {return ++count;};
}
var counter = createCounter();
console.log(counter()); // 输入 1
console.log(counter()); // 输入 2
在这个例子中,count
就是一个公有变量。内部无奈间接拜访 count
,只能通过counter
函数来操作。
9. 利用在异步编程中
在异步编程中,作用域起着十分重要的作用。当初思考上面代码:
for (var i = 0; i < 5; i++) {setTimeout(function() {console.log(i);
}, 1000);
}
这段代码的输入会让很多初学者感到意外,它会打印出五个 5,而不是 0 到 4。这是因为 setTimeout
中的回调函数是在循环完结后才执行的,而此时的 i
曾经变成了 5。
如果咱们想要打印出 0 到 4,咱们能够应用 let
来创立块级作用域:
for (let i = 0; i < 5; i++) {setTimeout(function() {console.log(i);
}, 1000);
}
或者咱们也能够应用闭包来创立一个新的作用域:
for (var i = 0; i < 5; i++) {(function(i) {setTimeout(function() {console.log(i);
}, 1000);
})(i);
}
10. 总结
了解作用域对于编写可保护的代码十分重要。心愿本文能帮忙你了解全局作用域、函数作用域、块级作用域 let
、const
及闭包等重要概念。