|
| 1 | +{ |
| 2 | + "lessonId": "js-7-1", |
| 3 | + "title": "V8 Hidden Classes (Shapes)", |
| 4 | + "concept": "The V8 engine uses an internal map called a 'Hidden Class' to speed up property access on objects.", |
| 5 | + "content": { |
| 6 | + "code": "function Point(x, y) {\n this.x = x;\n this.y = y;\n}\n\nconst p1 = new Point(1, 2);\nconst p2 = new Point(3, 4);\n\n// p1에 새로운 속성 추가 (히든 클래스 변경 발생!)\np1.z = 5;\n\nconsole.log(p1.x, p1.z);\nconsole.log(p2.x);", |
| 7 | + "steps": [ |
| 8 | + { |
| 9 | + "title": "Creating p1: hidden class creation", |
| 10 | + "explanation": "When `new Point(1, 2)` runs, the V8 engine creates a `Hidden Class` to remember the 'shape' of this object.\nEach time a property is added, the object transitions to a new hidden class.\n1. A hidden class C0 is created for the empty object\n2. `this.x = 1` causes a transition from C0 to C1 (x added)\n3. `this.y = 2` causes a transition from C1 to C2 (y added)\n`p1` ends up with hidden class C2.", |
| 11 | + "keyInsight": "Adding a property isn't just 'storing a value' — it triggers an internal structure change (Transition).", |
| 12 | + "visualizationType": "memory", |
| 13 | + "memoryState": { |
| 14 | + "stack": [ |
| 15 | + { |
| 16 | + "name": "p1", |
| 17 | + "value": "→ 0x001" |
| 18 | + } |
| 19 | + ], |
| 20 | + "heap": [ |
| 21 | + { |
| 22 | + "address": "0x001", |
| 23 | + "content": "{ x: 1, y: 2 }", |
| 24 | + "label": "HiddenClass: C2" |
| 25 | + } |
| 26 | + ] |
| 27 | + }, |
| 28 | + "code": "const p1 = new Point(1, 2);" |
| 29 | + }, |
| 30 | + { |
| 31 | + "title": "Creating p2: reusing the hidden class", |
| 32 | + "explanation": "When creating `p2`, V8 notices that the property addition order and structure are identical to `p1`.\nSo instead of creating a new hidden class, it reuses the C2 hidden class that `p1` already established.\nThis makes property access very fast.", |
| 33 | + "keyInsight": "Initializing properties in the same order lets the engine treat objects as the 'same type' and optimize them.", |
| 34 | + "visualizationType": "memory", |
| 35 | + "memoryState": { |
| 36 | + "stack": [ |
| 37 | + { |
| 38 | + "name": "p2", |
| 39 | + "value": "→ 0x002" |
| 40 | + } |
| 41 | + ], |
| 42 | + "heap": [ |
| 43 | + { |
| 44 | + "address": "0x001", |
| 45 | + "content": "{ x: 1, y: 2 }", |
| 46 | + "label": "HiddenClass: C2" |
| 47 | + }, |
| 48 | + { |
| 49 | + "address": "0x002", |
| 50 | + "content": "{ x: 3, y: 4 }", |
| 51 | + "label": "HiddenClass: C2 (Shared)" |
| 52 | + } |
| 53 | + ] |
| 54 | + }, |
| 55 | + "code": "const p2 = new Point(3, 4);" |
| 56 | + }, |
| 57 | + { |
| 58 | + "title": "Adding a property: hidden class branch", |
| 59 | + "explanation": "After object creation, we dynamically add the `z` property to `p1` only.\nSince the existing C2 hidden class has no information about `z`, V8 creates a new hidden class C3 (C2 + z) and transitions `p1` to C3.\nNow `p1` and `p2` have different hidden classes, reducing optimization efficiency.", |
| 60 | + "keyInsight": "Dynamically adding properties after object creation is the main culprit for breaking optimization.", |
| 61 | + "visualizationType": "memory", |
| 62 | + "memoryState": { |
| 63 | + "heap": [ |
| 64 | + { |
| 65 | + "address": "0x001", |
| 66 | + "content": "{ x: 1, y: 2, z: 5 }", |
| 67 | + "label": "HiddenClass: C3", |
| 68 | + "warning": "Shape Changed" |
| 69 | + }, |
| 70 | + { |
| 71 | + "address": "0x002", |
| 72 | + "content": "{ x: 3, y: 4 }", |
| 73 | + "label": "HiddenClass: C2" |
| 74 | + } |
| 75 | + ] |
| 76 | + }, |
| 77 | + "code": "p1.z = 5;" |
| 78 | + }, |
| 79 | + { |
| 80 | + "title": "Printing p1 properties", |
| 81 | + "explanation": "Printing `p1`'s `x` and `z` properties.\n`p1` uses the new hidden class C3 to locate the values.", |
| 82 | + "visualizationType": "memory", |
| 83 | + "memoryState": { |
| 84 | + "output": [ |
| 85 | + "1 5" |
| 86 | + ] |
| 87 | + }, |
| 88 | + "code": "console.log(p1.x, p1.z);" |
| 89 | + }, |
| 90 | + { |
| 91 | + "title": "Printing p2 properties", |
| 92 | + "explanation": "Printing `p2`'s `x` property.\n`p2` still uses the original hidden class C2.", |
| 93 | + "visualizationType": "memory", |
| 94 | + "memoryState": { |
| 95 | + "output": [ |
| 96 | + "1 5", |
| 97 | + "3" |
| 98 | + ] |
| 99 | + }, |
| 100 | + "code": "console.log(p2.x);" |
| 101 | + } |
| 102 | + ] |
| 103 | + }, |
| 104 | + "quiz": { |
| 105 | + "question": "What is the best practice for maintaining V8's Hidden Class optimization?", |
| 106 | + "options": [ |
| 107 | + "Initialize all properties in order inside the constructor", |
| 108 | + "Add properties on the fly whenever needed (obj.newProp = 1)", |
| 109 | + "Change the property initialization order each time (x, y → y, x)", |
| 110 | + "Frequently delete properties with the delete operator" |
| 111 | + ], |
| 112 | + "correctIndex": 0, |
| 113 | + "explanation": "Defining properties in order inside the constructor ensures all created objects share the same hidden class, giving you the best performance." |
| 114 | + }, |
| 115 | + "misconceptions": [ |
| 116 | + { |
| 117 | + "wrong": "JavaScript objects are HashMaps, so they're slow", |
| 118 | + "correct": "V8 uses hidden classes to access properties as fast as **array offsets**.", |
| 119 | + "why": "Early JS engines used hash maps, but modern V8 introduced hidden classes to achieve property access speeds comparable to statically typed languages like C++ and Java." |
| 120 | + } |
| 121 | + ], |
| 122 | + "keyTakeaway": "Keep your objects' 'shape' consistent. Consistency is speed." |
| 123 | +} |
0 commit comments