4 questions to investigate JS stack memory

Time:2022-1-7

1. Basic stack memory assessment

What is the output of the following question?

let a = {
  n : 1
}
let b = a
a.x = a = {
  n: 2
}
console.log(a.x)  // undefined
console.log(b)  // { n: 1, x: { n: 2 } }

Perform process analysis:

  1. a = {n: 1}First, create an address in the heap memory (for example:AAAFFF00), the content isn: 1, and then in the global execution contextEC(G)Global variable environment inVO(G), create a global variablea, point to heap memory address:AAAFFF00

    4 questions to investigate JS stack memory

  2. b = a, similarly, in the global variable environmentVO(G)Create a global variableb, point to heap memory address:AAAFFF00

    4 questions to investigate JS stack memory

  3. a.x = a = { n: 2 }, the priority of this is actually equal toa.x = {n: 1}a = {n: 1}

  4. a.x = {n: 1}, create another address in heap memory (for example:AAAFFF11), the content isn: 1, inaMemory address ofAAFF00Create one inxVariables, pointingAAAFFF11At this point,b = a = {n:1, x: {n: 1}}

    4 questions to investigate JS stack memory

  5. a = {n: 1}, willaThe direction of is determined byAAAFFF00Change toAAAFFF11

    4 questions to investigate JS stack memory

So the final result is:
b = {n:1, x: {n: 1}}
a = {n: 1}

2. Abnormal variable promotion

Variable promotion: before the current context is executed, thevar/functionDeclare or define an elevation, withvarDeclaration only, withfunctionDeclaration + definition of

