一、作用域链变量查找
题目1
var a = 10;
function test() {
var a = 20;
console.log(a);
}
test();
console.log(a);
题目2
var a = 10;
function test() {
console.log(a);
var a = 20;
}
test();
题目3
var a = 10;
function test() {
a = 20;
}
test();
console.log(a);
题目4
function outer() {
var a = 10;
function inner() {
console.log(a);
}
return inner;
}
var innerFunc = outer();
innerFunc();
二、 闭包程序输出判断
题目5
function outer() {
var a = 1;
function inner() {
console.log(a);
}
a++;
return inner;
}
var innerFunc = outer();
innerFunc();
题目6
var a = 1;
function outer() {
var a = 2;
function inner() {
console.log(a);
}
return inner;
}
var innerFunc = outer();
innerFunc();
题目7
function outer() {
var a = 1;
function inner() {
console.log(a);
a++;
}
return inner;
}
var innerFunc = outer();
innerFunc();
innerFunc();
题目8
function createCounter() {
var count = 0;
return function() {
return ++count;
};
}
var counter = createCounter();
console.log(counter());
console.log(counter());
题目9
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
题目10
for (var i = 0; i < 3; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}
题目11
function outer() {
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function() {
console.log(i);
};
}
return funcs;
}
var funcs = outer();
funcs[0]();
funcs[1]();
funcs[2]();
题目12
function outer() {
var funcs = [];
for (var i = 0; i < 3; i++) {
(function(i) {
funcs[i] = function() {
console.log(i);
};
})(i);
}
return funcs;
}
var funcs = outer();
funcs[0]();
funcs[1]();
funcs[2]();
题目13
function outer() {
var a = 1;
return {
inner: function() {
console.log(a);
},
increase: function() {
a++;
}
};
}
var obj = outer();
obj.inner();
obj.increase();
obj.inner();
题目14
function outer() {
var a = 1;
return function() {
a++;
console.log(a);
};
}
var inner1 = outer();
var inner2 = outer();
inner1();
inner1();
inner2();
答案
题目1
输出 20
解析:
JavaScript 使用了词法作用域(也称为静态作用域),在函数
test
内部,变量a
是局部变量,它与全局作用域中的变量a
是不同的。当你在函数
test
内部调用console.log(a)
时,JavaScript 解释器会首先在当前的作用域(也就是test
函数的作用域)中查找变量a
。在这个作用域中,有一个局部变量a
,所以解释器就使用了这个局部变量,而不是全局的变量a
。所以,
console.log(a)
输出的是局部变量a
的值,也就是 20。
题目2
输出 undefined
解析:
在 JavaScript 中,变量和函数声明会被提升到它们所在的作用域的顶部。但是,只有声明会被提升,初始化(也就是赋值操作)不会被提升。
在代码中,函数
test
的作用域内有一个变量a
的声明和初始化。由于变量提升,这个声明会被提升到test
函数作用域的顶部,但是初始化(a = 20
)仍然在原地。所以,当代码执行到
console.log(a)
时,变量a
已经被声明了,但还没有被初始化,所以它的值是undefined
。
题目3
输出 20
解析:
在
test
函数内部,变量a
没有通过var
关键字声明,所以它引用的是全局作用域中的变量a
。当
test
函数内部执行a = 20
时,实际上是在修改全局变量a
的值。所以,当test
函数执行后调用console.log(a)
,输出的是全局变量a
的新值,也就是 20。
题目4
输出 10
解析:
相关知识点:JavaScript 的闭包(Closure)机制。
在 JavaScript 中,当一个函数内部定义了另一个函数,内部的函数可以访问外部函数的变量,即使外部函数已经执行完毕。这种能够访问外部函数变量的内部函数,我们称之为闭包。
在代码中,
inner
函数就是一个闭包,它可以访问outer
函数的变量a
。当outer
函数执行完毕后,虽然它的局部变量a
在其他地方是不可访问的,但是inner
函数仍然可以访问到它。所以,当执行
innerFunc()
(也就是inner
函数)时,console.log(a)
输出的是outer
函数的变量a
的值,也就是 10。这是 JavaScript 作用域链和闭包机制的一个例子,它允许函数记住并访问它的词法作用域,即使函数在其词法作用域之外执行。
题目5
输出2
解析:
在代码中,
outer
函数创建了一个局部变量a
和一个内部函数inner
。inner
是一个闭包,它可以访问outer
函数的作用域,所以它可以访问变量a
。当
outer
函数执行时,它首先将a
设置为 1,然后inner
函数被创建,然后a
的值增加 1,最后outer
函数返回inner
函数。所以,当
innerFunc
(也就是inner
函数)被调用时,它打印的是a
的当前值,也就是 2。
题目6
输出2
解析:
outer
函数创建了一个局部变量a
和一个内部函数inner
。inner
是一个闭包,它可以访问outer
函数的作用域,所以它可以访问变量a
。当
outer
函数执行时,它首先将a
设置为 2,然后inner
函数被创建,最后outer
函数返回inner
函数。所以,当
innerFunc
(也就是inner
函数)被调用时,它打印的是a
的当前值,也就是 2,而不是全局变量a
的值。
题目7
输出 1和2
解析:
outer
函数创建了一个局部变量a
和一个内部函数inner
。inner
是一个闭包,它可以访问outer
函数的作用域,所以它可以访问和修改变量a
。当
outer
函数执行时,它首先将a
设置为 1,然后inner
函数被创建,最后outer
函数返回inner
函数。然后,题目调用了
innerFunc
(也就是inner
函数)两次。每次调用innerFunc
时,它都会打印a
的当前值,然后将a
的值增加 1。所以,第一次调用
innerFunc
时,它打印的是a
的初始值,也就是 1。然后,a
的值增加 1,变为 2。第二次调用
innerFunc
时,它打印的是a
的当前值,也就是 2。
题目8
输出 :
1
2
解析:
createCounter
函数创建了一个局部变量count
和一个匿名函数。这个匿名函数是一个闭包,它可以访问createCounter
函数的作用域,所以它可以访问和修改变量count
。当
createCounter
函数执行时,它首先将count
设置为 0,然后创建了一个匿名函数,最后返回这个匿名函数。然后,题目将
createCounter
函数的返回值赋值给了counter
,所以counter
现在是一个函数,这个函数可以访问和修改count
变量。题目调用了
counter
两次。每次调用counter
时,它都会将count
的值增加 1,然后返回count
的新值。所以,第一次调用
counter
时,它返回的是count
的初始值加 1,也就是 1。第二次调用counter
时,它返回的是count
的当前值加 1,也就是 2。
题目9
输出
3
3
3
解析:
在代码中,
setTimeout
函数创建了一个定时器,这个定时器在 1000 毫秒后执行一个函数。这个函数是一个闭包,它可以访问包含它的函数的作用域,所以它可以访问变量i
。然而,当
setTimeout
函数被调用时,它不会立即执行定时器的回调函数,而是将这个回调函数放入事件队列中,等待当前的代码执行完毕后再执行。所以,当
for
循环执行完毕时,变量i
的值已经是 3,然后所有的定时器的回调函数开始执行。由于这些回调函数都是闭包,它们访问的是同一个i
变量,所以它们打印的都是i
的当前值,也就是 3。
题目10
输出:
0
1
2
解析:
在代码中,
setTimeout
函数创建了一个定时器,这个定时器在 1000 毫秒后执行一个函数。这个函数是一个闭包,它可以访问包含它的函数的作用域,所以它可以访问变量i
。然而,这里的关键是
(function(i) {...})(i);
这部分代码。这是一个立即执行函数表达式(IIFE),它创建了一个新的作用域,这个作用域有它自己的i
变量,这个i
的值是循环的当前迭代值。所以,当
setTimeout
的回调函数执行时,它打印的是创建定时器时的i
值,而不是循环结束时的i
值。
题目11
输出
3
3
3
解析:
在代码中,
outer
函数创建了一个数组funcs
和一个for
循环。在for
循环中,每次迭代都创建了一个新的函数,并将这个函数赋值给funcs
数组的一个元素。这个新的函数是一个闭包,它可以访问outer
函数的作用域,所以它可以访问变量i
。然而,由于 JavaScript 的变量提升机制,
for
循环中的var i
实际上是在outer
函数的整个作用域中声明的。所以,当for
循环执行完毕后,变量i
的值是 3,然后所有的函数都是闭包,它们访问的是同一个i
变量,所以它们打印的都是i
的当前值,也就是 3。
题目12
这段代码的输出是:
0
1
2
这是因为 JavaScript 的闭包和立即执行函数表达式(IIFE)机制。
在代码中,
outer
函数创建了一个数组funcs
和一个for
循环。在for
循环中,每次迭代都创建了一个新的立即执行函数表达式(IIFE)。这个 IIFE 创建了一个新的作用域,这个作用域有它自己的i
变量,这个i
的值是循环的当前迭代值。然后,这个 IIFE 创建了一个新的函数,并将这个函数赋值给
funcs
数组的一个元素。这个新的函数是一个闭包,它可以访问包含它的函数的作用域,所以它可以访问i
变量。所以,当
funcs[0]()
、funcs[1]()
和funcs[2]()
被调用时,它们打印的是创建函数时的i
值,也就是 0、1 和 2。
题目13
这段代码的输出是:
0
1
2
在代码中,
outer
函数创建了一个局部变量a
和一个对象。这个对象有两个方法:inner
和increase
。这两个方法都是闭包,它们可以访问outer
函数的作用域,所以它们可以访问和修改变量a
。当
outer
函数执行时,它首先将a
设置为 1,然后创建了一个对象,最后返回这个对象。然后,
outer
函数的返回值赋值给了obj
,所以obj
现在是一个对象,这个对象的inner
和increase
方法可以访问和修改a
变量。随后,代码中调用了
obj.inner
、obj.increase
和obj.inner
。obj.inner
打印a
的当前值,obj.increase
将a
的值增加 1。所以,第一次调用
obj.inner
时,它打印的是a
的初始值,也就是 1。然后,obj.increase
将a
的值增加 1,变为 2。然后,第二次调用obj.inner
时,它打印的是a
的当前值,也就是 2。
题目14
这段代码的输出是:
2
3
2
在代码中,
outer
函数创建了一个局部变量a
和一个匿名函数。这个匿名函数是一个闭包,它可以访问outer
函数的作用域,所以它可以访问和修改变量a
。当
outer
函数执行时,它首先将a
设置为 1,然后创建了一个匿名函数,最后返回这个匿名函数。然后,
outer
函数的返回值赋值给了inner1
和inner2
,所以inner1
和inner2
现在都是函数,这两个函数可以访问和修改它们各自的a
变量。随后题目调用了
inner1
和inner2
。每次调用这些函数时,它们都会将a
的值增加 1,然后打印a
的新值。所以,第一次和第二次调用
inner1
时,它打印的是a
的值加 1,也就是 2 和 3。然后,调用inner2
时,它打印的是a
的值加 1,也就是 2,因为inner2
有它自己的a
变量。