你,我的朋友,完全糊涂了。您的第一句话本身就是错误的:
函数在定义时而不是在执行时创建它们的环境(范围)
其实恰恰相反。定义函数不会创建范围。调用函数会创建一个作用域。
什么是范围?
简单来说,作用域就是变量的生命周期。你看,每一个变量都是诞生、生和死的。作用域的开始标志着变量的诞生时间,作用域的结束标志着它死亡的时间。
一开始只有一个范围(称为程序范围或全局范围)。在这个范围内创建的变量只有在程序结束时才会消失。它们被称为全局变量。
例如,考虑这个程序:
const x = 10; // global variable x
{ // beginning of a scope
const x = 20; // local variable x
console.log(x); // 20
} // end of the scope
console.log(x); // 10
在这里,我们创建了一个名为 的全局变量x
。然后我们创建了一个块作用域。在这个块范围内,我们创建了一个局部变量x
。由于在我们登录时局部变量会影响全局变量,x
因此我们得到20
. x
当我们登录时回到全局范围10
(本地x
现在已经死了)。
块作用域和函数作用域
现在编程中有两种主要的作用域类型——块作用域和函数作用域。
上一个示例中的作用域是块作用域。这只是一段代码。由此得名。块作用域立即执行。
另一方面,函数作用域是块作用域的模板。顾名思义,函数作用域属于函数。但是,更准确地说,它属于函数调用。在调用函数之前,函数作用域不存在。例如:
const x = 10;
function inc(x) {
console.log(x + 1);
}
inc(3); // 4
console.log(x); // 10
inc(7); // 8
正如您所看到的,每次调用函数时都会创建一个新范围。这就是你得到输出的原因4
,10
和8
。
最初,JavaScript 只有函数作用域。它没有块作用域。因此,如果你想创建一个块作用域,那么你必须创建一个函数并立即执行它:
const x = 10; // global variable x
(function () { // beginning of a scope
const x = 20; // local variable x
console.log(x); // 20
}()); // end of the scope
console.log(x); // 10
这种模式称为立即调用函数表达式 (IIFE)。当然,现在我们可以使用const
and创建块范围的变量let
。
词法作用域和动态作用域
函数作用域又可以有两种类型——词法作用域和动态作用域。你看,在一个函数中有两种类型的变量:
- 自由变量
- 绑定变量
在范围内声明的变量绑定到该范围。未在作用域内声明的变量是免费的。这些自由变量属于其他范围,但属于哪个范围?
词法作用域
在词法作用域中,自由变量必须属于父作用域。例如:
function add(x) { // template of a new scope, x is bound in this scope
return function (y) { // template of a new scope, x is free, y is bound
return x + y; // x resolves to the parent scope
};
}
const add10 = add(10); // create a new scope for x and return a function
console.log(add10(20)); // create a new scope for y and return x + y
与大多数编程语言一样,JavaScript 具有词法作用域。
动态范围
与词法作用域相反,在动态作用域中,自由变量必须属于调用作用域(调用函数的作用域)。例如(这也不是 JS - 它没有动态范围):
function add(y) { // template of a new scope, y is bound, x is free
return x + y; // x resolves to the calling scope
}
function add10(y) { // template of a new scope, bind y
var x = 10; // bind x
return add(y); // add x and y
}
print(add10(20)); // calling add10 creates a new scope (the calling scope)
// the x in add resolves to 10 because the x in add10 is 10
就是这样。简单吧?
问题
您的第一个程序的问题是 JavaScript 没有动态范围。它只有词法作用域。看到错误了吗?
function f1() {
var a = 1;
f2();
}
function f2() {
return a;
}
f1(); // a is not defined (obviously - f2 can't access the `a` inside f1)
您的第二个程序非常混乱:
function f() {
var b = "barb";
return function() {
return b;
}
}
console.log(b); //ReferenceError: b is not defined
以下是错误:
- 你从来没有打电话
f
。b
因此,永远不会创建变量。
- 即使您调用
f
该变量b
也将是本地的f
。
这是你需要做的:
function f() {
const b = "barb";
return function() {
return b;
}
}
const x = f();
console.log(x());
当你调用x
它时返回b
。然而,这并不能成为b
全球性的。要b
全球化,您需要这样做:
function f() {
const b = "barb";
return function() {
return b;
}
}
const x = f();
const b = x();
console.log(b);
希望这有助于您了解范围和功能。