But if you encounter{}In block level scope, the new and old versions of browsers behave differently (Es3 compatible and ES6 compatible)

  • IE browser < = ie10 (old version)
    No matter{}, as alwaysfunctionDeclaration + definition, and there will be no block level scope
  • New browser version (here{} is {} in the division object
    {}Mediumfunction, only declare and no longer define in the global context;
    {}Appear infunction/let/const, a block level scope is created

Let’s take a question to illustrate:

var a = 0
if(true) {
  a = 1
  function a() {}
  a = 21
  console.log(a)  // 21
}
console.log(a) // 1

If it’s inBelow ie10Implementation process of the:

  1. Variable promotion function a() {}
  2. Global variable promotion var a
  3. Start execution, global a = 0
  4. Global a = 1
  5. Global a = 21
  6. Print global A: 21
  7. Print global A: 21

If the browser is in the new version, that is, the execution process of forward compatibility with Es3 and backward compatibility with ES6:

  1. Because the variable increases, sovar a,function aAll in the global execution contextEC(G)Global variable environment inVO(G)Create a global variable ina

  2. Code executiona = 0, global variable environmentVO(G)ina = 0

    4 questions to investigate JS stack memory

  3. encounter{}Block level scope to generate a block level execution contextEC(block), a private variable object is generatedAO(block)

  4. In this block level scope, because there arefunction aTherefore, variable promotion occurs at the block level, declaration + definition, andheapGenerate a function in heap memoryAO(block)Create a variableaPoint to function heap memory,After that, I met in this levelaIt’s all private

    4 questions to investigate JS stack memory

    image.png
  5. Execution in block levela = 1AO(block)ina = 1

    4 questions to investigate JS stack memory

    image.png
  6. encounterfunction a() {}Because this function, for forward compatibilityES3Therefore, it has been promoted once in the overall situation; For backward compatibilityES6, it is promoted again in the block level scope, soIn order to be compatible, when the browser really encounters a function in the block level, it will do one thing: before encountering the code, it will map all the operations on a before the code to the global one, but the later ones will not be processed. It will think that the later ones are private, i.e. beforea = 1Mapped to globalA = 1 in VO (g)

    4 questions to investigate JS stack memory

    image.png
  7. aftera = 21, is already private, so it only affects the block levelA = 21 in Ao (block)

    4 questions to investigate JS stack memory

    image.png
  8. In block levelconsole.log(a) => AO(block) a => 21

  9. Globalconsole.log(a) => VO(G) a => 1

Extended practice

In order to deepen this abnormal rule, let’s do a few more questions:

Abnormal lifting exercise 1

{
  function foo() {}
  foo = 1
}

console.log(foo);
  • Step 1:{}Mediumfunction fooIn variable promotion, only declared but not defined in the global
EC(G):  
   AO(G) function foo
  • Step 2, in{}Block levelfunctionTherefore, a block level scope will be generatedEC(Block)fooIn this block level scope, variable promotion: declaration + definition
EC(block):   
   AO(block): funciont foo() {}
  • Step 3, start execution (after the execution process,Ao variable object =>VO active object)BecausefooBoth global and private are declared for compatibilityES3andES6, on execution tofunction foo() {}In, the previous operations are mapped to the global, that isAO(block): funciont foo() {}Declared + defined process
EC(G):  
   VO(G) function foo() {}
  • Step 4: execute foo = 1. Since there is a foo private variable in the block level scope, it is assigned in EC (block)
EC(block):
      VO(G) foo => 1

Abnormal lifting exercise 2

Now I’m going to be lazy. Hahaha, please understand the following annotation steps in combination with the context

//Step 1, EC (g), Ao (g): function foo
//Step 2, EC (block), Ao (block): foo = > function foo() {}
//Step 3, start execution, EC (g), VO (g): foo = > function foo() {}
//Step 4, foo = 1, EC (block), VO (block): foo = > 1
//Step 5, EC (g), VO (g): foo = > 1
{
  function foo() {}
  foo = 1
  function foo() {} // 1
}

console.log(foo); // 1

Abnormal lifting exercise 2

//Step 1, EC (g), Ao (g): function foo
//Step 2, EC (block), Ao (block): foo = > function foo() {}
//Step 3: start to execute function foo() {}, EC (g), VO (g): foo = > function foo() {}
//Step 4, foo = 1, EC (block), VO (block): foo = > 1
//Step 5, function foo() {} = > EC (g), VO (g): foo = > 1
//Step 6, foo = 2, EC (block), VO (block): foo = > 2

{
  function foo() {}
  foo = 1
  function foo() {}
  foo = 2
  console.log(foo); // 2
}
console.log(foo); // 1

3. Stack memory with formal parameters

What is the output of the following function?

var x = 1
function func(x, y = function func2() { x = 2 }) {
  x = 3;
  y()
  console.log(x)  // 2
}
func(5)
console.log(x)  // 1

The following diagram is used to analyze the following process:

  1. Global executionx = 1, inEC(G)Create one inVO(G), create a value of 1 and create another objectx, X points to the value 1

    4 questions to investigate JS stack memory

    image.png
  2. Find the function declaration, create a heap memory for it, define its function, and declare its scope in this memory, that is, the global scopeVC(G), formal parameterx、y, and its function body string, and create one in the global variablefunc, point to this heap memory

    4 questions to investigate JS stack memory

    image.png
  3. implementfunc(5), executing a function creates a private execution context for itEC(func), in which a private variable environment is createdAO(func)

  4. stayEC(func)Initial scope chain: its own scopeEC(func)And its parent scopeEC(G)(global scope)

  5. Formal parameter assignment: passedx = 5yNo parameters are passed, so its default value is usedFunction func2, encounter a function and request heap memory for itAAAFFF111Similarly, analyze its scope and formal parameters, save the function body to memory, andY points to aaaffff111

    4 questions to investigate JS stack memory

    image.png
  6. After analyzing the relationship, start executionfunc5Function body

  7. x = 3, in its own scopeEC(func)Find X in and find privatex, soxPoint 3

  8. y(), in its own scopeEC(func)Find iny, foundypointfunc2, execute functionfunc2And create a separate execution contextEC(func2), analyze the scope chain for it and its own scopeEC(func2)And its parent scopeEC(func), because it has no formal parameter assignment, the relevant private variable is not created

    4 questions to investigate JS stack memory

    image.png
  9. Start function executionfunc2The body of the function,x = 2So in its own scopeEC(func2)The variable was not found inx, to its parent scopeEC(func)Found inx, soEC(func)inxpoint2

    4 questions to investigate JS stack memory

    image.png
  10. func2After execution, continue to executeEC(func)Mediumconsole.log(x), output its ownx:2

  11. funcAfter execution, continue to executeEC(G)Mediumconsole.log(x), output GlobalVO(G)Mediumx:1

    4 questions to investigate JS stack memory

    image.png

4. Abnormal version of stack memory with formal parameter function

What is the output of the following topic?

var x = 1
function func(x, y = function func2() { x = 2 }) {
  //Here, X has one more var declaration
  var x = 3;
  y()
  console.log(x)  
}
func(5)
console.log(x)

Here we will talk about the mechanism of the browser running ES6:

ES6 produces two cases of block level scopes

Type 1, normal{}Generated block level scope

This is what we usually know,ES6There is a block level scope in, that is, as long as{}(except in objects){})Appearlet/const/function

The second is generated when the browser is running, that is, as long as the following two conditions are met:
  1. Function has a default value assigned to any formal parameter
  2. A variable has been declared separately in the function body(var/let/const/function(all counts)
    Then this function will produce2 contexts
    • One is the private context generated by the execution of the function itself, such as the abovefuncWhen the function executes, aEC(FUNC)
    • One is the block level context enclosed by curly braces in the function bodyEC(BLOCK)

Next, we still draw pictures to analyze:

  1. The first process is consistent with step 1 \ 2 of the previous question, and still generates the global execution contextEC(G), it’sVO(G)Declare two variables:xandfuncxPoint to value 1,funcPoint to function heap memoryAAAFFF000

    4 questions to investigate JS stack memory

    image.png
  2. The second process, executionfunc(5)At this time, becausefuncMedium formal parameteryThe default value is set, and the variable is declared in the function bodyvar x = 3, according to the above rules, two execution contexts will be generated hereEC(FUNC)andEC(BLOCK)

  3. Let’s analyze it firstEC(FUNC), its scope isEC(G), the scope chain is its own contextEC(FUNC)And parent scope contextEC(G), its formal parameter isx => 5Y = > func2 heap memory aafff111

    4 questions to investigate JS stack memory

    image.png

4. YesEC(BLOCK)Code block analysis, its scope isEC(FUNC), the scope chain is its own contextEC(BLOCK)And parent execution contextEC(FUNC), at the block level,var x = 3It declaresx, soxIs a private variable in the block level scope. When executedx = 3When, thex => 3

4 questions to investigate JS stack memory

image.png
  1. Continue executiony(), found no in block levelyVariable, go to its scope chain parentEC(FUNC)Look, I found ity, executey(), generatefunc2Execution contextEC(FUNC2), its scope isEC(FUNC)So its scope chain is[self execution context EC (func2), scope EC (func)], there is no formal parameter and variable declaration, so there is no private variable; Execute function bodyx = 2, inEC(FUNC2)Not found inx, so go to its scope superiorEC(FUNC)EC(FUNC)There arex, soEC(FUNC)inx => 2

    4 questions to investigate JS stack memory

    image.png
  2. EC(BLOCK)Continue executionconsole.log(x), printedEC(BLOCK)Private inx, i.e3

  3. func(5)After execution, continue toEC(G)inconsole.log(x), printedVO(G)Mediumx, i.e1

    4 questions to investigate JS stack memory

    image.png

So the answer is 3 and 1

Next, in order to prove that I’m not talking nonsense = ‘=, let’s debug the last question on the browser

  1. Put a breakpoint debugger at the beginning and open it in the browser. Because there are too many global variables in the window, it is difficult to find the global variablesVO(G)ofx, so I’mWatchAdded inwindow.xVariables to facilitate our observationVO(G)Medium (i.e. browser)Global)xYou can see that before debugging, the globalxyesundefind
//Question 4: abnormal stack evaluation with formal parameters
debugger;
var x = 1
function func(x, y = function func2() { x = 2 }) {
  var x = 3;
  y()
  console.log(x)
}
func(5)
console.log(x)
4 questions to investigate JS stack memory

image.png
  1. When execution is completex=1When,func(5)Before execution, you can see the globalVO(G)ofx = 1

    4 questions to investigate JS stack memory

    image.png
  2. Continuefunc(5), you can see the birthScopeIn, that isEC(FUNC)Generate two execution contexts in the scope ofBLOCKandLOCAL, corresponding to what we said aboveEC(BLOCK)andEC(FUNC), because there arexThen there are declarations at the block levelx, so it will be privatexMap to in block levelx

    4 questions to investigate JS stack memory

    image.png
  3. In execution block levelvar x = 3, private variables found at block levelxBecome3

    4 questions to investigate JS stack memory

    image.png
  4. implementy(), that is, the execution context of func2 is generatedEC(FUNC2), because there is no private variable, itsLocalEmpty

    4 questions to investigate JS stack memory

    image.png
  5. End of executiony()inx = 2After, seeEC(FUNC)Mediumx => 2

    4 questions to investigate JS stack memory

    image.png
  6. implementEC(BLOCK)inconsole.log(x), the output isBlockMediumx

    4 questions to investigate JS stack memory

    image.png
  7. implementEC(G)inconsole.log(x), the output isGlobalMediumx

    4 questions to investigate JS stack memory

    image.png

Thus, the above analysis conclusion is verified.

Recommended Today

Proper memory alignment in go language

problem type Part1 struct { a bool b int32 c int8 d int64 e byte } Before we start, I want you to calculatePart1What is the total occupancy size? func main() { fmt.Printf(“bool size: %d\n”, unsafe.Sizeof(bool(true))) fmt.Printf(“int32 size: %d\n”, unsafe.Sizeof(int32(0))) fmt.Printf(“int8 size: %d\n”, unsafe.Sizeof(int8(0))) fmt.Printf(“int64 size: %d\n”, unsafe.Sizeof(int64(0))) fmt.Printf(“byte size: %d\n”, unsafe.Sizeof(byte(0))) fmt.Printf(“string size: %d\n”, […]