JS每日练习

浏览器控制台上会打印什么

1
2
3
4
5
6
var a = 10;
function foo() {
console.log(a);
var a = 20;
}
foo();

答案是:undefined
原因:在执行函数前,js 引擎会先扫描代码块,变量声明提升到代码的最前面.
这样的话,实际打印的 a 就是 undefined.

1
2
3
4
5
function foo() {
var a;
console.log(a);
a = 20;
}

如果我们使用 let 或 const 代替 var,输出是否相同

1
2
3
4
5
function foo() {
console.log(a);
let a = 20;
}
foo();

答案是会抛出异常: Uncaught ReferenceError: Cannot access ‘a’ before initialization
这是因为 let 和 const 可以让变量在其作用域上受限于它使用的代码块,语句或表达式。这时的 a 会放在 TDZ 区域。只有执行到声明才能访问到它。

“newArray”中有哪些元素?

1
2
3
4
5
6
var array = [];
for (var i = 0; i < 3; i++) {
array.push(() => i);
}
var newArray = array.map(e => e());
console.log(newArray);

答案是[3,3,3], 首先数组中保存了 3 个函数对象,在做 map 时,分别执行每一个函数,函数执行后返回 i,i 此时是全局变量 3,所以在数组中一共是 3 个 3.

如果我们在浏览器控制台中运行 foo 函数,是否会导致堆栈溢出错误

1
2
3
function foo() {
setTimeout(foo, 0);
}

答案是不会,调用 foo 会将 foo 函数推入堆栈,在处理内部代码时,js 引擎遇到 setTimeout,将 foo 函数传递给 webapi,并从函数返回,调用栈为空,计数器被设置为 0,foo 被发送到任务队列,由于此时调用栈为空,事件循环将 foo 推入调用栈进行处理,进程再次重复,堆栈不会溢出。

如果控制台中运行以下函数,页面是否会响应

1
2
3
function foo() {
return Promise.resolve().then(foo);
}

答案是不会响应,微任务队列总是在执行后返回到事件循环前清空,只有在微任务队列为空时,事件循环才会重新渲染。

我们能否以某种方式为下面的语句使用展开运算符而不导致类型错误

1
2
var obj = { x: 1, y: 2, z: 3 };
[...obj];

答案是通过为 obj 添加[Symbol.iterator]函数

1
2
3
4
5
6
7
8
9
10
11
12
13
obj[Symbol.iterator] = function() {
return {
next: function() {
if (this._countDown === 3) {
const last = this._countDown;
return { value: last, done: true };
}
this._countDown++;
return { value: this._countDown, done: false };
},
_countDown: 0
};
};

运行以下代码片段会打印什么

1
2
3
4
5
6
var obj = { a: 1, b: 2 };
Object.setPrototypeOf(obj, { c: 3 });
Object.defineProperty(obj, d, { value: 4, enumerable: false });
for (let prop in obj) {
console.log(prop);
}

答案是 a b c

xGetter()会打印什么

1
2
3
4
5
6
7
8
9
10
11
var x = 10;
var foo = {
x = 90;
getX: function() {
return this.x;
}
}

foo.getX();
var xGetter = foo.getX;
xGetter();

会打印 10, 这里的 this 指向的是全局变量 10