作用域
作用域是指代码中定义变量的区域。即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。换句话说,作用域决定了代码区块中变量和其他资源的可见性
举个例子:
function myFunction() {
let inVariable = "函数内部变量";
}
myFunction();//要先执行这个函数,否则根本不知道里面是啥
console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined
上述例子中,当我们在全局访问这个变量的时候,系统会报错。这就说明我们在全局是无法获取到(闭包除外)函数内部的变量。
作用域一般分成:
- 全局作用域
- 函数作用域
- 块级作用域
1. 全局作用域
任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问。
在代码的任何地方都能访问到的变量和函数处于全局作用域。当你在任何地方(不在任何函数内部)声明一个变量或函数时,它都处于全局作用域。全局变量在整个程序中都是可见的,但过度使用全局变量可能会导致命名冲突和代码难以维护。
var globalVar = 'I am a global variable';
function globalFunction() {
console.log('I am a global function');
}
2. 函数作用域(局部作用域)
函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面。这些变量只能在函数内部访问,不能在函数以外去访问,它们的作用域被限制在函数体内部。这意味着在函数外部无法直接访问函数内部声明的变量或函数,除非它们被显式地暴露为全局变量或通过某种机制(如返回值或闭包)传递到外部。
function exampleFunction() {
var localVar = 'I am a local variable';
function localFunction() {
console.log('I am a local function');
}
console.log(localVar); // 输出: I am a local variable
localFunction(); // 输出: I am a local function
}
exampleFunction();
console.log(localVar); // ReferenceError: localVar is not defined 函数内部声明,只能内部访问
localFunction(); // ReferenceError: localFunction is not defined 函数内部声明,只能内部访问
可见上述代码中在函数内部声明的变量或函数,在函数外部是无法访问的,这说明在函数内部定义的变量或者方法只是函数作用域
3. 块级作用域
ES6引入了let和const关键字,和var关键字不同,在大括号中使用let和const声明的变量存在于块级作用域中。在大括号之外不能访问这些变量,这些变量只在声明它们的代码块(例如 if 语句、for 循环等)内部可见。这进一步增强了作用域的控制能力。
if (true) {
let blockScopedVar = 'I am block-scoped';
console.log(blockScopedVar); // 输出: I am block-scoped
}
console.log(blockScopedVar); // ReferenceError: blockScopedVar is not defined
{
// 块级作用域中的变量
let greeting = 'Hello World!';
var lang = 'English';
console.log(greeting); // Prints 'Hello World!'
}
console.log(lang); // 变量 'English'
console.log(greeting); // 报错:Uncaught ReferenceError: greeting is not defined
静态作用域与动态作用域
1. 静态作用域(词法作用域)
函数的作用域在函数定义的时候就确定了
具体来说,函数的作用域是由其在代码中定义的位置决定的,而不是由函数调用的位置决定。
当函数内部需要访问某个变量时,会按照从当前作用域开始,逐级向上查找,直到找到全局作用域为止的顺序进行查找。(作用域链)
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
2. 动态作用域
函数的作用域是在函数调用的时候才确定
这意味着,函数的作用域取决于函数在何处被调用,以及调用它的上下文环境。
.
让我们认真看个例子就能明白之间的区别:
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();
// 结果是 ???
假设JavaScript采用静态作用域,让我们分析下执行过程:
执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。
假设JavaScript采用动态作用域,让我们分析下执行过程:
执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。
前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 1。
作用域链
当在Javascript中使用一个变量的时候,首先Javascript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域
如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错
把作用域比喻成一个建筑,这份建筑代表程序中的嵌套作用域链,第一层代表当前的执行作用域,顶层代表全局作用域
变量的引用会顺着当前楼层进行查找,如果找不到,则会往上一层找,一旦到达顶层,查找的过程都会停止
var sex = '男';
function person() {
var name = '张三';
function student() {
var age = 18;
console.log(name); // 张三
console.log(sex); // 男
}
student();
console.log(age); // Uncaught ReferenceError: age is not defined
}
person();