1.函数会选择最新的内容吗?
函数 sayHi 使用外部变量。当函数运行时,将使用哪个值?
let name = "John";
function sayHi() {
alert("Hi, " + name);
}
name = "Pete";
sayHi(); // 会显示什么:"John" 还是 "Pete"?
这种情况在浏览器和服务器端开发中都很常见。一个函数可能被计划在创建之后一段时间后才执行,例如在用户行为或网络请求之后。
因此,问题是:它会接收最新的修改吗?
2.哪些变量可用呢?
下面的 makeWorker
函数创建了另一个函数并返回该函数。可以在其他地方调用这个新函数。
它是否可以从它被创建的位置或调用位置(或两者)访问外部变量?
function makeWorker() {
let name = "Pete";
return function() {
alert(name);
};
}
let name = "John";
// create a function
let work = makeWorker();
// call it
work(); // 会显示什么?
会显示哪个值?“Pete” 还是 “John”?
3.Counter 是独立的吗?
在这儿我们用相同的 makeCounter
函数创建了两个计数器(counters):counter
和 counter2
。
它们是独立的吗?第二个 counter 会显示什么?0,1
或 2,3
还是其他?
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
let counter2 = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
alert( counter2() ); // ?
alert( counter2() ); // ?
4.Counter 对象
这里通过构造函数创建了一个 counter 对象。
它能正常工作吗?它会显示什么呢?
function Counter() {
let count = 0;
this.up = function() {
return ++count;
};
this.down = function() {
return --count;
};
}
let counter = new Counter();
alert( counter.up() ); // ?
alert( counter.up() ); // ?
alert( counter.down() ); // ?
5.if 内的函数
看看下面这个代码。最后一行代码的执行结果是什么?
let phrase = "Hello";
if (true) {
let user = "John";
function sayHi() {
alert(`${phrase}, ${user}`);
}
}
sayHi();
6.闭包 sum
编写一个像 sum(a)(b) = a+b
这样工作的 sum
函数。
是的,就是这种通过双括号的方式(并不是错误)。
举个例子:
sum(1)(2) = 3
sum(5)(-1) = 4
7.变量可见吗?
下面这段代码的结果会是什么?
let x = 1;
function func() {
console.log(x); // ?
let x = 2;
}
func();
P.S. 这个任务有一个陷阱。解决方案并不明显。
答案及解析
1.答案:Pete。
函数将从内到外依次在对应的词法环境中寻找目标变量,它使用最新的值。
旧变量值不会保存在任何地方。当一个函数想要一个变量时,它会从自己的词法环境或外部词法环境中获取当前值。
2.答案:Pete.
下方代码中的函数 work()
在其被创建的位置通过外部词法环境引用获取 name
:
所以这里的结果是
"Pete"
。
但如果在 makeWorker()
中没有 let name
,那么将继续向外搜索并最终找到全局变量,正如我们可以从上图中看到的那样。在这种情况下,结果将是 "John"
。
3.答案:0,1。
函数 counter
和 counter2
是通过 makeCounter
的不同调用创建的。
因此,它们具有独立的外部词法环境,每一个都有自己的 count
。
4.当然行得通。
这两个嵌套函数都是在同一个词法环境中创建的,所以它们可以共享对同一个 count 变量的访问:
function Counter() {
let count = 0;
this.up = function() {
return ++count;
};
this.down = function() {
return --count;
};
}
let counter = new Counter();
alert( counter.up() ); // 1
alert( counter.up() ); // 2
alert( counter.down() ); // 1
5.答案:error。
函数 sayHi
是在 if
内声明的,所以它只存在于 if
中。外部是没有 sayHi
的。
6.了使第二个括号有效,第一个(括号)必须返回一个函数。
就像这样:
function sum(a) {
return function(b) {
return a + b; // 从外部词法环境获得 "a"
};
}
alert( sum(1)(2) ); // 3
alert( sum(5)(-1) ); // 4
7.答案:error。
你运行一下试试:
let x = 1;
function func() {
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 2;
}
func();
在这个例子中,我们可以观察到“不存在”的变量和“未初始化”的变量之间的特殊差异。
你可能已经在 变量作用域,闭包中了解到了,从程序执行进入代码块(或函数)的那一刻起,变量就开始进入“未初始化”状态。它一直保持未初始化状态,直至程序执行到相应的 let
语句。
换句话说,一个变量从技术的角度来讲是存在的,但是在 let
之前还不能使用。
下面的这段代码证实了这一点。
function func() {
// 引擎从函数开始就知道局部变量 x,
// 但是变量 x 一直处于“未初始化”(无法使用)的状态,直到结束 let(“死区”)
// 因此答案是 error
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 2;
}
变量暂时无法使用的区域(从代码块的开始到 let
)有时被称为“死区”。