f 等于几,为什么?

2021-05-31 14:37:02 +08:00
 Kasumi20
(() => {
	{
		function f() { 'A' }
		f = 1;
		f = 2;
		function f() { 'B' }
		f = 3;
	}
	console.log('f =', f);
})();
2806 次点击
所在节点    JavaScript
8 条回复
Jooooooooo
2021-05-31 14:59:04 +08:00
[] - []; // ?
{} - {}; // ?
[] - {}; // ?
{} - []; // ?


找出会返回 -0 的那一项.
AoEiuV020
2021-05-31 15:02:29 +08:00
f=2,因为我在 console 跑过了,
Jirajine
2021-05-31 15:06:21 +08:00
我感觉应该是 undefined 才对,是因为没开 strict mode 么。
thunderw
2021-05-31 15:46:15 +08:00
dfkjgklfdjg
2021-06-01 10:56:38 +08:00
我们在先在每次为 f 赋值之后输出以下 f 的值
```js
(() => {
{
function f() { 'A' }
console.log('f_1 =', f); // f_1 = ƒ f() { 'B' }
f = 1;
console.log('f_2 =', f); // f_2 = 1
f = 2;
console.log('f_3 =', f); // f_3 = 2
function f() { 'B' }
console.log('f_4 =', f); // f_4 = 2
f = 3;
console.log('f_5 =', f); // f_5 = 3
}
console.log('f_out =', f); // f_out = 2
})();
```
好了,那么有疑惑的点只会在 f_1 = ƒ f() { 'B' } 、f_4 = 2 和 f_out = 2 上对不对,其它的都是符合直觉的。
首先来看为什么 f_1 = ƒ f() { 'B' } 与 f_4 = 2
其实正确的书写方式应该是 1.声明函数; 2.声明变量; 3.业务逻辑;
所以简单改正之后其实类似这样
```js
(() => {
var f
{
f = function () { 'A' }
f = function () { 'B' }
console.log('f_1 =', f); // f_1 = ƒ f() { 'B' }
f = 1;
console.log('f_2 =', f); // f_2 = 1
f = 2;
console.log('f_3 =', f); // f_3 = 2
console.log('f_4 =', f); // f_4 = 2
f = 3;
console.log('f_5 =', f); // f_5 = 3
}
console.log('f_out =', f); // f_out = 2
})();
```
那么就剩下一个疑惑了,就是为什么 最后的 f_out = 2
明明之前已经输出了 f_5 = 3,这个我就不知道原理了,只能从 block scope 和 local scope 和你说
我把外部 local scope 中的 f 继续声明成 f , 而内部 block scope 中的 f 的声明成 _f:
```js
(() => {
var f
{
var _f
f = _f = function () { 'A' }
f = _f = function () { 'B' }
console.log('f_1 =', _f); // f_1 = ƒ f() { 'B' }
_f = 1;
console.log('f_2 =', _f); // f_2 = 1
_f = 2;
console.log('f_3 =', _f); // f_3 = 2
f = _f
console.log('f_4 =', _f); // f_4 = 2
_f = 3;
console.log('f_5 =', _f); // f_5 = 3
}
console.log('f_out =', f); // f_out = 2
})();
```
具体为什么可能需要大佬来解释了。
libook
2021-06-01 14:44:16 +08:00
可以逐行调试,然后看每一行执行完后,f 在不同作用域下的值是什么:

// 此时没有 f
(() => {
f = -1;
// 函数块内:-1
{
f = 1;
// 块内:1 函数块内:-1
function f() { 'A' }
// 块内:f() 函数块内:f()
f = 1;
// 块内:1 函数块内:f()
f = 2;
// 块内:2 函数块内:f()
function f() { 'B' }
// 块内:2 函数块内:2
f = 3;
// 块内:3 函数块内:2
}
console.log('f =', f); // 输出函数块内的 f 值,即 2
})();

可以看出来,把操作分成两类,一类是赋值,另一类是函数声明。
赋值的逻辑很简单,就是直接影响当前块内作用域的 f 值,执行到哪就赋值成啥。
比较反常识的是函数声明,可以看到函数声明的时候其实只把当前块内作用域的 f 复制一份放到函数块内,第一次和第二次函数声明都是这样的,如果当前块内作用域块内没有 f,那么就会在当前块内作用域声明一个 f 变量,再把函数赋值上去,紧接着再把块内的 f 复制一份到函数块内作用域。

以上是我根据行为做出的推断,实际上还是得看 ES 标准以及引擎的实现方案。

不过这个说白了还是老生常谈的 ES5 作用域提升的特性,在严格模式+块级作用域下是不会有这种现象的,而且如今的商业开发也极少会考虑保留作用域提升特性,拥抱 ES6+或使用 TS 都在团队开发效率方面有很高的回报。
libook
2021-06-01 14:46:22 +08:00
上面代码编辑有问题,更新一个正确的版本:

// 此时没有 f
(() => {
f = -1;
// 函数块内:-1
{
f = 1;
// 块内:1 函数块内:-1
function f() { 'A' }
// 块内:1 函数块内:1
f = 1;
// 块内:1 函数块内:f()
f = 2;
// 块内:2 函数块内:f()
function f() { 'B' }
// 块内:2 函数块内:2
f = 3;
// 块内:3 函数块内:2
}
console.log('f =', f); // 输出函数块内的 f 值,即 2
})();

第一次函数声明的时候就是做了值的复制,所以内外都是 1
mxT52CRuqR6o5
2021-07-07 17:35:58 +08:00
win 10 ie11 的结果为 f = function f () { 'B' }
win 10 ie11 用 ie10 兼容模式跑结果为 f = 3
使用 browserstack 测试 chrome 30/firefox 30/safari 9.1 跑结果为 f = 3
chrome/firefox/safari 最新版测试,结果为 f = 2
严格模式下直接报错

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/780333

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX