function-hoisting.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. const noHoistLexTopLevel = false;
  2. let canCallNonHoisted = 0;
  3. expect(basicHoistTopLevel()).toEqual("basicHoistTopLevel");
  4. function basicHoistTopLevel() {
  5. return "basicHoistTopLevel";
  6. }
  7. expect(typeof noHoistLexTopLevel).toBe("boolean");
  8. expect(typeof hoistInBlockTopLevel).toBe("undefined");
  9. {
  10. expect(noHoistLexTopLevel()).toEqual("noHoistLexTopLevel");
  11. ++canCallNonHoisted;
  12. expect(basicHoistTopLevel()).toEqual("basicHoistTopLevelInBlock");
  13. ++canCallNonHoisted;
  14. expect(hoistInBlockTopLevel()).toEqual("hoistInBlockTopLevel");
  15. function hoistInBlockTopLevel() {
  16. return "hoistInBlockTopLevel";
  17. }
  18. function noHoistLexTopLevel() {
  19. return "noHoistLexTopLevel";
  20. }
  21. function basicHoistTopLevel() {
  22. return "basicHoistTopLevelInBlock";
  23. }
  24. }
  25. expect(canCallNonHoisted).toBe(2);
  26. expect(hoistInBlockTopLevel()).toEqual("hoistInBlockTopLevel");
  27. {
  28. {
  29. expect(nestedBlocksTopLevel()).toEqual("nestedBlocksTopLevel");
  30. function nestedBlocksTopLevel() {
  31. return "nestedBlocksTopLevel";
  32. }
  33. }
  34. expect(nestedBlocksTopLevel()).toEqual("nestedBlocksTopLevel");
  35. }
  36. expect(nestedBlocksTopLevel()).toEqual("nestedBlocksTopLevel");
  37. expect(hoistInBlockTopLevel()).toEqual("hoistInBlockTopLevel");
  38. expect(typeof hoistSecondOneTopLevel).toBe("undefined");
  39. {
  40. expect(typeof hoistSecondOneTopLevel).toBe("undefined");
  41. {
  42. expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
  43. function hoistSecondOneTopLevel() {
  44. return "hoistFirstOneTopLevel";
  45. }
  46. expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
  47. function hoistSecondOneTopLevel() {
  48. return "hoistSecondOneTopLevel";
  49. }
  50. expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
  51. {
  52. expect(hoistSecondOneTopLevel()).toEqual("hoistThirdOneTopLevel");
  53. function hoistSecondOneTopLevel() {
  54. return "hoistThirdOneTopLevel";
  55. }
  56. expect(hoistSecondOneTopLevel()).toEqual("hoistThirdOneTopLevel");
  57. }
  58. expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
  59. }
  60. expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
  61. }
  62. expect(hoistSecondOneTopLevel()).toEqual("hoistSecondOneTopLevel");
  63. test("Non-strict function does hoist", () => {
  64. const noHoistLexFunction = false;
  65. let canCallNonHoisted = 0;
  66. expect(basicHoistFunction()).toEqual("basicHoistFunction");
  67. function basicHoistFunction() {
  68. return "basicHoistFunction";
  69. }
  70. expect(typeof noHoistLexFunction).toBe("boolean");
  71. expect(typeof hoistInBlockFunction).toBe("undefined");
  72. {
  73. expect(noHoistLexFunction()).toEqual("noHoistLexFunction");
  74. ++canCallNonHoisted;
  75. expect(basicHoistFunction()).toEqual("basicHoistFunctionInBlock");
  76. ++canCallNonHoisted;
  77. expect(hoistInBlockFunction()).toEqual("hoistInBlockFunction");
  78. function hoistInBlockFunction() {
  79. return "hoistInBlockFunction";
  80. }
  81. function noHoistLexFunction() {
  82. return "noHoistLexFunction";
  83. }
  84. function basicHoistFunction() {
  85. return "basicHoistFunctionInBlock";
  86. }
  87. }
  88. expect(canCallNonHoisted).toBe(2);
  89. expect(hoistInBlockFunction()).toEqual("hoistInBlockFunction");
  90. {
  91. {
  92. expect(nestedBlocksFunction()).toEqual("nestedBlocksFunction");
  93. function nestedBlocksFunction() {
  94. return "nestedBlocksFunction";
  95. }
  96. }
  97. expect(nestedBlocksFunction()).toEqual("nestedBlocksFunction");
  98. }
  99. expect(nestedBlocksFunction()).toEqual("nestedBlocksFunction");
  100. expect(hoistInBlockFunction()).toEqual("hoistInBlockFunction");
  101. expect(typeof hoistSecondOneFunction).toBe("undefined");
  102. {
  103. expect(typeof hoistSecondOneFunction).toBe("undefined");
  104. {
  105. expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
  106. function hoistSecondOneFunction() {
  107. return "hoistFirstOneFunction";
  108. }
  109. expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
  110. function hoistSecondOneFunction() {
  111. return "hoistSecondOneFunction";
  112. }
  113. expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
  114. {
  115. expect(hoistSecondOneFunction()).toEqual("hoistThirdOneFunction");
  116. function hoistSecondOneFunction() {
  117. return "hoistThirdOneFunction";
  118. }
  119. expect(hoistSecondOneFunction()).toEqual("hoistThirdOneFunction");
  120. }
  121. expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
  122. }
  123. expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
  124. }
  125. expect(hoistSecondOneFunction()).toEqual("hoistSecondOneFunction");
  126. expect(notBlockFunctionTopLevel()).toBe("second");
  127. function notBlockFunctionTopLevel() {
  128. return "first";
  129. }
  130. expect(notBlockFunctionTopLevel()).toBe("second");
  131. function notBlockFunctionTopLevel() {
  132. return "second";
  133. }
  134. expect(notBlockFunctionTopLevel()).toBe("second");
  135. });
  136. test("Strict function does not hoist", () => {
  137. "use strict";
  138. const noHoistLexStrictFunction = false;
  139. let canCallNonHoisted = 0;
  140. expect(basicHoistStrictFunction()).toEqual("basicHoistStrictFunction");
  141. function basicHoistStrictFunction() {
  142. return "basicHoistStrictFunction";
  143. }
  144. expect(typeof noHoistLexStrictFunction).toBe("boolean");
  145. // We cannot use expect(() => ).toThrow because that introduces extra scoping
  146. try {
  147. hoistInBlockStrictFunction;
  148. expect().fail();
  149. } catch (e) {
  150. expect(e).toBeInstanceOf(ReferenceError);
  151. expect(e.message).toEqual("'hoistInBlockStrictFunction' is not defined");
  152. }
  153. {
  154. expect(noHoistLexStrictFunction()).toEqual("noHoistLexStrictFunction");
  155. ++canCallNonHoisted;
  156. expect(basicHoistStrictFunction()).toEqual("basicHoistStrictFunctionInBlock");
  157. ++canCallNonHoisted;
  158. expect(hoistInBlockStrictFunction()).toEqual("hoistInBlockStrictFunction");
  159. function hoistInBlockStrictFunction() {
  160. return "hoistInBlockStrictFunction";
  161. }
  162. function noHoistLexStrictFunction() {
  163. return "noHoistLexStrictFunction";
  164. }
  165. function basicHoistStrictFunction() {
  166. return "basicHoistStrictFunctionInBlock";
  167. }
  168. }
  169. expect(canCallNonHoisted).toBe(2);
  170. try {
  171. hoistInBlockStrictFunction;
  172. expect().fail();
  173. } catch (e) {
  174. expect(e).toBeInstanceOf(ReferenceError);
  175. expect(e.message).toEqual("'hoistInBlockStrictFunction' is not defined");
  176. }
  177. {
  178. try {
  179. nestedBlocksStrictFunction;
  180. expect().fail();
  181. } catch (e) {
  182. expect(e).toBeInstanceOf(ReferenceError);
  183. expect(e.message).toEqual("'nestedBlocksStrictFunction' is not defined");
  184. }
  185. {
  186. expect(nestedBlocksStrictFunction()).toEqual("nestedBlocksStrictFunction");
  187. function nestedBlocksStrictFunction() {
  188. return "nestedBlocksStrictFunction";
  189. }
  190. }
  191. try {
  192. nestedBlocksStrictFunction;
  193. expect().fail();
  194. } catch (e) {
  195. expect(e).toBeInstanceOf(ReferenceError);
  196. expect(e.message).toEqual("'nestedBlocksStrictFunction' is not defined");
  197. }
  198. }
  199. try {
  200. nestedBlocksStrictFunction;
  201. expect().fail();
  202. } catch (e) {
  203. expect(e).toBeInstanceOf(ReferenceError);
  204. expect(e.message).toEqual("'nestedBlocksStrictFunction' is not defined");
  205. }
  206. expect(notBlockStrictFunctionTopLevel()).toBe("second");
  207. function notBlockStrictFunctionTopLevel() {
  208. return "first";
  209. }
  210. expect(notBlockStrictFunctionTopLevel()).toBe("second");
  211. function notBlockStrictFunctionTopLevel() {
  212. return "second";
  213. }
  214. {
  215. expect(notBlockStrictFunctionTopLevel()).toBe("third");
  216. function notBlockStrictFunctionTopLevel() {
  217. return "third";
  218. }
  219. expect(notBlockStrictFunctionTopLevel()).toBe("third");
  220. }
  221. expect(notBlockStrictFunctionTopLevel()).toBe("second");
  222. // Inside a block inside a strict function gives a syntax error
  223. let didNotRunEval = true;
  224. expect(`
  225. didNotRunEval = false;
  226. () => {
  227. "use strict";
  228. {
  229. function f() {
  230. return "first";
  231. }
  232. function f() {
  233. return "second";
  234. }
  235. }
  236. };
  237. `).not.toEval();
  238. expect(didNotRunEval).toBeTrue();
  239. // However, in eval it's fine but the function does not escape the eval
  240. {
  241. let ranEval = false;
  242. eval(`
  243. expect(hoistSecondOneStrictFunction()).toBe("hoistSecondOneStrictFunction");
  244. function hoistSecondOneStrictFunction() {
  245. return "hoistFirstOneStrictFunction";
  246. }
  247. function hoistSecondOneStrictFunction() {
  248. return "hoistSecondOneStrictFunction";
  249. }
  250. ranEval = true;
  251. `);
  252. expect(ranEval).toBeTrue();
  253. try {
  254. hoistSecondOneStrictFunction;
  255. expect().fail();
  256. } catch (e) {
  257. expect(e).toBeInstanceOf(ReferenceError);
  258. expect(e.message).toEqual("'hoistSecondOneStrictFunction' is not defined");
  259. }
  260. }
  261. });