MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1// Test class function (method) hoisting behavior
2// Class methods can be called regardless of their definition order within the class body
3// But classes themselves are NOT hoisted like function declarations
4
5console.log("=== Test 1: Methods calling other methods defined later ===");
6class Calculator {
7 calculate(x) {
8 return this.double(x) + this.triple(x);
9 }
10
11 double(x) {
12 return x * 2;
13 }
14
15 triple(x) {
16 return x * 3;
17 }
18}
19
20let calc = new Calculator();
21console.log("calculate(5) should be 25:", calc.calculate(5));
22
23console.log("\n=== Test 2: Constructor calling methods defined after it ===");
24class Greeter {
25 constructor(name) {
26 this.name = name;
27 this.greeting = this.makeGreeting();
28 }
29
30 makeGreeting() {
31 return "Hello, " + this.name + "!";
32 }
33}
34
35let greeter = new Greeter("World");
36console.log("greeting should be 'Hello, World!':", greeter.greeting);
37
38console.log("\n=== Test 3: Method calling method defined before constructor ===");
39class Parser {
40 parse(input) {
41 return this.validate(input);
42 }
43
44 constructor() {
45 this.initialized = true;
46 }
47
48 validate(input) {
49 return input.length > 0;
50 }
51}
52
53let parser = new Parser();
54console.log("parse('test') should be true:", parser.parse("test"));
55
56console.log("\n=== Test 4: Mutual method references ===");
57class Counter {
58 increment() {
59 this.value = this.getValue() + 1;
60 }
61
62 decrement() {
63 this.value = this.getValue() - 1;
64 }
65
66 constructor(initial) {
67 this.value = initial;
68 }
69
70 getValue() {
71 return this.value;
72 }
73}
74
75let counter = new Counter(10);
76counter.increment();
77console.log("after increment should be 11:", counter.getValue());
78counter.decrement();
79counter.decrement();
80console.log("after two decrements should be 9:", counter.getValue());
81
82console.log("\n=== Test 5: Deep method call chain ===");
83class Chain {
84 a() {
85 return this.b() + 1;
86 }
87
88 b() {
89 return this.c() + 2;
90 }
91
92 c() {
93 return this.d() + 3;
94 }
95
96 d() {
97 return 10;
98 }
99}
100
101let chain = new Chain();
102console.log("a() should be 16 (10+3+2+1):", chain.a());
103
104console.log("\n=== Test 6: Class NOT hoisted (should error if accessed before) ===");
105try {
106 let early = new NotYetDefined();
107 console.log("ERROR: Should not reach here");
108} catch (e) {
109 console.log("Correctly caught error - class not hoisted");
110}
111
112class NotYetDefined {
113 constructor() {
114 this.value = 42;
115 }
116}
117
118let late = new NotYetDefined();
119console.log("After definition, value should be 42:", late.value);
120
121console.log("\n=== Test 7: Static methods hoisting within class ===");
122class StaticTest {
123 static compute() {
124 return StaticTest.helper() * 2;
125 }
126
127 static helper() {
128 return 21;
129 }
130}
131
132console.log("StaticTest.compute() should be 42:", StaticTest.compute());
133
134console.log("\n=== Test 8: Getter/setter order independence ===");
135class GetSet {
136 get doubled() {
137 return this._val * 2;
138 }
139
140 constructor(val) {
141 this._val = val;
142 }
143
144 set doubled(val) {
145 this._val = val / 2;
146 }
147}
148
149let gs = new GetSet(5);
150console.log("doubled should be 10:", gs.doubled);
151gs.doubled = 20;
152console.log("after setting doubled=20, _val should be 10:", gs._val);
153
154console.log("\n=== All class function hoisting tests completed ===");