V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
Kasumi20
V2EX  ›  JavaScript

f 等于几,为什么?

  •  
  •   Kasumi20 · 2021-05-31 14:37:02 +08:00 · 2813 次点击
    这是一个创建于 1307 天前的主题,其中的信息可能已经有所发展或是发生改变。
    (() => {
    	{
    		function f() { 'A' }
    		f = 1;
    		f = 2;
    		function f() { 'B' }
    		f = 3;
    	}
    	console.log('f =', f);
    })();
    
    8 条回复    2021-07-07 17:35:58 +08:00
    Jooooooooo
        1
    Jooooooooo  
       2021-05-31 14:59:04 +08:00
    [] - []; // ?
    {} - {}; // ?
    [] - {}; // ?
    {} - []; // ?


    找出会返回 -0 的那一项.
    AoEiuV020
        2
    AoEiuV020  
       2021-05-31 15:02:29 +08:00
    f=2,因为我在 console 跑过了,
    Jirajine
        3
    Jirajine  
       2021-05-31 15:06:21 +08:00
    我感觉应该是 undefined 才对,是因为没开 strict mode 么。
    thunderw
        4
    thunderw  
       2021-05-31 15:46:15 +08:00   ❤️ 2
    dfkjgklfdjg
        5
    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
        6
    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
        7
    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
        8
    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
    严格模式下直接报错
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2764 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 14:32 · PVG 22:32 · LAX 06:32 · JFK 09:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.