Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
121 views
in Technique[技术] by (71.8m points)

Why default arguments are immutable inside of a function in javascript?

Why , in javascript, the default arguments cannot be changed inside of a function and the non-default ones can?

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

vs

function func(a = 55) {
  arguments[0] = 99; // updating arguments[0] does not also update a
  console.log(a);
}
func(10); // prints 10
question from:https://stackoverflow.com/questions/65941242/why-default-arguments-are-immutable-inside-of-a-function-in-javascript

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Looking at MDN Web Docs:

Non-strict functions that are passed only simple parameters (that is, not rest, default, or restructured parameters) will sync the value of variables new values in the body of the function with the arguments object, and vice versa:

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

and also

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

Conversely, non-strict functions that are passed rest, default, or destructured parameters will not sync new values assigned to argument variables in the function body with the arguments object. Instead, the arguments object in non-strict functions with complex parameters will always reflect the values passed to the function when the function was called (this is the same behavior as exhibited by all strict-mode functions, regardless of the type of variables they are passed):

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

and

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

and also

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

This is probably due to some issue with the sync process syntactic sugar in the javascript compiler.

UPDATE:

It turns out that deno is doing something different under the hood and printing in both cases 10 which is kinda strange so I looked into the assembly generated here:

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'

The first command prints this code, which I tried to understand, putting comments where I think it is doing something relevant.

[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)

and this is the output of the second program that I processed:

[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)

As you can see, the second code looks like the first one, with the addition of an if statement to check for the optional argument.

In the end, the v8 google engine within deno prints 10 twice. That is weird but I assume there is a reason why this is happening. It might be that someone made this decision in their implementation and not everyone reflected the change. However, please don't roast me, I tried my best to understand the bytecode without a manual, so if it's wrong, just tell me and I'll fix it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...