🚀 作用域:代码的隐秘地盘
首先,让我们来理解一下什么是作用域。在JavaScript中,作用域指的是变量的可访问性范围。也就是说,不同位置的代码可以访问不同范围内的变量。就好像你家的客厅和卧室里的东西,客厅里的东西不一定在卧室里能找到。
📚 作用域的类型
Javascript中有三种作用域:
- 全局作用域;
- 函数作用域;
- 块级作用域;
🏙️ 全局作用域:变量的大舞台
全局作用域就像是城市的中心广场,所有人都可以访问。在任何地方定义的全局变量都可以被整个脚本访问。例如:
ini
let globalVar = "我是全局变量";
function showGlobalVar() {
console.log(globalVar);
}
showGlobalVar(); // 输出:我是全局变量
在这个例子中,变量globalVar在全局范围内定义,所以函数showGlobalVar可以自由访问它。
🏠 函数作用域:变量的私密角落
与之相对,函数作用域就像是你的私人领地。在函数内定义的变量只能在函数内部访问,就像是你的房间里的物品,无法被其他房间的人看到。
javascript
function localScopeExample() {
let localVar = "我是局部变量";
console.log(localVar);
}
localScopeExample(); // 输出:我是局部变量
console.log(localVar); // 报错!localVar未定义
在这个例子中,变量localVar只能在localScopeExample函数内部访问,尝试在函数外部访问它会导致错误。
🧱块级作用域:变量的秘密小角落
相似于函数作用域,块级作用域是你的编程领地中的一处秘密小角落。在块级作用域中定义的变量只能在该块中访问,就如同你的书房里的私人收藏,无法被其他房间的人所窥见。
javascript
{
// 块级作用域中的变量
let greeting = 'Hello World!';
var lang = 'English';
console.log(greeting); // Prints 'Hello World!'
}
// 变量 'English'
console.log(lang);
// 报错:Uncaught ReferenceError: greeting is not defined
console.log(greeting);
🔮 作用域嵌套:代码的魔法迷宫
像Javascript中函数可以在一个函数内部声明另一个函数一样,作用域也可以嵌套在另一个作用域中。请看例子:
ini
var name = 'Peter';
function greet() {
var greeting = 'Hello';
{
let lang = 'English';
console.log(`${lang}: ${greeting} ${name}`);
}
}
greet();
🚁 词法作用域:代码的时光机器
词法作用域(也叫静态作用域)从字面意义上看是说作用域在词法化阶段(通常是编译阶段)确定而非执行阶段确定的。形象一点来说就是,词法作用域就像是代码的时光机器,它在代码编写的时候就决定了变量在哪里被找到。这就像是你在写日记的时候,每个词语都记录下了当时的情感和环境。无论你在哪里调用函数,它都会回到过去,找到当初写下的内容,就像是读取你的时间旅行日记一样!
例子:
scss
let number = 42;
function printNumber() {
console.log(number);
}
function log() {
let number = 54;
printNumber();
}
// Prints 42
log();
上面代码可以看出无论printNumber()
在哪里调用console.log(number)
都会打印42
。动态作用域 不同,console.log(number)
这行代码打印什么取决于函数printNumber()
在哪里调用。
如果是动态作用域 ,上面console.log(number)
这行代码就会打印54
。
使用词法作用域,我们可以仅仅看源代码就可以确定一个变量的作用范围,但如果是动态作用域,代码执行之前我们没法确定变量的作用范围。
像C,C++,Java,Javascript等大多数编程语言都支持静态作用域。Perl 既支持动态作用域也支持静态作用域。
🔗 作用域链:连接不同作用域的纽带
当在Javascript中使用一个变量的时候,首先 Javascript
引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域。
如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错。
例如:
ini
let foo = 'foo';
function bar() {
let baz = 'baz';
// 打印 'baz'
console.log(baz);
// 打印 'foo'
console.log(foo);
number = 42;
console.log(number); // 打印 42
}
bar();
当函数bar()
被调用,Javascript引擎首先在当前作用域下寻找变量baz
,然后寻找foo变量但发现在当前作用域下找不到,然后继续在外部作用域寻找找到了它(这里是在全局作用域找到的)。
然后将42
赋值给变量number
。Javascript引擎会在当前作用域以及外部作用域下一步步寻找number变量(没找到)。
如果是在非严格模式下,引擎会创建一个number
的全局变量并把42
赋值给它。但如果是严格模式下就会报错了。
结论:当使用一个变量的时候,Javascript引擎会循着作用域链一层一层往上找该变量,直到找到该变量为止。