【问题标题】:Why default arguments are immutable inside of a function in javascript?为什么默认参数在 javascript 中的函数内部是不可变的?
【发布时间】:2021-01-28 16:27:49
【问题描述】:

为什么在 javascript 中,默认参数不能在函数内部更改而非默认参数可以?

function func(a) {
  a = 99; // updating a also updates arguments[0]
  console.log(arguments[0]);
}
func(10); // prints 99

function func(a = 55) {
  arguments[0] = 99; // updating arguments[0] does not also update a
  console.log(a);
}
func(10); // prints 10

【问题讨论】:

    标签: javascript arguments immutability default-arguments


    【解决方案1】:

    看着MDN Web Docs

    只传递简单参数的非严格函数(即,不是rest、default或restructured参数)会将函数体中的变量新值与arguments对象同步,反之亦然:

    function func(a) {
      arguments[0] = 99; // updating arguments[0] also updates a
      console.log(a);
    }
    func(10); // 99
    

    还有

    function func(a) {
      a = 99; // updating a also updates arguments[0]
      console.log(arguments[0]);
    }
    func(10); // 99
    

    相反,传递rest、默认或解构参数的非严格函数不会将分配给函数体中参数变量的新值与参数对象同步。相反,具有复杂参数的非严格函数中的 arguments 对象将始终反映调用函数时传递给函数的值(这与所有严格模式函数表现出的行为相同,无论它们是什么类型的变量通过):

    function func(a = 55) {
      arguments[0] = 99; // updating arguments[0] does not also update a
      console.log(a);
    }
    func(10); // 10
    

    function func(a = 55) {
      a = 99; // updating a does not also update arguments[0]
      console.log(arguments[0]);
    }
    func(10); // 10
    

    还有

    // An untracked default parameter
    function func(a = 55) {
      console.log(arguments[0]);
    }
    func(); // undefined
    

    这可能是由于 javascript 编译器中的同步过程语法糖存在问题。

    更新:

    事实证明,deno 在后台做了一些不同的事情,并且在两种情况下都打印了10,这有点奇怪,所以我查看了这里生成的程序集:

    deno eval 'function f(a) { a = 99; console.log(arguments[0]); } f(10);' --v8-flags='--print-bytecode,--print-bytecode-filter=f'
    deno eval 'function f(a = 55) { arguments[0] = 99; console.log(a); } f(10);' --v8-flags='--print-bytecode,--print-bytecode-filter=f'
    

    第一个命令打印我试图理解的这段代码,将 cmets 放在我认为它正在做相关事情的地方。

    [generated bytecode for function: f (0x38db0826b679 <SharedFunctionInfo f>)]
    Parameter count 2
    Register count 4
    Frame size 32
        0x38db0826b846 @    0 : 88                CreateUnmappedArguments
        0x38db0826b847 @    1 : 26 fb             Star r0: Store accumulator in register 0 (presumibly function frame pointer)
        0x38db0826b849 @    3 : 0c 63             LdaSmi [99]: Load (SMallInteger) 99 in accumulator
        0x38db0826b84b @    5 : 26 02             Star a0: Store accumulator in function argument 0
        0x38db0826b84d @    7 : 13 00 00          LdaGlobal [0], [0]: I don't know
        0x38db0826b850 @   10 : 26 f9             Star r2: Store accumulator in register 2
        0x38db0826b852 @   12 : 28 f9 01 02       LdaNamedProperty r2, [1], [2]: Load a in the accumulator
        0x38db0826b856 @   16 : 26 fa             Star r1: Store accumulator in register 1
        0x38db0826b858 @   18 : 0b                LdaZero: Load 0 in accumulator
        0x38db0826b859 @   19 : 2a fb 04          LdaKeyedProperty r0, [4]: (presumibly) loading the property with key 4
                                                                            from the function frame with the accumulator
                                                                            (syncing) the argument here.
        0x38db0826b85c @   22 : 26 f8             Star r3: Store accumulator in register 3
        0x38db0826b85e @   24 : 59 fa f9 f8 06    CallProperty1 r1, r2, r3, [6]: console.log(r1)
        0x38db0826b863 @   29 : 0d                LdaUndefined: Load undefined in accumulator
        0x38db0826b864 @   30 : aa                Return: Return the accumulator value
    Constant pool (size = 2)
    Handler Table (size = 0)
    Source Position Table (size = 0)
    

    这是我处理的第二个程序的输出:

    [generated bytecode for function: f (0x3bd80826b679 <SharedFunctionInfo f>)]
    Parameter count 2
    Register count 4
    Frame size 32
        0x3bd80826b846 @    0 : 88                CreateUnmappedArguments
        0x3bd80826b847 @    1 : 26 fa             Star r1: Store accumulator in register 1 (presumibly function frame pointer)
        0x3bd80826b849 @    3 : 25 02             Ldar a0: Load in the accumulator the argument 0
        0x3bd80826b84b @    5 : 9e 06             JumpIfNotUndefined [6] (0x3bd80826b851 @ 11): If accumulator is undefined:
        0x3bd80826b84d @    7 : 0c 37                 LdaSmi [55]: Load (SMallInteger) 55 in accumulator
        0x3bd80826b84f @    9 : 8b 04             Jump [4] (0x3bd80826b853 @ 13): else:
        0x3bd80826b851 @   11 : 25 02                 Ldar a0: Load argument 0 in accumulator
        0x3bd80826b853 @   13 : 26 fb             Star r0: Store accumulator in register 0
        0x3bd80826b855 @   15 : 0b                LdaZero: Load 0 in accumulator
        0x3bd80826b856 @   16 : 26 f8             Star r3: Store accumulator in register 3
        0x3bd80826b858 @   18 : 0c 63             LdaSmi [99]: Load (SMallInteger) 99 in accumulator
        0x3bd80826b85a @   20 : 30 fa f8 00       StaKeyedProperty r1, r3, [0]: (presumibly) loading in the function frame
                                                                                (r1) the value of the accumulator (99) in the
                                                                                property with key r3 (0) (syncing)
        0x3bd80826b85e @   24 : 13 00 02          LdaGlobal [0], [2]: (presumibly) loading the global value containing the
                                                                      pointer to a in the accumulator
        0x3bd80826b861 @   27 : 26 f8             Star r3: Store accumulator in register 3
        0x3bd80826b863 @   29 : 28 f8 01 04       LdaNamedProperty r3, [1], [4]: Load a in the accumulator
        0x3bd80826b867 @   33 : 26 f9             Star r2: Store accumulator in register 2
        0x3bd80826b869 @   35 : 59 f9 f8 fb 06    CallProperty1 r2, r3, r0, [6]: console.log(r2)
        0x3bd80826b86e @   40 : 0d                LdaUndefined: Load undefined in accumulator
        0x3bd80826b86f @   41 : aa                Return: Return the accumulator value
    Constant pool (size = 2)
    Handler Table (size = 0)
    Source Position Table (size = 0)
    

    如您所见,第二个代码看起来像第一个,添加了一个 if 语句来检查可选参数。

    最后,deno 中的v8 google engine 打印两次10。 这很奇怪,但我认为发生这种情况是有原因的。 可能有人在他们的实施中做出了这个决定,并不是每个人都反映了这个变化。但是,请不要烤我,我尽力在没有手册的情况下理解字节码,所以如果有错误,请告诉我,我会修复它。

    【讨论】:

    • 我读了几次文档。您只复制粘贴文档。我假设它与参数类型有关。例如,当默认值设置为constant 而未设置时为let;或者...它可以是 val 尚未实现的对象。感谢您抽出宝贵时间回复。
    • 您好,感谢您的尝试。我不是在烤你。 :)) 现在你想出了一些东西。但是我没有得到汇编代码。我明白你指的是什么,但我无法确认,因为我不明白。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-07
    • 2011-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多