1. 请听题
(function () { var a = b = 5 })()
console.log(a);
console.log(b);
答案
Uncaught ReferenceError: a is not defined
解析
本题考察变量提升。
在闭包中,用var关键字声明了局部变量a,而b没有声明,在非严格模式下,默认是全局变量。执行console.log(a)时,闭包函数已经执行完毕,局部变量a已销毁,所以console.log(a)直接报错,后面的console.log(b)不再执行。所以答案是a is not defined。
变形一
刚才提到了这个是在非严格模式下,如果是在严格模式下呢?
'use strict'; (function () { var a = b = 5 })()
console.log(a);
console.log(b);
执行一下结果是:
Uncaught ReferenceError: b is not defined
2. 请听题
var a = 1;
function b (a) {
a = 2;
console.log(a);
}
b(a);
console.log(a);
答案
2,1
解析
本题考察局部变量和传参。
红宝书里写到:** ECMAScript中所有参数传递都是值,不可能通过引用传递参数。**
参数分为基本类型和引用类型,此题是基本类型的参数传递,把全局变量a传给方法b,这时是全局参数a把值复制给了方法b的arguments里的a。这样方法b里的a是方法b的局部变量。它是不影响全局变量a的,所以输出是2, 1。
变形一
前面提到了参数还有个引用类型,那如果传引用类型,结果又是什么样子呢?
var a = {}
function b (a) {
a.name = 'fish'
console.log(a)
}
b(a)
console.log(a)
执行一下结果:
{name: "fish"}
{name: "fish"}
这么一看,像是引用传递呀,里面的对象添加了属性name,并且影响到了外面的对象。那我们再来看下面这段代码:
var a = {}
function b (a) {
a.name = "fish"
a = new Object()
a.name = 'pig'
console.log(a)
}
b(a)
console.log(a)
执行结果为:
{ name: "pig" }
{ name: "fish" }
如果是引用传递的话,两次结果应该都是{ name: "pig" }的呀?
其实,引用类型也是值传递,只是这个值指的是引用对象的地址,这个地址指向存放在堆里的对象。在把参数传进来时,是把这个参数对象的地址,复制了一份给参数a, 这样它们都是指向同一个堆中的对象。后来,新创建一个对象,赋值给了参数a,也就是把新创建的对象的地址给了a,这样就把之前的地址覆盖了,参数a指向了新的对象,就不再影响全局对象a了。
3. 请听题
var a = new Boolean(false)
if (a) {
console.log(1)
}
var b = Boolean(0)
if (b) {
console.log(2)
}
答案
1
解析
这里的a其实是Boolean对象,** 任何对象转为布尔值,都是true。** 在js中,只有0, -0,NaN, "", null, undefined这6个转为Boolean时为false。
而第二段代码,注意是Boolean不是new Boolean,是显式类型转换,所以结果为false。
4. 请听题
const a = 12;
a = 13;
console.log(a);
const g = { b:3 };
console.log(g.b);
g.b = 12;
console.log(g.b);
let [head, ...tail] = [1,2,3,4];
console.log(tail);
答案
Uncaught TypeError: Assignment to constant variable.
3, 12
[2,3,4]
解析
const 声明的变量不可以北重写,但定义对象的属性可以被修改。
解构赋值
5. 请听题
var k = 0;
for (var i = 0, j = 0; i < 10, j < 6; i++,j++) {
k += i + j;
}
console.log(k);
答案
30
解析
for循环第二项的布尔值决定了循环是否继续。如果i < 10和i < 6互换,结果就不一样了。
6. 请听题
var x = 0;
switch (++x) {
case 0: console.log('a'); ++x;
case 1: console.log('b'); ++x;
case 2: console.log('c'); ++x;
}
答案
b
c
3
解析
这里没有break,所以执行到最后。
i++ 返回原来的值,++i返回加1后的值。i++不能作为左值,而++i可以。
变形一
var x = 0;
switch (x++) {
case 0: console.log('a'); ++x;
case 1: console.log('b'); ++x;
case 2: console.log('c'); ++x;
}
执行结果:
a
b
c
4
变形二
var x = 0;
switch (x++) {
case 0: console.log('a'); x++;
case 1: console.log('b'); x++;
case 2: console.log('c'); x++;
}
执行结果:
a
b
c
3
7. 请听题
function Foo () {
var i = 0;
return function () {
document.write(i++);
}
}
var f1 = Foo();
f2 = Foo();
f1();
f1();
f2();
8. 请听题
var a = 10
(function a () {
a = 20
console.log(a)
})()
答案
解析
报错位置时在执行方法a,报错内容是10 is not a function。也就是说此时的a已经被复制为10了。
我们分析下执行顺序:
执行代码之前,会先解析声明和变量,首先声明一个名为a的function,这是因为存在function a,所以不会再声明一个a的变量。
开始执行时,10复制给a,其实就是10赋值给function a,这样a就是10,不再是方法,所以在执行时报错。
变形一
var a
(function a () {
a = 20
console.log(a)
})()
答案
ƒ a () {
a = 20
console.log(a)
}
思考:为何a = 20 没生效呢?