首页 > 解决方案 > 为什么默认参数在 javascript 中的函数内部是不可变的?

问题描述

为什么,在 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

标签: javascriptargumentsimmutabilitydefault-arguments

解决方案


查看MDN 网络文档

仅传递简单参数的非严格函数(即,不传递参数、默认参数或重组参数)会将函数体中的变量新值与参数对象同步,反之亦然:

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'

第一个命令打印我试图理解的这段代码,并在我认为它正在做相关事情的地方添加注释。

[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 语句来检查可选参数。

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


推荐阅读