{"id":346333,"date":"2023-03-06T09:04:51","date_gmt":"2023-03-06T09:04:51","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=346333"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=346333","title":{"rendered":"<span>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 JavaScript<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>\u041f\u0440\u043e \u0441\u0442\u0430\u0442\u044c\u044e<\/h3>\n<p>\u041c\u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e \u0445\u043e\u0434\u0443 \u0447\u0442\u0435\u043d\u0438\u044f \u043e\u0447\u0435\u043d\u044c \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u0432 \u0441\u0435\u0440\u0432\u0438\u0441 <a href=\"http:\/\/codesandbox.io\/\" rel=\"noopener noreferrer nofollow\">codesandbox.io<\/a> \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u043d\u0438\u044f, \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0438\u0442\u0430\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435. \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0432\u0435\u043d\u043d\u043e, \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430, \u0430 \u043d\u0435 \u0441 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043b\u0430\u0441\u044c \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0439 \u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u0434\u043b\u044f \u0432\u0441\u0435\u0445. \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u0434\u0436\u0443\u043d \u0440\u0435\u0431\u044f\u0442. \u041d\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u043c\u044b \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0443\u0441\u0438\u043b\u0438\u0439 \u043e\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0441 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c. \u042f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u043b\u0441\u044f \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438 \u043b\u0438\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u0442\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b. \u0412\u0435\u0440\u044e, \u0447\u0442\u043e \u043a\u043e\u043c\u0443 \u043d\u0430\u0434\u043e \u0438 \u0441\u0430\u043c \u0437\u0430\u0433\u0443\u0433\u043b\u0438\u0442 \u043d\u0435\u0445\u0432\u0430\u0442\u0430\u044e\u0449\u0443\u044e \u0435\u043c\u0443 \u0442\u0435\u043e\u0440\u0438\u044e \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e. \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u044f \u0443 \u0432\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u043e\u0435 \u0438 \u0432\u0441\u0435\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0441\u043b\u043e\u0436\u0438\u0442\u0441\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432 \u0442\u0430\u043a\u0438\u0445 \u0442\u0435\u043c\u0430\u0445 \u043a\u0430\u043a \u0437\u0430\u043c\u044b\u043a\u0430\u043d\u0438\u0435, \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432\u044b\u0441\u0448\u0435\u0433\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0430, \u0447\u0438\u0441\u0442\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u0430\u0440\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, TDD, \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044f \u0438 property-based \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0410 \u0433\u043b\u0430\u0432\u043d\u043e\u0435 &#8212; \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043a\u0430\u043a \u0438 \u0433\u0434\u0435 \u044d\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c.<\/p>\n<h3>\u041f\u0440\u043e \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u044b<\/h3>\n<p>\u0424\u0440\u0430\u0437\u0430 \u201c\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u201d \u043e\u0431\u044b\u0447\u043d\u043e \u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0430\u0441\u0430 \u0447\u0435\u0433\u043e-\u0442\u043e. \u041d\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u0438\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u0437\u0434\u0435\u0441\u044c. \u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0442\u0435\u043c\u0435. \u041f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0432 \u0441\u0430\u043c\u043e\u043c\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e, \u043c\u044b \u043b\u0443\u0447\u0448\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f \u0432 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u044b\u0447\u043d\u043e \u0434\u0435\u043b\u0430\u044e\u0442 \u0447\u0430\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b \u0437\u0430 \u043d\u0430\u0441. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u044b  \u0441\u043c\u043e\u0436\u0435\u043c \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u043e\u043b\u044c\u0437\u044b \u0438\u0437 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0437\u043d\u0430\u043d\u0438\u044f \u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0432\u0430\u043c \u0434\u0435\u0431\u0430\u0436\u0438\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0445\u043e\u0442\u044f \u0431\u044b \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0437\u043d\u0430\u0442\u044c, \u0447\u0442\u043e \u043c\u043e\u0433\u043b\u043e \u043f\u043e\u0439\u0442\u0438 \u043d\u0435 \u0442\u0430\u043a. \u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u0448\u044c\u0441\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0441\u0430\u043c, \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u0435\u0449\u0438 \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u043e\u0449\u0435 \u0438 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u044e\u0442 \u0431\u044b\u0442\u044c \u043c\u0430\u0433\u0438\u0435\u0439. \u0410 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435, \u043a\u0430\u0437\u0430\u0432\u0448\u0438\u0435\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f <a href=\"https:\/\/www.youtube.com\/watch?v=-5wpm-gesOY&amp;ab_channel=Computerphile\" rel=\"noopener noreferrer nofollow\">\u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u043f\u0438\u0442\u0430\u043d\u043d\u044b\u043c\u0438 \u043d\u044e\u0430\u043d\u0441\u0430\u043c\u0438<\/a>, \u0447\u0442\u043e \u0442\u044b \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0448\u044c\u0441\u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u0435\u043d \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044e \u043f\u0430\u043a\u0435\u0442\u0430 \u0437\u0430 \u0435\u0433\u043e \u0442\u0440\u0443\u0434.<\/p>\n<h3>\u041c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p>\u041c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u043d\u044b\u043c \u0441\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041a \u043a\u044d\u0448\u0443 \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u044c\u0441\u044f \u043b\u044e\u0431\u043e\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0431\u0443\u0434\u0443\u0449\u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043a \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0432\u044b\u0437\u043e\u0432\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0412 \u043e\u0431\u0449\u0435\u043c, \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043f\u043e\u0441\u043e\u0431 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u0430 \u043d\u0430\u0448\u0438\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c.<\/p>\n<p>\u041a\u0430\u043a \u0443\u0436\u0435 \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b\u043e\u0441\u044c, \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u043f\u043e\u043b\u043d\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439. \u0412\u043e\u0442 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c: <a href=\"https:\/\/lodash.com\/docs\/#memoize\" rel=\"noopener noreferrer nofollow\">\u043b\u043e\u0434\u0430\u0448\u0435\u0432\u0441\u043a\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f memoize<\/a>, <a href=\"https:\/\/www.npmjs.com\/package\/p-memoize\" rel=\"noopener noreferrer nofollow\">p-memoize<\/a>, <a href=\"https:\/\/caolan.github.io\/async\/v3\/docs.html#memoize\" rel=\"noopener noreferrer nofollow\">async<\/a>, <a href=\"https:\/\/www.npmjs.com\/package\/memoizee\" rel=\"noopener noreferrer nofollow\">memoizee<\/a> (\u043c\u043e\u0439 \u043b\u044e\u0431\u0438\u043c\u0447\u0438\u043a), <a href=\"https:\/\/www.npmjs.com\/package\/moize\" rel=\"noopener noreferrer nofollow\">moize<\/a>, <a href=\"https:\/\/www.npmjs.com\/package\/fast-memoize\" rel=\"noopener noreferrer nofollow\">fast-memoize<\/a>,  <a href=\"https:\/\/github.com\/developit\/decko#memoize\" rel=\"noopener noreferrer nofollow\">ES7\u00a0\u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <\/a><code>@memoize<\/code>\u00a0\u0438\u0437\u00a0decko.<\/p>\n<p>\u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0432\u044b \u044f\u0432\u043d\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044e, \u0442\u043e \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u044e\u0442 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0432\u044b \u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435\u0441\u044c \u043d\u0430 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0439 \u043e\u0441\u043d\u043e\u0432\u0435. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0432 \u043c\u0438\u0440\u0435 react\u2019\u0430 \u044d\u0442\u043e \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/github.com\/reduxjs\/reselect\" rel=\"noopener noreferrer nofollow\">reselect<\/a>, <a href=\"https:\/\/tanstack.com\/query\/v4\" rel=\"noopener noreferrer nofollow\">react-query<\/a>, <a href=\"https:\/\/www.apollographql.com\/docs\/react\/\" rel=\"noopener noreferrer nofollow\">apollo-client<\/a>, \u0445\u0443\u043a <a href=\"https:\/\/ru.reactjs.org\/docs\/hooks-reference.html#usememo\" rel=\"noopener noreferrer nofollow\">useMemo<\/a> \u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0432\u044b\u0441\u0448\u0435\u0433\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0430 <a href=\"https:\/\/ru.reactjs.org\/docs\/react-api.html#reactmemo\" rel=\"noopener noreferrer nofollow\">memo<\/a> \u0438 \u043c\u043d\u043e\u0433\u043e\u0435 \u0434\u0440\u0443\u0433\u043e\u0435.<\/p>\n<h2>\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0451\u0439<\/h2>\n<p>\u0412 CodeSandbox \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f <a href=\"https:\/\/codesandbox.io\/s\/starter-template-o02e2z?file=\/src\/withMemo.js\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0440\u0442\u043e\u0432\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d<\/a> \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c, \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438. \u0412 \u043d\u0451\u043c \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430: withMemo.js \u0438 withMemo.test.js<\/p>\n<p>\u041f\u0440\u043e\u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435 \u043f\u043e \u0441\u0442\u0430\u0442\u044c\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0448\u0430\u0433\u043e\u0432:<\/p>\n<ol>\n<li>\n<p>\u042f \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0447\u0442\u043e-\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043b\u0438 \u043a\u0430\u043a-\u0442\u043e \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0442\u0435\u043c \u044f \u0434\u0430\u044e \u0442\u0435\u0441\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 withMemo.test.js \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0435 \u0447\u0442\u043e-\u0442\u043e \u043d\u0430 \u0441\u0432\u043e\u0451 \u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u0438\u0435 \u0432 withMemo.js \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043d\u043e\u0432\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043f\u0440\u043e\u0448\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u0430 \u0441\u0442\u0430\u0440\u044b\u0435 \u043d\u0435 \u0441\u043b\u043e\u043c\u0430\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u042f \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043e\u0442 \u0441\u0435\u0431\u044f \u0438, \u0435\u0441\u043b\u0438 \u043d\u0430\u0434\u043e, \u0442\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 (\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432, \u0430 \u0437\u0430\u0442\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043b\u0438\u0441\u044c) \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f <a href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/450316\/\" rel=\"noopener noreferrer nofollow\">Test Driven Development (TDD)<\/a>.<\/p>\n<h2>\u041f\u043e\u0435\u0445\u0430\u043b\u0438<\/h2>\n<h2>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0435\u0437 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432<\/h2>\n<ol>\n<li>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439:<\/p>\n<pre><code class=\"javascript\">\/\/ \u0412\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e function generateDigitsOfPi() {     const DIGITS_COUNT = 10_001;     let q = 1n;     let r = 180n;     let t = 60n;     let i = 2n;     let str = '';     for (let j = 0; j &lt; DIGITS_COUNT; j++) {         let digit = ((i * 27n - 12n) * q + r * 5n) \/ (t * 5n);         str += digit         let u = i * 3n;         u = (u + 1n) * 3n * (u + 2n);         r = u * 10n * (q * (i * 5n - 2n) + r - t * digit);         q *= 10n * i * (i++ * 2n - 1n);         t *= u;     }      const arr = str.split('');     arr.splice(1, 0, '.');     return arr.join(''); }<\/code><\/pre>\n<p>\u042d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u0435\u0440\u043d\u0451\u0442 \u0447\u0438\u0441\u043b\u043e \u041f\u0438 \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c\u044e \u0432 10 000 \u0446\u0438\u0444\u0440 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439. \u0422\u0430\u043a\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442 \u0434\u043b\u044f \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0451 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0430. \u0410 \u043f\u043e\u0441\u043b\u0435 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0434\u0435\u043b\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0422\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(     \"should call original function only ones and \" +       \"always return original result (no args)\",     () => {       const result = Math.random();       const fn = jest.fn(() => result);       const memoizedFn = withMemo(fn);        expect(fn).toHaveBeenCalledTimes(0);       expect(memoizedFn()).toBe(result);       expect(fn).toHaveBeenCalledTimes(1);       expect(memoizedFn()).toBe(result);       expect(fn).toHaveBeenCalledTimes(1);     }   );<\/code><\/pre>\n<p>\u0417\u0430\u043c\u0435\u0442\u0438\u043b\u0438, \u0447\u0442\u043e \u044f \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>result<\/code> \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0432\u043c\u0435\u0441\u0442\u043e \u0447\u0435\u0433\u043e-\u0442\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e? \u042d\u0442\u043e \u043e\u0434\u043d\u0430 \u0438\u0437 \u043f\u0440\u0430\u043a\u0442\u0438\u043a <a href=\"https:\/\/github.com\/dubzzz\/fast-check\/blob\/main\/packages\/fast-check\/documentation\/HandsOnPropertyBased.md\" rel=\"noopener noreferrer nofollow\">property-based \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/a>. \u0414\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u043e\u043d\u0430 \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0435\u0442\u0441\u044f \u0432 <a href=\"https:\/\/www.youtube.com\/watch?v=DlQZ4OsALgI&amp;ab_channel=AndrewBurgess\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u043c \u0432\u0438\u0434\u0435\u043e<\/a>. \u0422\u0430\u043a\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u0447\u0443\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u043f\u0438\u0441\u0430\u0442\u044c, \u0437\u0430\u0442\u043e \u043e\u043d\u0438 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u044e\u0442 \u0431\u043e\u043b\u0435\u0435 \u043e\u0431\u0449\u0438\u0435 \u0441\u043b\u0443\u0447\u0430\u0438. \u0410 <a href=\"https:\/\/github.com\/dubzzz\/fast-check\" rel=\"noopener noreferrer nofollow\">\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438<\/a> \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u043e\u0433\u043e\u043d\u044f\u0442\u044c \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u0442\u0435\u0441\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0441\u0430\u043c\u0438, \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044f \u0440\u0430\u0437\u043d\u044b\u0435 \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044f \u043a\u0440\u0430\u0439\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u0443\u0441\u0442\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438\u043b\u0438 \u043c\u0430\u0441\u0441\u0438\u0432\u044b) \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u0430\u043c\u0438 \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0437\u0430\u0431\u044b\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0443 <code>Tests<\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/967\/b86\/fbc\/967b86fbc60a640bea8f37681d5512c9.png\" width=\"3424\" height=\"536\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/967\/b86\/fbc\/967b86fbc60a640bea8f37681d5512c9.png\"\/><\/figure>\n<p>\u0418\u043d\u043e\u0433\u0434\u0430 codesandbox \u043d\u0435 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0442\u0435\u0441\u0442\u044b \u043f\u043e\u0441\u043b\u0435 \u0432\u043d\u0435\u0441\u0435\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439. \u041f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u044d\u0442\u043e.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">\/\/ \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432 \u0432\u0438\u0434\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e originFn export const withMemo = (originFn) => {   let cache;  \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f    \/\/ \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438   return () => { \/\/ \u0435\u0441\u043b\u0438 \u043a\u044d\u0448\u0430 \u043d\u0435\u0442, \u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044e \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0435\u0433\u043e     if (cache === undefined) {       cache = originFn();     }  \/\/ \u0432 \u043a\u043e\u043d\u0446\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043a\u044d\u0448\u0430     return cache;   }; };<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u043d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e \u043d\u0435\u0442. \u041e\u0431\u0440\u0430\u0442\u0438\u043c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u0438.<\/p>\n<p>\u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, withMemo \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <a href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/428570\/\" rel=\"noopener noreferrer nofollow\">\u0424\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u0432\u044b\u0441\u0448\u0435\u0433\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0430<\/a>. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043b\u0438\u0431\u043e \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0434\u0440\u0443\u0433\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432 \u0432\u0438\u0434\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430, \u043b\u0438\u0431\u043e \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e. \u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>withMemo<\/code> \u0434\u0435\u043b\u0430\u0435\u0442 \u0438 \u0442\u043e \u0438 \u0434\u0440\u0443\u0433\u043e\u0435.<\/p>\n<p>\u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, \u0432 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0435\u0441\u0442\u044c <a href=\"https:\/\/learn.javascript.ru\/closure\" rel=\"noopener noreferrer nofollow\">\u0437\u0430\u043c\u044b\u043a\u0430\u043d\u0438\u0435<\/a>. \u041e\u043d\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 <code>cache<\/code>. \u0421\u043c\u044b\u0441\u043b \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u0437\u043e\u0432\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u044d\u0442\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f <code>cache<\/code>, \u043d\u043e \u043e\u043d\u0430 \u043d\u0435 \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a withMemo \u043e\u0442\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u0410 \u043d\u0435 \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u043d\u0430 \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e withMemo \u0432\u0435\u0440\u043d\u0443\u043b\u0430 \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0435\u0431\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u044d\u0442\u043e\u0439 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 (\u0442\u043e \u0435\u0441\u0442\u044c \u0438\u043c\u0435\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043d\u0435\u0451). \u041d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441\u0430\u043c \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u044d\u0442\u0443 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>cache<\/code>, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0441 \u043d\u0435\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<p>\u041f\u043e\u043d\u044f\u0442\u0438\u0435 \u0437\u0430\u043c\u044b\u043a\u0430\u043d\u0438\u044f \u0442\u0435\u0441\u043d\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 <a href=\"https:\/\/habr.com\/ru\/post\/517338\/\" rel=\"noopener noreferrer nofollow\">\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u044e \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445<\/a>. \u041e\u0431\u043b\u0430\u0441\u0442\u0438 \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u043b\u043e\u0436\u0435\u043d\u044b \u0434\u0440\u0443\u0433 \u0432 \u0434\u0440\u0443\u0433\u0430 \u0438 \u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0442 \u0434\u0440\u0435\u0432\u043e\u0432\u0438\u0434\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443. \u0412 \u044d\u0442\u043e\u043c \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f.<\/p>\n<\/li>\n<\/ol>\n<h2>\u0410 \u0435\u0441\u043b\u0438 undefined<\/h2>\n<ol>\n<li>\n<p>\u0412 \u043c\u043e\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u0448\u0430\u0433\u0430 \u0434\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>undefined<\/code>. \u0415\u0441\u043b\u0438 \u0432 \u043a\u044d\u0448\u0435 \u043b\u0435\u0436\u0438\u0442 \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0442\u043e \u043a\u044d\u0448 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043f\u0443\u0441\u0442\u044b\u043c. \u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u0441\u0442\u043e \u0431\u044b\u0442\u044c \u0438 \u0441 \u043d\u0438\u043c \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0432 \u043f\u043e\u0434\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432. \u041d\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u0441\u0451 \u0436\u0435 \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u043c \u044d\u0442\u043e\u0442 \u043a\u0440\u0430\u0439\u043d\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(\"'undefined' is valid value\", () => {     const result = undefined;     const fn = jest.fn(() => result);     const memoizedFn = withMemo(fn);      expect(fn).toHaveBeenCalledTimes(0);     expect(memoizedFn()).toBe(result);     expect(fn).toHaveBeenCalledTimes(1);     expect(memoizedFn()).toBe(result);     expect(fn).toHaveBeenCalledTimes(1);   });<\/code><\/pre>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438, \u043f\u0440\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/github.com\/dubzzz\/fast-check\" rel=\"noopener noreferrer nofollow\">fast-check<\/a>, \u043e\u043d\u0430 \u0431\u044b \u0441\u0430\u043c\u0430 \u0443\u043a\u0430\u0437\u0430\u043b\u0430 \u043d\u0430\u043c \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e <code>withMemo<\/code> \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u044d\u0442\u043e\u043c \u043a\u0440\u0430\u0439\u043d\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435. \u041d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u0441\u0432\u043e\u044e \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c .<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const withMemo = (originFn) => {   const cache = {     value: undefined,      isCached: false, \/\/ \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f   };    return () => {     if (!cache.isCached) {       cache.value = originFn();       cache.isCached = true;     }     return cache.value;   }; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/e6d6be554f0143efceb139cc135f7e2efbb8d692\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/e6d6be554f0143efceb139cc135f7e2efbb8d692<\/a><\/p>\n<\/div>\n<\/details>\n<p>\u042f \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u0432\u0451\u043b \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0434\u043b\u044f \u044f\u0432\u043d\u043e\u0433\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u0438\u043b\u0438 \u043d\u0435\u0442.<\/p>\n<\/li>\n<\/ol>\n<h2>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0434\u0438\u043d \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442<\/h2>\n<ol>\n<li>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438, \u0432\u0441\u0451 \u0436\u0435, \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>generateDigitsOfPi<\/code> \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0445\u0430\u0440\u0434\u043a\u043e\u0434\u0438\u0442\u044c \u0447\u0438\u0441\u043b\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439 \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>DIGITS_COUNT<\/code>, \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435  \u043a\u0430\u043a \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 <code>digitsCount<\/code>.<\/p>\n<pre><code class=\"javascript\">function generateDigitsOfPi(digitsCount) {     let q = 1n;     let r = 180n;     let t = 60n;     let i = 2n;     let str = '';     for (let j = 0; j &lt; digitsCount; j++) {         let digit = ((i * 27n - 12n) * q + r * 5n) \/ (t * 5n);         str += digit         let u = i * 3n;         u = (u + 1n) * 3n * (u + 2n);         r = u * 10n * (q * (i * 5n - 2n) + r - t * digit);         q *= 10n * i * (i++ * 2n - 1n);         t *= u;     }      const arr = str.split('')     arr.splice(1, 0, '.')     return arr.join('') }<\/code><\/pre>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js.<\/p>\n<pre><code class=\"javascript\">it(\"should cache depending on argument\", () => {     const factor = Math.random();     const fn = jest.fn((arg) => arg * factor);     const memoizedFn = withMemo(fn);      expect(fn).toHaveBeenCalledTimes(0);      expect(memoizedFn(1)).toBe(factor);     expect(fn).toHaveBeenCalledTimes(1);      expect(memoizedFn(1)).toBe(factor);     expect(fn).toHaveBeenCalledTimes(1);      expect(memoizedFn(2)).toBe(2 * factor);     expect(fn).toHaveBeenCalledTimes(2);      expect(memoizedFn(2)).toBe(2 * factor);     expect(fn).toHaveBeenCalledTimes(2);      expect(memoizedFn(1)).toBe(factor);     expect(fn).toHaveBeenCalledTimes(2);   });<\/code><\/pre>\n<\/li>\n<li>\n<p>\u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a<\/p>\n<details class=\"spoiler\">\n<summary> \u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const withMemo = (originFn) => {   \/\/ \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043a\u044d\u0448\u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442, \u043a\u043b\u044e\u0447\u0430\u043c\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0431\u0443\u0434\u0443\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f   \/\/ \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0443\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0432\u044b\u0437\u043e\u0432\u043e\u0432 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u0442\u043d\u043e\u0439   \/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0432\u0443\u044e\u0449\u0435\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0435   const cache = {};    return (arg) => { \/\/ \u044d\u0442\u0430 \u0437\u0430\u043f\u0438\u0441\u044c \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043c\u0435\u043d\u0435\u0435 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u0430. \u043f\u043e \u0441\u0443\u0442\u0438 \u043e\u043d\u0430 \u043f\u043e\u0445\u043e\u0436\u0430 \u043d\u0430     \/\/ if(!cache[arg])     \/\/ \u043d\u043e \u043f\u0440\u043e\u0439\u0434\u0451\u0442 \u0435\u0441\u043b\u0438 \u0432 \u043a\u044d\u0448 \u0437\u0430\u043f\u0438\u0441\u0430\u043d null, undefined, 0, ''.     if (!(arg in cache)) {       cache[arg] = originFn(arg);     }      return cache[arg];   }; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/f877e175a30a77e3ffa27ea1832573e6f68430c9\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/f877e175a30a77e3ffa27ea1832573e6f68430c9<\/a><\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u043a\u0430 \u043d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e \u0432\u0435\u0434\u044c?<\/p>\n<\/li>\n<\/ol>\n<h2>\u0421\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u044f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432<\/h2>\n<ol>\n<li>\n<p>\u042d\u043a\u0432\u0438\u0432\u0430\u043b\u0435\u043d\u0442\u043d\u044b \u043b\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0434\u0432\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430? \u041d\u0443\u0436\u043d\u043e \u043b\u0438 \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u043c\u0438?<\/p>\n<pre><code class=\"javascript\">const obj1 = { foo: 'bar', } const obj2 = { foo: 'bar', } <\/code><\/pre>\n<p>\u0417\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u0443\u044e \u043e\u043f\u0446\u0438\u044e <code>getKey<\/code> \u0432 \u043d\u0430\u0448\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>withMemo<\/code>, \u0447\u0442\u043e\u0431\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u044b\u043b\u0430 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(\"should cache depending on arg\", () => {     const num = Math.random();     \/\/ \u0441\u043e\u0437\u0434\u0430\u0451\u043c 2 \u0444\u0443\u043d\u043a\u0446\u0438\u0438     const fn1 = jest.fn((arg) => ({ ...arg, num }));     const fn2 = jest.fn((arg) => ({ ...arg, num }));      \/\/ \u0432 \u043f\u0435\u0440\u0432\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435     const memoizedFn1 = withMemo(fn1, {   \/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u043b\u0436\u043d\u0430 \u043e\u043f\u0446\u0438\u0430\u043d\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043d\u0438\u043c\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e getKey       getKey: (arg) => arg,      });      \/\/ \u0432\u043e \u0432\u0442\u043e\u0440\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c\u0443     const memoizedFn2 = withMemo(fn2, {       getKey: (arg) => JSON.stringify(arg),     });     const argA1 = { a: \"a\" };     const argA2 = { a: \"a\" };      memoizedFn1(argA1);     expect(fn1).toHaveBeenCalledTimes(1);     memoizedFn1(argA2);     expect(fn1).toHaveBeenCalledTimes(2);      memoizedFn2(argA1);     expect(fn2).toHaveBeenCalledTimes(1);     memoizedFn2(argA2);     expect(fn2).toHaveBeenCalledTimes(1);   });<\/code><\/pre>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435:<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const withMemo = (originFn, { getKey = (arg) => arg } = {}) => {   \/\/ \u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445 cache \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442 \u0430 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 Map'\u0430.   \/\/ \u0422\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043a\u043b\u044e\u0447\u043e\u043c \u043a\u044d\u0448\u0430 \u043c\u043e\u0433\u043b\u0438 \u0431\u044b\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a\u0438.   \/\/ \u0410 \u0442\u043e \u0440\u0430\u043d\u044c\u0448\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043a\u043b\u044e\u0447\u043e\u043c \u0431\u044b\u043b\u0430 \u0441\u0442\u0440\u043e\u043a\u0430 \"[object Object]\"   const cache = new Map();    return (arg) => {     const cacheKey = getKey(arg); \/\/ \u0440\u0430\u043d\u044c\u0448\u0435 \u043a\u043b\u044e\u0447\u043e\u043c \u0431\u044b\u043b \u0441\u0430\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \/\/ \u0442\u0435\u043f\u0435\u0440\u044c \u0442\u043e, \u0447\u0442\u043e \u0432\u0435\u0440\u043d\u0451\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044f getKey      if (!cache.has(cacheKey)) {       cache.set(cacheKey, originFn(arg));     }      return cache.get(cacheKey);   }; };<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043b\u044f getKey \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u0445 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u044b \u0440\u0430\u0437\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438<\/p>\n<ul>\n<li>\n<p><code>(arg) \u21d2 JSON.stringify(arg)<\/code><\/p>\n<\/li>\n<li>\n<p><code>(arg) \u21d2 stableStringify(arg)<\/code>, \u0433\u0434\u0435 <code>stableStringify<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u0430 <a href=\"https:\/\/www.npmjs.com\/package\/fast-json-stable-stringify\" rel=\"noopener noreferrer nofollow\">fast-json-stable-stringify<\/a>.  \u0421\u0443\u0442\u044c \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0438\u0437 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043d\u0438\u0436\u0435 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e. \u0412\u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u0440\u044f\u0434\u043a\u0430 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439.<\/p>\n<pre><code class=\"javascript\">import stableStringify from 'fast-json-stable-stringify'  const obj1 = {a: 'a', b: 'b'}; const obj2 = {b: 'b', a: 'a'};  JSON.stringify(obj1) === JSON.stringify(obj2) \/\/ false stableStringify(obj1) === stableStringify(obj2) \/\/ true<\/code><\/pre>\n<\/li>\n<\/ul>\n<ul>\n<li>\n<p><code>(arg) \u21d2 hash(arg)<\/code>. \u0413\u0434\u0435 <code>hash<\/code> \u043b\u044e\u0431\u0430\u044f <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A5%D0%B5%D1%88-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F\" rel=\"noopener noreferrer nofollow\">\u0445\u0435\u0448-\u0444\u0443\u043d\u043a\u0446\u0438\u044f<\/a> \u043d\u0430 \u0432\u0430\u0448 \u0432\u044b\u0431\u043e\u0440. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0435\u0437\u043d\u043e, \u0435\u0441\u043b\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u043a\u0430\u043a \u043f\u0440\u0438 <code>JSON.stringify<\/code>, \u043d\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0442\u0430\u043d\u0443\u0442 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043b\u0438\u043d\u043d\u044b\u043c\u0438 \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438 \u0438 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u043f\u0430\u043c\u044f\u0442\u0438. \u0410 \u0445\u0435\u0448 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u043e \u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0434\u043b\u0438\u043d\u044b.<\/p>\n<\/li>\n<li>\n<p><code>(arg) \u21d2 arg<\/code>. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u044f \u0432\u044b\u0431\u0440\u0430\u043b \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d\u043e \u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u0438 \u044d\u0442\u043e \u0441\u0430\u043c\u044b\u0439 \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u0434\u0432\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 &#8212; \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435.<\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<p>\u0425\u043e\u0447\u0443 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e, \u043a\u0430\u043a \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0432\u0442\u043e\u0440\u043e\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0432 withMemo. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u0435\u0439 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0434\u0432\u0430 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430 \u043a\u0430\u043a \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c<\/p>\n<pre><code class=\"javascript\">\/\/ 1) const withMemo = (originFn, getKey = (arg) => arg)  \/\/ 2) const withMemo = (originFn, { getKey = (arg) => arg } = {}) <\/code><\/pre>\n<p>\u041f\u0440\u0438 \u0432\u0442\u043e\u0440\u043e\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435 \u043d\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u043e\u0431\u043d\u0435\u0439 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043e\u043f\u0446\u0438\u0430\u043d\u0430\u043b\u044c\u043d\u044b\u0435  \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044e. \u041a \u0442\u043e\u043c\u0443 \u0436\u0435, \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442\u0441\u044f \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438, \u0447\u0442\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0439 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<hr\/>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e <code>Map<\/code> \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>WeakMap<\/code>. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0430\u043c\u044f\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0438\u0449\u0430\u0442\u044c\u0441\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u0435\u0435. \u041d\u043e \u0442\u043e\u0433\u0434\u0430 \u043c\u044b \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043a\u043b\u044e\u0447\u0430 \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u044b (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0441\u0442\u0440\u043e\u043a\u0438). \u0410 \u044d\u0442\u043e \u043d\u0435 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e. \u0425\u043e\u0440\u043e\u0448\u0438\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u0431\u0443\u0434\u0435\u0442 \u0434\u0430\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u044e\u0437\u0435\u0440\u0443 \u0441\u0430\u043c\u043e\u043c\u0443 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u043a\u044d\u0448. \u0413\u043b\u0430\u0432\u043d\u043e\u0435, \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b \u043d\u0443\u0436\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430<\/p>\n<pre><code class=\"typescript\">interface Map&lt;K, V> {     clear(): void; \/\/ \u043f\u043e\u043a\u0430 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438     delete(key: K): boolean; \/\/ \u043f\u043e\u043a\u0430 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438     get(key: K): V | undefined;     has(key: K): boolean;     set(key: K, value: V): this;     readonly size: number; \/\/ \u043f\u043e\u043a\u0430 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 } <\/code><\/pre>\n<p>\u0418\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0442\u0430\u043a<\/p>\n<pre><code class=\"javascript\">export const withMemo = ( originFn,  {  getKey = (arg) => arg, cache = new Map(), } = {} ) => {   return (arg) => {     const cacheKey = getKey(arg);     if (!cache.has(cacheKey)) {       cache.set(cacheKey, originFn(arg));     }      return cache.get(cacheKey);   }; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/30f1cee7db8a614cad9db9e043398eea2187e419\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/30f1cee7db8a614cad9db9e043398eea2187e419<\/a><\/p>\n<\/li>\n<\/ol>\n<h2>\u041f\u0440\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a API<\/h2>\n<ol>\n<li>\n<p>\u041a\u0430\u043a \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u043f\u043e\u0447\u0443\u0432\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c, \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044f \u043b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0434\u043b\u044f <a href=\"https:\/\/frontend-stuff.com\/blog\/pure-and-impure-functions-in-js\/\" rel=\"noopener noreferrer nofollow\">\u0447\u0438\u0441\u0442\u044b\u0445<\/a> \u0444\u0443\u043d\u043a\u0446\u0438\u0439. \u0412\u044b\u0437\u043e\u0432\u044b \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432\u0440\u044f\u0434 \u043b\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0437\u0432\u0430\u0442\u044c \u0447\u0438\u0441\u0442\u044b\u043c\u0438. \u041d\u043e \u043a\u043e\u0433\u043e \u044d\u0442\u043e \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442? \u0412\u044b\u0437\u043e\u0432\u044b \u043c\u0435\u0442\u043e\u0434\u043e\u0432 api \u0447\u0430\u0441\u0442\u043e \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u0443\u044e\u0442. \u042d\u0442\u043e \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442 \u0442\u0440\u0430\u0444\u0438\u043a \u0438 \u0441\u043b\u0435\u0433\u043a\u0430 \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440. \u041d\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0432\u0430\u043b\u0438\u0442\u044c\u0441\u044f \u043f\u043e \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u043c \u043e\u0442 \u043d\u0430\u0441 \u043f\u0440\u0438\u0447\u0438\u043d\u0430\u043c. \u0418 \u044d\u0442\u043e \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043d\u0438\u0447\u0435\u0433\u043e \u0432 \u043a\u044d\u0448 \u043f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(\"should clear cache on error\", async () => {     const result = Math.random();     let callIndex = 0;     const fn = jest.fn(async () => {       callIndex++;       if (callIndex === 1) return Promise.reject(\"Some error\");       return Promise.resolve(result);     });      const memoizedFn = withMemo(fn, {       cacheRejectedPromise: false     });      await expect(memoizedFn()).rejects.toEqual(\"Some error\");     expect(fn).toHaveBeenCalledTimes(1);      expect(await memoizedFn()).toBe(result);     expect(fn).toHaveBeenCalledTimes(2);      expect(await memoizedFn()).toBe(result);     expect(fn).toHaveBeenCalledTimes(2);   });    it(\"should not clear cache on error\", async () => {     const error = Math.random();     const fn = jest.fn(async () => Promise.reject(error));      const memoizedFn = withMemo(fn, {       cacheRejectedPromise: true     });      await expect(memoizedFn()).rejects.toEqual(error);     expect(fn).toHaveBeenCalledTimes(1);     await expect(memoizedFn()).rejects.toEqual(error);     expect(fn).toHaveBeenCalledTimes(1);   });<\/code><\/pre>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435:<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const withMemo = (   originFn,   {     getKey = (arg) => arg,     cache = new Map(),     cacheRejectedPromise = false, \/\/ \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u043d\u043e\u0432\u0443\u044e \u043e\u043f\u0446\u0438\u044e   } = {} ) => {   return (arg) => {     const cacheKey = getKey(arg);      if (!cache.has(cacheKey)) {       cache.set(cacheKey, originFn(arg));     }      const cachedValue = cache.get(cacheKey);      \/\/ \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0438\u0437 \u043a\u044d\u0448\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u0431\u0443\u0434\u0435\u0442      \/\/ \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u043d\u044b\u0439 \u043f\u0440\u043e\u043c\u0438\u0441     if (!cacheRejectedPromise) {       Promise.resolve(cachedValue).catch(() => {         cache.delete(cacheKey);       });     }      return cachedValue;   }; };<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0425\u043e\u0447\u0443 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0432 \u043a\u044d\u0448\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0431\u0443\u0434\u0435\u0442 \u043d\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u0440\u043e\u043c\u0438\u0441\u0430, \u0430 \u0441\u0430\u043c \u043f\u0440\u043e\u043c\u0438\u0441. \u0412\u044b \u043c\u043e\u0433\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435 \u0442\u0430\u043a\u043e\u0433\u043e:<\/p>\n<pre><code class=\"javascript\">originFn(arg).then(result => cache.set(cacheKey, result)); \/\/ \u0438\u043b\u0438  const result = await constoriginFn(arg); \/\/ \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0440\u043e\u043c\u0438\u0441 :( cache.set(cacheKey, result)); <\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u0432\u0430\u0448 \u0441\u043b\u0443\u0447\u0430\u0439, \u0442\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0435\u0433\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u043a \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0451\u0442 \u0432\u0442\u043e\u0440\u043e\u0439 \u0432\u044b\u0437\u043e\u0432 \u0434\u043e \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u0437\u0430\u0440\u0435\u0437\u043e\u043b\u0432\u0438\u0442\u0441\u044f \u043f\u0440\u043e\u043c\u0438\u0441 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430, \u0442\u043e \u043e\u0442 \u043a\u044d\u0448\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0442\u043e\u043b\u043a\u0443. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u0434\u0432\u0435 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u0445\u0435\u0434\u0435\u0440 \u0438 \u0441\u0430\u0439\u0434\u0431\u0430\u0440) \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044e\u0442 getCurrentUser, \u0442\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0443\u0439\u0434\u0451\u0442 2 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u041d\u043e \u0435\u0441\u043b\u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u043a\u044d\u0448\u0435 \u0441\u0430\u043c \u043f\u0440\u043e\u043c\u0438\u0441, \u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u0431\u0443\u0434\u0435\u0442 \u043e\u0434\u0438\u043d. \u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u044b\u0437\u043e\u0432 \u0432\u043c\u0435\u0441\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u043c\u0438\u0441\u0430, \u0431\u0443\u0434\u0435\u0442 \u0436\u0434\u0430\u0442\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u043c\u0438\u0441\u0430 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430.<\/p>\n<hr\/>\n<p>\u0415\u0449\u0451 \u0445\u043e\u0447\u0443 \u043e\u0431\u044a\u044f\u0441\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u0437\u0430\u043f\u0438\u0441\u044c:<\/p>\n<pre><code class=\"javascript\">Promise.resolve(cachedValue)<\/code><\/pre>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u044d\u0442\u043e\u0433\u043e \u0432\u044b\u0440\u043e\u0436\u0435\u043d\u0438\u044f <strong>\u0432\u0441\u0435\u0433\u0434\u0430<\/strong> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u043c\u0438\u0441. \u0415\u0441\u043b\u0438 <code>cachedValue<\/code> \u0443\u0436\u0435 \u043f\u0440\u043e\u043c\u0438\u0441, \u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f, \u0430 \u0438\u043d\u0430\u0447\u0435 <code>cachedValue<\/code> \u043e\u0431\u0435\u0440\u043d\u0451\u0442\u0441\u044f \u0432 \u043f\u0440\u043e\u043c\u0438\u0441. \u0412 \u0438\u0442\u043e\u0433\u0435 \u043c\u044b \u0432\u0441\u0435\u0433\u0434\u0430 \u043c\u043e\u0436\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u044d\u0442\u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u043a\u0430\u043a \u0441 \u043f\u0440\u043e\u043c\u0438\u0441\u043e\u043c \u0438 \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043b\u0438\u0448\u043d\u0438\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u0439.<\/p>\n<hr\/>\n<p>\u0415\u0449\u0451 \u0445\u043e\u0447\u0443 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c,  \u0447\u0442\u043e <a href=\"https:\/\/developer.mozilla.org\/ru\/docs\/Web\/HTTP\/Caching\" rel=\"noopener noreferrer nofollow\">HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043c\u043e\u0433\u0443\u0442 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f<\/a> \u0438 \u0431\u0435\u0437 \u0442\u0430\u043a\u043e\u0433\u043e \u0432\u043c\u0435\u0448\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430. \u0423\u043c\u043d\u044b\u0435 \u043b\u044e\u0434\u0438 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0451 \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043c\u043d\u043e\u0433\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u0438 \u0442\u0430\u043a.<\/p>\n<\/li>\n<\/ol>\n<h2>\u0411\u043e\u043b\u044c\u0448\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432<\/h2>\n<ol>\n<li>\n<p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043b\u044e\u0434\u0438 \u0441\u0447\u0438\u0442\u0430\u044e\u0442, \u0447\u0442\u043e \u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430. \u0418 \u0443 \u043d\u0438\u0445 \u0434\u0430\u0436\u0435 \u0435\u0441\u0442\u044c \u0445\u043e\u0440\u043e\u0448\u0438\u0435 <em>\u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b<\/em> \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0436\u0435 \u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043a\u0440\u043e\u0435\u043c \u044d\u0442\u043e\u0442 \u0441\u043b\u0443\u0447\u0430\u0439.<\/p>\n<p>\u0421\u0430\u043c\u044b\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u044d\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u043d\u0435 \u043c\u043d\u043e\u0433\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442-\u043c\u0430\u0441\u0441\u0438\u0432. \u0422\u043e\u0433\u0434\u0430 \u0437\u0430\u0434\u0430\u0447\u0443 \u043c\u043e\u0436\u043d\u043e \u0441\u0432\u0435\u0441\u0442\u0438 \u043a \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439, \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0432 \u0432\u0441\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e JSON.stringify. \u041f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"javascript\">export const withMemo = (originFn) => { const cache = new Map();    return (...args) => {     const cacheKey = JSON.stringify(args); \/\/ \u0412\u0441\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0440\u0430\u0437\u043e\u043c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u0435\u043c      if (!cache.has(cacheKey)) {       cache.set(cacheKey, originFn(...args));     }      return cache.get(cacheKey);   }; };<\/code><\/pre>\n<p>\u041d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u043e\u043c \u0442\u0443\u0442 \u0431\u0443\u0434\u0435\u0442 \u0442\u043e, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u043d\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044e \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u043d\u0438\u044f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u0410 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u044d\u0442\u043e \u0441\u0430\u043c\u043e\u0435 \u0431\u044b\u0441\u0442\u0440\u043e\u0435 \u0438 \u043c\u0435\u043d\u0435\u0435 \u0437\u0430\u0442\u0440\u0430\u0442\u043d\u043e\u0435 \u043f\u043e \u043f\u0430\u043c\u044f\u0442\u0438.<\/p>\n<p>\u042f \u0445\u043e\u0447\u0443 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442\u044c 2 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u041f\u0435\u0440\u0432\u0430\u044f \u0438\u0434\u0435\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043a\u044d\u0448 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u043c. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u044e \u043f\u0440\u043e\u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/p>\n<pre><code class=\"javascript\">const sum = (...args) => args.reduce((acc, num) => acc + num, 0)  \/\/ \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043a\u044d\u0448 \u043f\u0443\u0441\u0442\u043e\u0439 cache = {}  sum(4, 6) \/\/ 10 \/\/ \u043a\u044d\u0448 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f cache = { 4: { 6: 10, \/\/ \u0438\u0437 \u044d\u0442\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0441\u043b\u0435\u0434\u0443\u0435\u0442, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0432\u044b\u0439 \u0430\u0440\u0443\u0433\u0443\u043c\u0435\u043d\u0442 4, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 6, \u0442\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0431\u0443\u0434\u0435\u0442 10 } }  sum(4, 8) \/\/ 12 cache = { 4: { 6: 10, 8: 12, } }  sum(2, 1) \/\/ 3 cache = {   2: {     1: 3,   }, 4: { 6: 10, 8: 12, } }  sum(4) \/\/ 4 cache = {   2: {     1: { 1: 4,   },   }, 4: { 6: 10, 8: 12,     '': 4, } }<\/code><\/pre>\n<p>\u0412\u0442\u043e\u0440\u0430\u044f \u0438\u0434\u0435\u044f \u0431\u043e\u043b\u0435\u0435 \u0445\u0438\u0442\u0440\u0430\u044f. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0443\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <a href=\"https:\/\/learn.javascript.ru\/currying-partials\" rel=\"noopener noreferrer nofollow\">\u043a\u0430\u0440\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c<\/a>. \u041a\u0430\u0440\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u2013 \u044d\u0442\u043e \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u043b\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043d\u0435 \u043a\u0430\u043a\u00a0<code>f(a, b, c)<\/code>, \u0430 \u043a\u0430\u043a\u00a0<code>f(a)(b)(c)<\/code>. \u0422\u043e \u0435\u0441\u0442\u044c \u043e\u0434\u043d\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e 1 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043d\u043a\u0446\u0438\u044e \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0442\u043e\u0436\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e 1 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442. \u0410 \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0430\u0448\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>withMemo<\/code> \u0443\u0436\u0435 \u0443\u043c\u0435\u0435\u0442 \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043e\u0434\u043d\u0438\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c, \u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0435\u0451 <a href=\"https:\/\/learn.javascript.ru\/recursion\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e\u0439<\/a>.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js.<\/p>\n<pre><code class=\"javascript\">it(\"multiple arguments\", () => {     const fn = jest.fn((...args) => args.reduce((acc, num) => acc + num, 0));     const memoizedFn = withMemo(fn);      expect(memoizedFn(4, 6)).toBe(10);     expect(fn).toHaveBeenCalledTimes(1);      expect(memoizedFn(4, 6)).toBe(10);     expect(fn).toHaveBeenCalledTimes(1);      expect(memoizedFn(4, 8)).toBe(12);     expect(fn).toHaveBeenCalledTimes(2);      expect(memoizedFn(4)).toBe(4);     expect(fn).toHaveBeenCalledTimes(3);      expect(memoizedFn(4, 6)).toBe(10);     expect(fn).toHaveBeenCalledTimes(3);   });<\/code><\/pre>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c. \u041c\u043e\u0436\u0435\u0442\u0435 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u0438\u0434\u0435\u0439 \u0438\u043b\u0438 \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u0442\u044c \u0441\u0432\u043e\u0451 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u042f \u043d\u0430\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0443\u0441\u0432\u043e\u0435\u043d\u0438\u044f \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0440\u0435\u0448\u0435\u043d\u0438\u0439:<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const withMemo = (   originFn,   {     getKey = (arg) => arg,     getCacheStore = () => new Map(), \/\/ \u0442\u0443\u0442 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435     cacheRejectedPromise = false,   } = {} ) => {   const createSubCache = () => ({     subCaches: getCacheStore(),     result: null,     isCached: false   });    const cache = createSubCache();    return (...args) => {     let currentCache = cache;      for (const arg of args) {       const cacheKey = getKey(arg);       if (!currentCache.subCaches.has(cacheKey)) {         currentCache.subCaches.set(cacheKey, createSubCache());       }        currentCache = currentCache.subCaches.get(cacheKey);     }      if (!currentCache.isCached) {       currentCache.result = originFn(...args);       currentCache.isCached = true;     }      if (!cacheRejectedPromise) {       Promise.resolve(currentCache.result).catch(() => {         currentCache.isCached = false;         currentCache.result = null;       });     }      return currentCache.result;   }; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/30548ae8f47fc40d600493a025d3cfb313a63df0\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/30548ae8f47fc40d600493a025d3cfb313a63df0<\/a><\/p>\n<p>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0441 \u043a\u0430\u0440\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0438 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0435\u0439:<\/p>\n<pre><code class=\"javascript\">\/\/ options \u043d\u0435 \u0440\u0430\u0441\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0437\u0434\u0435\u0441\u044c, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0442\u043e\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0432 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0438 export const withMemo = (originFn, options = {}) => {   const {     getKey = (arg) => arg,     getCacheStore = () => new Map(),     cacheRejectedPromise = false,   } = options;    const cache = {     subFunctions: getCacheStore(),     result: null,     isCached: false   };    return (...args) => {     \/\/ \u042d\u0442\u043e \u0443\u0441\u043b\u043e\u0432\u0438\u043d \u0432\u044b\u0445\u043e\u0434\u0430 \u0438\u0437 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0438 - \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0438\u0441\u044c     if (!args.length) {       if (!cache.isCached) {         cache.result = originFn();         cache.isCached = true;       }        if (!cacheRejectedPromise) {         Promise.resolve(cache.result).catch(() => {           cache.result = null;           cache.isCached = false;         });       }        return cache.result;     }      const [arg, ...otherArgs] = args;     const cacheKey = getKey(arg);      if (!cache.subFunctions.has(cacheKey)) {       cache.subFunctions.set(         cacheKey, \/\/ \u0442\u0443\u0442 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044f         withMemo((...theArgs) => originFn(arg, ...theArgs), options)       );     }      const subFunction = cache.subFunctions.get(cacheKey);      return subFunction(...otherArgs);   }; };<\/code><\/pre>\n<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0435\u0449\u0451 \u043f\u043b\u043e\u0445\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442\u0435 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044e, \u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f, \u0442\u043e \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0432\u0430\u043c \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432\u0442\u043e\u0440\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043d\u0435 \u043f\u043e\u0434\u0433\u043b\u044f\u0434\u044b\u0432\u0430\u044f, \u0442\u043e \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0447\u0438\u0442\u0430\u0442\u044c, \u0447\u0442\u043e \u0432\u044b \u0434\u043e\u0441\u0442\u0438\u0433\u043b\u0438 \u0443\u0441\u043f\u0435\u0445\u0430. \u0415\u0441\u043b\u0438 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0433\u043b\u044f\u0434\u0435\u0442\u044c, \u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044e)<\/p>\n<\/div>\n<\/details>\n<\/li>\n<\/ol>\n<h2>\u0412\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438<\/h2>\n<ol>\n<li>\n<p>\u041c\u044b \u0443\u0436\u0435 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0441 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0435\u0439. \u041e\u0447\u0435\u043d\u044c \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0438 \u0438\u0434\u0435\u0438, \u043a\u0430\u043a \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435. \u041d\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0435\u0449\u0451 \u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f.<\/p>\n<blockquote>\n<p><em>\u201c\u0412 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u043d\u043e\u0439 \u043d\u0430\u0443\u043a\u0435 \u0435\u0441\u0442\u044c \u0434\u0432\u0435 \u0442\u0440\u0443\u0434\u043d\u044b\u0435 \u0432\u0435\u0449\u0438: \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u044d\u0448\u0430, \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u0435\u0449\u0435\u0439 \u0438 \u043e\u0448\u0438\u0431\u043a\u0438 \u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0443\u201d, \u2014 \u041b\u0435\u043e\u043d \u0411\u0430\u043c\u0431\u0440\u0438\u043a.<\/em><\/p>\n<\/blockquote>\n<p>\u0420\u0430\u0437 \u043c\u044b \u043d\u0430\u0447\u0430\u043b\u0438 \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u043d\u0435\u0447\u0438\u0441\u0442\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043a\u0430\u043a \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u0430\u043f\u0438, \u0442\u043e \u043d\u0430\u0434\u043e \u043f\u043e\u0437\u0430\u0431\u043e\u0442\u0438\u0442\u044c\u0441\u044f \u043e\u0431 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043a\u044d\u0448\u0430. \u0422\u043e \u0435\u0441\u0442\u044c \u043e \u0435\u0433\u043e \u043e\u0447\u0438\u0449\u0435\u043d\u0438\u0438.<\/p>\n<p><a href=\"http:\/\/highscalability.com\/blog\/2016\/1\/25\/design-of-a-modern-cache.html\" rel=\"noopener noreferrer nofollow\">\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a> \u0435\u0441\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0435 \u0442\u0435\u0445\u043d\u0438\u043a\u0438 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0435\u0433\u043e \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438. \u041c\u044b \u0437\u0430\u0439\u043c\u0451\u043c\u0441\u044f \u043f\u043e\u043a\u0430 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u0432\u0435\u0449\u0430\u043c\u0438. \u0410 \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u043d\u0430\u043c \u0432\u043e\u043e\u0431\u0449\u0435 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u044d\u0448? \u041e\u0434\u043d\u0430 \u0438\u0437 \u043f\u0440\u0438\u0447\u0438\u043d &#8212; \u043f\u0440\u043e\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0438 \u0432 \u043a\u044d\u0448\u0435 \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435. \u0414\u0440\u0443\u0433\u0430\u044f \u043f\u0440\u0438\u0447\u0438\u043d\u0430 &#8212; \u043a\u044d\u0448 \u043d\u0430\u0447\u0430\u043b \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u043f\u0430\u043c\u044f\u0442\u0438.<\/p>\n<p>\u041e\u0434\u043d\u0438\u043c \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0436\u0438\u0437\u043d\u0438 \u043a\u044d\u0448\u0430. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u0443\u044e \u043e\u043f\u0446\u0438\u044e ttl (Time to live) \u043a \u043d\u0430\u0448\u0435\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(\"ttl\", async () => {     const fn = jest.fn();     const memoizedFn = withMemo(fn, { ttl: 1000 }); \/\/ \u0432 \u043c\u0438\u043b\u0438\u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445      memoizedFn();     expect(fn).toHaveBeenCalledTimes(1);     await wait(500);      memoizedFn();     expect(fn).toHaveBeenCalledTimes(1);     await wait(500);      memoizedFn();     expect(fn).toHaveBeenCalledTimes(2);   });<\/code><\/pre>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const withMemo = (   originFn,   {     getKey = (arg) => arg,     getCacheStore = () => new Map(),     cacheRejectedPromise = false,     ttl   } = {} ) => {   const createSubCache = () => ({     subCaches: getCacheStore(),     result: null,     isCached: false,     invalidationTimeoutId: null,   });    const cache = createSubCache();    const invalidateByCache = (theCache) => {     theCache.isCached = false;     theCache.result = null;     clearTimeout(theCache.invalidationTimeoutId);   };    return (...args) => {     let currentCache = cache;      for (const arg of args) {       const cacheKey = getKey(arg);       if (!currentCache.subCaches.has(cacheKey)) {         currentCache.subCaches.set(cacheKey, createSubCache());       }        currentCache = currentCache.subCaches.get(cacheKey);     }      if (!currentCache.isCached) {       currentCache.result = originFn(...args);       currentCache.isCached = true;        \/\/ \u0441\u0442\u0430\u0432\u0438\u043c \u0442\u0430\u0439\u043c\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u043a\u044d\u0448       if (ttl != null) {         currentCache.invalidationTimeoutId = setTimeout(           () => invalidateByCache(currentCache),           ttl         );       }     }      if (!cacheRejectedPromise) {       Promise.resolve(currentCache.result).catch(() =>         invalidateByCache(currentCache)       );     }      return currentCache.result;   }; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/70f90c3fa3e8f8f71126a132fc949b1441683eb2\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/70f90c3fa3e8f8f71126a132fc949b1441683eb2<\/a><\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u043a\u044d\u0448\u0430 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 2\u0445 \u043c\u0435\u0441\u0442\u0430\u0445 (\u043f\u043e \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u0443 \u0438 \u043f\u0440\u0438 \u043e\u0442\u043a\u043b\u043e\u043d\u0451\u043d\u043d\u043e\u043c \u043f\u0440\u043e\u043c\u0438\u0441\u0435). \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044d\u0442\u0430 \u043b\u043e\u0433\u0438\u043a\u0430 \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>invalidateByCache<\/code>.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u043d\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u043a\u044d\u0448\u0430 \u0432\u0432\u0435\u0440\u0445 \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443. \u041d\u043e \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c. \u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"javascript\">\/\/ \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043c\u0435\u043c\u043e\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0441\u0443\u043c\u043c\u044b \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 memoizedSum(4, 6, 5) \/\/ 15 memoizedSum(4, 6, 10) \/\/ 20  \/\/ \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u0438\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 \u043a\u044d\u0448 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a cache == {  4: { 6: { 5: 15, 10: 20, } } }  \/\/ \u043f\u043e\u0441\u043b\u0435 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043a\u044d\u0448\u0430 \u0434\u043b\u044f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 (4, 6, 5), \/\/ \u043e\u0431\u0449\u0438\u0439 \u043a\u044d\u0448 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a cache == {  4: { 6: { 10: 20, } } }  \/\/ \u043f\u043e\u0441\u043b\u0435 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043a\u044d\u0448\u0430 \u0434\u043b\u044f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 (4, 6, 10), \/\/ \u043e\u0431\u0449\u0438\u0439 \u043a\u044d\u0448 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a cache == {  4: { 6: { } } }  \/\/ \u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0435\u0439 \u0431\u044b\u043b\u043e \u0431\u044b \u0442\u0430\u043a cache == {}<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<\/li>\n<\/ol>\n<h2>\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440<\/h2>\n<ol>\n<li>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438 \u043d\u0430\u0448\u0435\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439. \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0442\u044c \u043c\u044b \u0431\u0443\u0434\u0435\u043c, \u043f\u0440\u0430\u0432\u0434\u0430, \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0432 \u043a\u044d\u0448\u0435, \u0430 \u043d\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043d\u044f\u0442\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u0432 \u0431\u0430\u0439\u0442\u0430\u0445 (\u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u043c\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u043d\u0435 \u0442\u0430\u043a\u0430\u044f \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430, \u043a\u0430\u043a \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f). \u041d\u043e \u044d\u0442\u0438 \u0434\u0432\u0430 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044f \u043e\u0431\u044b\u0447\u043d\u043e \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u043a\u043e\u0440\u0440\u0435\u043b\u0438\u0440\u0443\u044e\u0442, \u0442\u0430\u043a \u0447\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u0445\u0432\u0430\u0442\u0438\u0442. \u0417\u0430\u0432\u0435\u0434\u0451\u043c \u043d\u043e\u0432\u0443\u044e \u043e\u043f\u0446\u0438\u044e <code>maxSize<\/code> \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u041f\u043e\u0441\u043b\u0435 \u0434\u043e\u0441\u0442\u0438\u0436\u0435\u043d\u0438\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0432 \u043a\u044d\u0448\u0435, \u043f\u0440\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438, \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u043e\u0434\u043d\u0443 \u0438\u0437 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439. \u041d\u0443\u0436\u043d\u043e \u0432\u044b\u0431\u0440\u0430\u0442\u044c <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%BA%D1%8D%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044e \u0432\u044b\u0442\u0435\u0441\u043d\u0435\u043d\u0438\u044f \u043a\u044d\u0448\u0430<\/a>. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <em>\u0441\u0430\u043c\u043e\u0433\u043e \u0434\u0430\u0432\u043d\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e<\/em> \u0438\u043b\u0438 \u0441\u0430\u043c\u043e\u0433\u043e \u0440\u0435\u0434\u043a\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u043d\u044c\u0448\u0435 \u0432\u0441\u0435\u0445 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e. \u0421\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0439 \u043c\u043d\u043e\u0433\u043e \u0438 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0438 \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0439 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0440\u0430\u0437\u043d\u044b\u0435.<\/p>\n<p>\u0414\u0430\u0436\u0435 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f MRU (Most Recently Used &#8212; \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043d\u0435\u0434\u0430\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0432\u0448\u0438\u0439\u0441\u044f) \u0438\u043d\u043e\u0433\u0434\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0443\u0447\u0448\u0438\u043c \u0432\u044b\u0431\u043e\u0440\u043e\u043c.<\/p>\n<ul>\n<li>\n<blockquote>\n<p>\u041a\u043e\u0433\u0434\u0430 \u0444\u0430\u0439\u043b \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043a\u0430\u043d\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u043e \u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u0445\u0435\u043c\u0435, MRU (Most Recently Used &#8212; \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043d\u0435\u0434\u0430\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0432\u0448\u0438\u0439\u0441\u044f) \u2014 \u043d\u0430\u0438\u043b\u0443\u0447\u0448\u0438\u0439 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0432\u044b\u0442\u0435\u0441\u043d\u0435\u043d\u0438\u044f.<\/p>\n<\/blockquote>\n<p>\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a Hong-Tai Chou&#187;. David J. Dewitt:\u00a0<a href=\"http:\/\/www.vldb.org\/conf\/1985\/P127.PDF\" rel=\"noopener noreferrer nofollow\">http:\/\/www.vldb.org\/conf\/1985\/P127.PDF<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u042f \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c LRU (Least recently used &#8212; \u0412\u044b\u0442\u0435\u0441\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u0432\u043d\u043e \u043d\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445) \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044e \u0432\u044b\u0442\u0435\u0441\u043d\u0435\u043d\u0438\u044f \u043a\u044d\u0448\u0430, \u043d\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044e. \u0414\u043b\u044f \u043b\u044e\u0431\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430\u043c \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443 \u043f\u043e \u0432\u044b\u0437\u043e\u0432\u0430\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0439. \u0414\u043b\u044f \u043e\u0431\u0449\u0435\u0433\u043e \u043a\u0435\u0439\u0441\u0430 \u043d\u0430\u043c \u0445\u0432\u0430\u0442\u0438\u0442 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0442\u0430\u0439\u043c\u0441\u0442\u0435\u043c\u043f\u043e\u0432 \u0445\u0438\u0442\u043e\u0432 (hit &#8212; \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u044d\u0448\u0430). \u0422\u043e \u0435\u0441\u0442\u044c \u043d\u0430\u0448\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u0430\u043c\u044f\u0442\u0438, \u0447\u0442\u043e\u0431\u044b \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443. \u041d\u0443 \u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u044b\u0442\u0435\u0441\u043d\u044f\u0435\u043c\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c \u043f\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438. \u0422\u0430\u043a \u0447\u0442\u043e, \u0435\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u0442\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0442\u043e \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u044d\u0442\u0443 \u0444\u0438\u0447\u0443 \u0431\u0435\u0437 \u043d\u0430\u0434\u043e\u0431\u043d\u043e\u0441\u0442\u0438. \u0418\u043d\u0430\u0447\u0435 \u0432\u0430\u0448\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0443\u0436 \u0442\u043e\u0447\u043d\u043e \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u043d\u0430\u0437\u0432\u0430\u0442\u044c \u201c\u0441\u0430\u043c\u043e\u0439 \u0431\u044b\u0441\u0442\u0440\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438\u201d.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(\"LRU\", async () => {     const fn = jest.fn((...args) => args.join(\"-\"));     const memoizedFn = withMemo(fn, {       cacheReplacementPolicy: {         maxSize: 3       }     });      memoizedFn(1, 2);     await wait(0);     memoizedFn(1, 3);     await wait(0);     memoizedFn(2, 3);     await wait(0);     memoizedFn(1, 2);     expect(fn).toHaveBeenCalledTimes(3);     await wait(0);     memoizedFn(3, 3); \/\/ replace cache for (1, 3)     expect(fn).toHaveBeenCalledTimes(4);     await wait(0);     memoizedFn(1, 2);     expect(fn).toHaveBeenCalledTimes(4);     await wait(0);     memoizedFn(2, 3);     expect(fn).toHaveBeenCalledTimes(4);     await wait(0);     memoizedFn(3, 3);     expect(fn).toHaveBeenCalledTimes(4);     await wait(0);     memoizedFn(1, 3); \/\/ replace cache for (1, 2)     expect(fn).toHaveBeenCalledTimes(5);   });<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0434 \u043a\u0430\u0436\u0434\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u043e\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u044f \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b <code>await wait(0);<\/code> \u0447\u0442\u043e\u0431\u044b \u0442\u0430\u0439\u043c\u0441\u0442\u0435\u043c\u043f\u044b \u0445\u0438\u0442\u043e\u0432 \u043e\u0442\u043b\u0438\u0447\u0430\u043b\u0438\u0441\u044c. \u0418\u043d\u0430\u0447\u0435 \u0443 \u0432\u0441\u0435\u0445 \u0445\u0438\u0442\u043e\u0432 \u0431\u044b\u043b\u043e \u0431\u044b \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0438 \u043c\u044b \u0431\u044b \u043d\u0435 \u0441\u043c\u043e\u0433\u043b\u0438 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u0441\u0430\u043c\u044b\u0439 \u0434\u0430\u0432\u043d\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u043a\u044d\u0448. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u044d\u0442\u043e \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442. \u0442\u043e \u043c\u043e\u0436\u043d\u043e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0445\u0438\u0442\u0430\u0445 \u043d\u0435 \u043c\u0430\u0441\u0441\u0438\u0432 \u0442\u0430\u0439\u043c\u0441\u0442\u0435\u043c\u043f\u043e\u0432, \u0430 \u043c\u0430\u0441\u0441\u0438\u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0442\u0438\u043f\u0430 {index, timestamp}. index \u0443\u0436 \u0442\u043e\u0447\u043d\u043e \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c\u0441\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const cacheReplacementStrategies = { \/\/ \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 \u0432\u044b\u0442\u0435\u0441\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u0432\u043d\u043e \u043d\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u043a\u0435\u0448\u0435\u0439   lru: (itemsSet) => {     const asArray = Array.from(itemsSet)       .sort((a, b) => a.hits.at(-1) - b.hits.at(-1)) \/\/ \u044f \u0440\u0435\u0448\u0438\u043b, \u0447\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u041c\u0430\u0441\u0441\u0438\u0432 \u043a\u044d\u0448\u0435\u0439 \u0434\u043b\u044f \u0432\u044b\u0442\u0435\u0441\u043d\u0435\u043d\u0438\u044f.   \/\/ \u0438\u043d\u043e\u0433\u0434\u0430, \u0432 \u0446\u0435\u043b\u044f\u0445 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438, \u043b\u0443\u0447\u0448\u0435 \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u044c \u0441\u0440\u0430\u0437\u0443 \u043c\u043d\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 10%)     \/\/ \u0447\u0435\u043c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0447\u0430\u0441\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e     return asArray.slice(0, 1); \/\/ \u0435\u0441\u043b\u0438 \u0445\u043e\u0442\u0438\u043c \u043e\u0447\u0438\u0441\u0442\u0438\u0442\u044c 10% \u043e\u0442 \u0432\u0441\u0435\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \/\/ return asArray.slice(0, Math.round(asArray.length * 0.1);   } };  export const withMemo = (   originFn,   {     getKey = (arg) => arg,     getCacheStore = () => new Map(),     cacheRejectedPromise = false,     ttl, \/\/ \u043d\u043e\u0432\u0430\u044f \u043e\u043f\u0446\u0438\u044f. \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c \u0441\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 maxSize \u0438 strategy     cacheReplacementPolicy = null   } = {} ) => {   if (cacheReplacementPolicy &amp;&amp; !cacheReplacementPolicy.maxSize) {     throw new Error(\"maxSize is mandatory for cacheReplacementPolicy\");   }    const createSubCache = () => ({     subCaches: getCacheStore(),     result: null,     isCached: false,     invalidationTimeoutId: null,     hits: [], \/\/ \u043d\u043e\u0432\u043e\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043a\u044d\u0448\u0430   });    const rootCache = createSubCache();  \/\/ \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u043f\u043b\u043e\u0441\u043a\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0432\u0441\u0435\u0445 \u043a\u0435\u0448\u0435\u0439 (\u043d\u0435 \u0434\u0435\u0440\u0435\u0432\u043e), \u0447\u0442\u043e\u0431\u044b \u0441\u0440\u0430\u0437\u0443 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c   \/\/ \u0432\u0441\u0435 \u043a\u0435\u0448\u0438 \u0432 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u0435\u0448\u0430.     \/\/ allCacheRecords \u043c\u043e\u0433 \u0431\u044b \u0431\u044b\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u043c \u0432\u043c\u0435\u0441\u0442\u043e set'\u0430.    \/\/ \u043d\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0438\u0437 \u0441\u0435\u0440\u0435\u0434\u0438\u043d\u044b \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f, \u043f\u043e\u0442\u043e\u043c\u0443    \/\/ \u0447\u0442\u043e \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0441\u0434\u0432\u0438\u0433\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0443.    \/\/ allCacheRecords \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0448\u0442\u0443\u043a\u0430. \u0415\u0441\u043b\u0438 \u0435\u0451 \u043d\u0435 \u0437\u0430\u0432\u043e\u0434\u0438\u0442\u044c   \/\/ \u0442\u043e \u043f\u0435\u0440\u0435\u0434 \u0432\u044b\u0437\u043e\u0432\u043e\u043c \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 \u0432\u044b\u0442\u0435\u0441\u043d\u0435\u043d\u0438\u044f \u043a\u0435\u0448\u0430 \u043d\u0430\u0434\u043e \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u043f\u043e \u0432\u0441\u0435\u043c\u0443   \/\/ \u0434\u0435\u0440\u0435\u0432\u0443 \u043a\u044d\u0448\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435 \u0437\u0430\u043f\u0438\u0441\u0438.   \/\/ \u041a\u0430\u043a\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043b\u0443\u0447\u0448\u0435 - \u0441\u043f\u043e\u0440\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441   const allCacheRecords = new Set();     const invalidateByCache = (theCache) => {     clearTimeout(theCache.invalidationTimeoutId);     theCache.isCached = false;     theCache.result = null;     theCache.invalidationTimeoutId = null;     \/\/ \u0434\u0432\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u0440\u0438 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043a\u044d\u0448\u0430     theCache.hits = [];     allCacheRecords.delete(theCache);   };    return (...args) => {     let currentCache = rootCache;      for (const arg of args) {       const cacheKey = getKey(arg);       if (!currentCache.subCaches.has(cacheKey)) {         currentCache.subCaches.set(cacheKey, createSubCache());       }        currentCache = currentCache.subCaches.get(cacheKey);     }      if (!currentCache.isCached) { \/\/ \u0435\u0441\u043b\u0438 \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u044d\u0448\u0430 \u043f\u0435\u0440\u0435\u043f\u043e\u043b\u043d\u0435\u043d \u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043e cacheReplacementPolicy       \/\/ \u0442\u043e \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u043c \u0447\u0430\u0441\u0442\u044c \u043a\u044d\u0448\u0430       if (cacheReplacementPolicy) {         const {           maxSize,           strategy = cacheReplacementStrategies.lru         } = cacheReplacementPolicy;         if (allCacheRecords.size >= maxSize) {           const cachesToReplace = strategy(allCacheRecords);           cachesToReplace.forEach(invalidateByCache);         }       }        currentCache.result = originFn(...args);       currentCache.isCached = true;        if (ttl != null) {         currentCache.invalidationTimeoutId = setTimeout(           () => invalidateByCache(currentCache),           ttl         );       } \/\/ \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043d\u043e\u0432\u044b\u0439 \u043a\u044d\u0448 \u0432 allCacheRecords       allCacheRecords.add(currentCache);     }     \/\/ \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 hit     currentCache.hits.push(+new Date());      if (!cacheRejectedPromise) {       Promise.resolve(currentCache.result).catch(() =>         invalidateByCache(currentCache)       );     }      return currentCache.result;   }; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/1a5809cb1bd769e3e49cb5fe5abccb4354fbe2ce\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/1a5809cb1bd769e3e49cb5fe5abccb4354fbe2ce<\/a><\/p>\n<p>\u0425\u043e\u0447\u0443 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u044d\u0448\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043c\u0435\u043d\u044c\u0448\u0435, \u0447\u0435\u043c allCacheRecords.size. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0432 <code>getCacheStore<\/code> \u0447\u0442\u043e-\u0442\u043e, \u0447\u0442\u043e \u0432\u0435\u0440\u043d\u0451\u0442 <code>WeakMap<\/code> \u0442\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u043a\u044d\u0448\u0430 \u043c\u043e\u0433\u0443\u0442 \u0443\u0445\u043e\u0434\u0438\u0442\u044c \u043c\u0438\u043d\u0443\u044f \u043d\u0430\u0448 <code>invalidateByCache<\/code>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u0432\u044b\u0442\u0435\u0441\u043d\u0438\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u043a\u044d\u0448\u0430, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u0445\u043e\u0442\u0435\u0442\u044c \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e \u043f\u0440\u043e\u0439\u0442\u0438\u0441\u044c \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443 \u0438 \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u044d\u0448\u0430.<\/p>\n<\/div>\n<\/details>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435, \u0442\u043e \u044f \u0431\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u043e\u0432\u0430\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>strategy<\/code> \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c, \u0430 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c lru \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u0435\u0441\u043b\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438. \u0422\u0430\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0431\u0443\u0434\u0443\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043e\u0441\u043e\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440. \u041d\u043e \u043d\u0435 \u043d\u0430\u0434\u043e \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u043e\u043b\u043b\u0435\u0433 \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438. \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043d\u0430\u0431\u043e\u0440 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043d\u0438 \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<\/ol>\n<h2>\u0420\u0443\u0447\u043d\u0430\u044f \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043a\u044d\u0448\u0430<\/h2>\n<ol>\n<li>\n<p>\u041c\u044b \u043d\u0430\u0443\u0447\u0438\u043b\u0438 \u043d\u0430\u0448\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0442\u044c \u043a\u044d\u0448 \u043f\u043e \u0440\u044f\u0434\u0443 \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432. \u041d\u043e \u0438 \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u0445\u0432\u0430\u0442\u0438\u0442\u044c. \u0418\u043d\u043e\u0433\u0434\u0430 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u044d\u0448 \u0434\u043b\u044f \u043a\u0430\u043a\u0438\u0445-\u0442\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438\u043b\u0438, \u0438 \u0432\u043e\u0432\u0441\u0435, \u0432\u043e\u043e\u0431\u0449\u0435 \u0432\u0435\u0441\u044c \u043a\u044d\u0448 \u0441\u0431\u0440\u043e\u0441\u0438\u0442\u044c (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043e\u0433\u0434\u0430 \u044e\u0437\u0435\u0440 \u0440\u0430\u0437\u043b\u043e\u0433\u0438\u043d\u0438\u043b\u0441\u044f).<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(\"invalidateCache\", () => {     const fn = jest.fn();     const memoizedFn = withMemo(fn);      memoizedFn(1, 2);     memoizedFn(1, 3);     memoizedFn.invalidateCache();     expect(fn).toHaveBeenCalledTimes(2);     memoizedFn(1, 3);     expect(fn).toHaveBeenCalledTimes(3);     memoizedFn(1, 2);     expect(fn).toHaveBeenCalledTimes(4);   });    it(\"invalidateCacheByArgs\", () => {     const fn = jest.fn();     const memoizedFn = withMemo(fn);      memoizedFn(1, 2);     memoizedFn(1, 3);     memoizedFn.invalidateCacheByArgs(1, 2);     expect(fn).toHaveBeenCalledTimes(2);     memoizedFn(1, 3);     expect(fn).toHaveBeenCalledTimes(2);     memoizedFn(1, 2);     expect(fn).toHaveBeenCalledTimes(3);   });<\/code><\/pre>\n<p>\u0425\u043e\u0447\u0443 \u043d\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c, \u0447\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432 JS \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438. \u0422\u043e \u0435\u0441\u0442\u044c \u0432 \u043d\u0438\u0445, \u043a\u0430\u043a \u0438 \u0432\u043e \u0432\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043c\u043e\u0436\u043d\u043e \u043f\u043e \u043a\u043b\u044e\u0447\u0443 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>obj.key = value<\/code>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 <code>memoizedFn.invalidateCacheByArgs(1, 2);<\/code> \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u0432\u0430\u043b\u0438\u0434\u043d\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const cacheReplacementStrategies = {   lru: (itemsSet) => {     const asArray = Array.from(itemsSet)       .sort((a, b) => a.hits.at(-1) - b.hits.at(-1))     return asArray.slice(0, 1);   } };  export const withMemo = (   originFn,   {     getKey = (arg) => arg,     getCacheStore = () => new Map(),     cacheRejectedPromise = false,     ttl,     cacheReplacementPolicy = null   } = {} ) => {   if (cacheReplacementPolicy &amp;&amp; !cacheReplacementPolicy.maxSize) {     throw new Error(\"maxSize is mandatory \");   }    const createSubCache = () => ({     subCaches: getCacheStore(),     result: null,     isCached: false,     invalidationTimeoutId: null,     hits: []   });    const rootCache = createSubCache();   const allCacheRecords = new Set();    const invalidateByCache = (theCache) => {     clearTimeout(theCache.invalidationTimeoutId);     theCache.isCached = false;     theCache.result = null;     theCache.invalidationTimeoutId = null;     theCache.hits = [];     allCacheRecords.delete(theCache);   };  \/\/ \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0441\u0440\u0430\u0437\u0443 \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0430   \/\/ \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0435\u0451 \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0442\u043e\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432   const memoizedFn = (...args) => {     let currentCache = rootCache;      for (const arg of args) {       const cacheKey = getKey(arg);       if (!currentCache.subCaches.has(cacheKey)) {         currentCache.subCaches.set(cacheKey, createSubCache());       }        currentCache = currentCache.subCaches.get(cacheKey);     }      if (!currentCache.isCached) {       if (cacheReplacementPolicy) {         const {           maxSize,           strategy = cacheReplacementStrategies.lru         } = cacheReplacementPolicy;         if (allCacheRecords.size >= maxSize) {           const cachesToReplace = strategy(allCacheRecords);           cachesToReplace.forEach(invalidateByCache);         }       }       currentCache.result = originFn(...args);       currentCache.isCached = true;        if (ttl != null) {         currentCache.invalidationTimeoutId = setTimeout(           () => invalidateByCache(currentCache),           ttl         );       }       allCacheRecords.add(currentCache);     }     currentCache.hits.push(+new Date());      if (!cacheRejectedPromise) {       Promise.resolve(currentCache.result).catch(() =>         invalidateByCache(currentCache)       );     }      return currentCache.result;   };    \/\/ \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0430\u0448\u0438 \u0434\u0432\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u043a\u044d\u0448\u0430   memoizedFn.invalidateCache = () => { if (ttl != null) {       allCacheRecords.forEach( cacheData => clearTimeout(cacheData.invalidationTimeoutId) )     }     allCacheRecords.clear();     Object.assign(rootCache, createSubCache());   };    memoizedFn.invalidateCacheByArgs = (...args) => {     let currentCache = rootCache;      for (const arg of args) {       const cacheKey = getKey(arg);        currentCache = currentCache.subCaches.get(cacheKey);       if (!currentCache) return;     }      invalidateByCache(currentCache);   };  \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438   return memoizedFn; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/cac2b38ba6952a9a558a5e23727d3deb1474ed5c\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/cac2b38ba6952a9a558a5e23727d3deb1474ed5c<\/a><\/p>\n<\/div>\n<\/details>\n<\/li>\n<\/ol>\n<h2>\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442<\/h2>\n<ol>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 <a href=\"https:\/\/learn.javascript.ru\/object-methods#klyuchevoe-slovo-this-v-metodah\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430<\/a> (\u043e\u0442 <code>this<\/code>), \u0442\u043e \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u0432\u044b \u0434\u0435\u043b\u0430\u0435\u0442\u0435 \u0447\u0442\u043e-\u0442\u043e \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e. \u041d\u043e<\/p>\n<p>     a. \u0412\u0441\u044f\u043a\u0438\u0435 \u0431\u044b\u0432\u0430\u044e\u0442 \u0441\u043b\u0443\u0447\u0430\u0438.<\/p>\n<p>     b. \u0414\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0446\u0435\u043b\u0435\u0439 \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a \u0438 \u0432\u0430\u0436\u043d\u043e.<\/p>\n<p>\u0422\u0430\u043a \u0447\u0442\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0443\u0447\u0438\u043c \u043d\u0430\u0448\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0430\u0434\u0435\u043a\u0432\u0430\u0442\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043d\u0430\u0448\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u043f\u0446\u0438\u044e <code>getContextKey<\/code> \u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 <code>getKey<\/code>. \u0415\u0441\u043b\u0438 \u044d\u0442\u0430 \u043e\u043f\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u0430, \u0442\u043e \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u0435\u0449\u0451 \u043e\u0434\u043d\u0438\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u0441\u0432\u043e\u0434\u0438\u043c \u0437\u0430\u0434\u0430\u0447\u0443 \u043a \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(\"context\", () => {     class Point {       constructor(x, y) {         this.x = x;         this.y = y;       }              doubleX() {         return this.x * 2       }     }     const fn = jest.fn(Point.prototype.doubleX);     Point.prototype.doubleX = withMemo(fn, {       getContextKey: (context) => context.x     });      const p1 = new Point(3, 100);     const p2 = new Point(5, 50);      expect(p1.doubleX()).toBe(6);     expect(fn).toHaveBeenCalledTimes(1);     expect(p1.doubleX()).toBe(6);     expect(fn).toHaveBeenCalledTimes(1);     expect(p2.doubleX()).toBe(10);     expect(fn).toHaveBeenCalledTimes(2);     p2.x = 3;     expect(p2.doubleX()).toBe(6);     expect(fn).toHaveBeenCalledTimes(2);     p2.x = 33;     expect(p2.doubleX()).toBe(66);     expect(fn).toHaveBeenCalledTimes(3);   });    it(\"invalidate with context\", () => {     const fn = jest.fn();     const memoizedFn = withMemo(fn, {       getContextKey: (context) => context     });     class Dummy {}     Dummy.prototype.memoizedFn = memoizedFn;      const obj1 = new Dummy();     const obj2 = new Dummy();      obj1.memoizedFn(2);     obj1.memoizedFn(3);     obj1.memoizedFn(3);     expect(fn).toHaveBeenCalledTimes(2);     obj2.memoizedFn(2);     obj2.memoizedFn(3);     expect(fn).toHaveBeenCalledTimes(4);     obj1.memoizedFn.invalidateCacheByContextAndArgs(obj1, 3);     obj2.memoizedFn(3);     expect(fn).toHaveBeenCalledTimes(4);     obj1.memoizedFn(2);     expect(fn).toHaveBeenCalledTimes(4);     obj1.memoizedFn(3);     expect(fn).toHaveBeenCalledTimes(5);   });<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u043b\u0438 \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 <code>invalidateCacheByContextAndArgs<\/code>. \u041c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>invalidateCacheByArgs<\/code> \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0432 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0432 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const cacheReplacementStrategies = {   lru: (itemsSet) => {     const asArray = Array.from(itemsSet)       .sort((a, b) => a.hits.at(-1) - b.hits.at(-1))      return asArray.slice(0, 1);   } };  export const withMemo = (   originFn,   {     getKey = (arg) => arg,     getContextKey = null, \/\/ \u043d\u043e\u0432\u0430\u044f \u043e\u043f\u0446\u0438\u044f     getCacheStore = () => new Map(),     cacheRejectedPromise = false,     ttl,     cacheReplacementPolicy = null   } = {} ) => {   if (cacheReplacementPolicy &amp;&amp; !cacheReplacementPolicy.maxSize) {     throw new Error(\"maxSize is mandatory \");   }    const createSubCache = () => ({     subCaches: getCacheStore(),     result: null,     isCached: false,     invalidationTimeoutId: null,     hits: []   });    const rootCache = createSubCache();   const allCacheRecords = new Set();    const invalidateByCache = (theCache) => {     clearTimeout(theCache.invalidationTimeoutId);     theCache.isCached = false;     theCache.result = null;     theCache.invalidationTimeoutId = null;     theCache.hits = [];     allCacheRecords.delete(theCache);   };  \/\/ \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u044b\u0437\u043e\u0432\u0430 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c    \/\/ \u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u0443\u044e   const memoizedFn = function (...args) {     let currentCache = rootCache;  \/\/ \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 getContextKey, \u0442\u043e \u0432 \u043a\u043e\u0440\u043d\u0435 \u043a\u044d\u0448\u0430 \u043b\u0435\u0436\u0430\u0442     \/\/ \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430     if (getContextKey) {       const cacheKey = getContextKey(this);       if (!currentCache.subCaches.has(cacheKey)) {         currentCache.subCaches.set(cacheKey, createSubCache());       }        currentCache = currentCache.subCaches.get(cacheKey);     }      for (const arg of args) {       const cacheKey = getKey(arg);       if (!currentCache.subCaches.has(cacheKey)) {         currentCache.subCaches.set(cacheKey, createSubCache());       }        currentCache = currentCache.subCaches.get(cacheKey);     }      if (!currentCache.isCached) {       if (cacheReplacementPolicy) {         const {           maxSize,           strategy = cacheReplacementStrategies.lru         } = cacheReplacementPolicy;         if (allCacheRecords.size >= maxSize) {           const cachesToReplace = strategy(allCacheRecords);           cachesToReplace.forEach(invalidateByCache);         }       }       currentCache.result = originFn.call(this, ...args);       currentCache.isCached = true;        if (ttl != null) {         currentCache.invalidationTimeoutId = setTimeout(           () => invalidateByCache(currentCache),           ttl         );       }       allCacheRecords.add(currentCache);     }     currentCache.hits.push(+new Date());      if (!cacheRejectedPromise) {       Promise.resolve(currentCache.result).catch(() =>         invalidateByCache(currentCache)       );     }      return currentCache.result;   };    memoizedFn.invalidateCache = () => { if (ttl != null) {       allCacheRecords.forEach( cacheData => clearTimeout(cacheData.invalidationTimeoutId) )     }     allCacheRecords.clear();     Object.assign(rootCache, createSubCache());   };    memoizedFn.invalidateCacheByArgs = (...args) => {     if (getContextKey) throw new Error(\"Use invalidateCacheByContextAndArgs instead\");      let currentCache = rootCache;      for (const arg of args) {       const cacheKey = getKey(arg);        currentCache = currentCache.subCaches.get(cacheKey);       if (!currentCache) return;     }      invalidateByCache(currentCache);   };  \/\/ \u043d\u043e\u0432\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f   memoizedFn.invalidateCacheByContextAndArgs = (context, ...args) => {     if (!getContextKey) throw new Error(\"Use invalidateCacheByArgs instead\");      let currentCache = rootCache;     const contextKey = getContextKey(context);     currentCache = currentCache.subCaches.get(contextKey); if (!currentCache) return;      for (const arg of args) {       const cacheKey = getKey(arg);        currentCache = currentCache.subCaches.get(cacheKey);       if (!currentCache) return;     }      invalidateByCache(currentCache);   };    return memoizedFn; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/a5abe90f04dfef1b8e0c4faf248bba1377171c1f\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/a5abe90f04dfef1b8e0c4faf248bba1377171c1f<\/a><\/p>\n<\/div>\n<\/details>\n<p>\u0412\u044b\u0437\u044b\u0432\u0430\u0442\u044c <code>invalidateCacheByContextAndArgs<\/code> \u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u043d <code>getContextKey<\/code> \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u043a\u043e\u0433\u0434\u0430 \u0442\u0430\u043a\u043e\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442, \u043b\u0443\u0447\u0448\u0435 \u0432\u044b\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0441 \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u043e\u0439. \u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0438 \u0441 <code>invalidateCacheByArgs<\/code>, \u043a\u043e\u0433\u0434\u0430 <code>getContextKey<\/code> \u043f\u0435\u0440\u0435\u0434\u0430\u043d.<\/p>\n<\/li>\n<\/ol>\n<h2>\u0422\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432<\/h2>\n<ol>\n<li>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u044f \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c, \u044d\u0442\u043e \u043e\u043f\u0446\u0438\u044f <code>transformArgs<\/code>. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%9A%D0%BE%D0%BC%D0%BC%D1%83%D1%82%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D1%81%D1%82%D1%8C\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u043c\u043c\u0443\u0442\u0430\u0442\u0438\u0432\u043d\u0430\u044f<\/a> \u0444\u0443\u043d\u043a\u0446\u0438\u044f (\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u043e\u0440\u044f\u0434\u043a\u0430 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432). \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0441\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u041c\u044b \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u0438, \u0447\u0442\u043e\u0431\u044b \u0435\u0441\u043b\u0438 \u0443\u0436\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f <code>sum(4, 9)<\/code>, \u0442\u043e \u0434\u043b\u044f <code>sum(9, 4)<\/code> \u0432\u0437\u044f\u043b\u043e\u0441\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043a\u044d\u0448\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e, \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u043d\u0430\u0447\u0430\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445. <code>transformArgs<\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0442\u044c \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0438\u043b\u0438 \u043a\u0430\u043a-\u0442\u043e \u0438\u0445 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(\"transformArgs\", () => {     const fn = jest.fn((a, b) => a + b);      const memoizedFn = withMemo(fn, {       transformArgs: (...args) => args.sort()     });      expect(memoizedFn(4, 9)).toBe(13);     expect(memoizedFn(9, 4)).toBe(13);     expect(fn).toHaveBeenCalledTimes(1);     expect(memoizedFn(4, 4)).toBe(8);     expect(fn).toHaveBeenCalledTimes(2);   });<\/code><\/pre>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">const cacheReplacementStrategies = {   lru: (itemsSet) => {     const asArray = Array.from(itemsSet)       .sort((a, b) => a.hits.at(-1) - b.hits.at(-1))      return asArray.slice(0, 1);   } };  export const withMemo = (   originFn,   {     getKey = (arg) => arg,     getContextKey = null,     getCacheStore = () => new Map(),     cacheRejectedPromise = false,     ttl,     cacheReplacementPolicy = null,     transformArgs = (args) => args \/\/ \u043d\u043e\u0432\u0430\u044f \u043e\u043f\u0446\u0438\u044f. \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u043c   } = {} ) => {   if (cacheReplacementPolicy &amp;&amp; !cacheReplacementPolicy.maxSize) {     throw new Error(\"maxSize is mandatory \");   }    const createSubCache = () => ({     subCaches: getCacheStore(),     result: null,     isCached: false,     invalidationTimeoutId: null,     hits: []   });    const rootCache = createSubCache();   const allCacheRecords = new Set();    const invalidateByCache = (theCache) => {     clearTimeout(theCache.invalidationTimeoutId);     theCache.isCached = false;     theCache.result = null;     theCache.invalidationTimeoutId = null;     theCache.hits = [];     allCacheRecords.delete(theCache);   };    const memoizedFn = function (...args) {     let currentCache = rootCache;      if (getContextKey) {       const cacheKey = getContextKey(this);       if (!currentCache.subCaches.has(cacheKey)) {         currentCache.subCaches.set(cacheKey, createSubCache());       }        currentCache = currentCache.subCaches.get(cacheKey);     }      \/\/ \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0442\u0443\u0442 \u0438 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0445 invalidateCacheByArgs \u0438 invalidateCacheByContextAndArgs     for (const arg of transformArgs(...args)) {       const cacheKey = getKey(arg);       if (!currentCache.subCaches.has(cacheKey)) {         currentCache.subCaches.set(cacheKey, createSubCache());       }        currentCache = currentCache.subCaches.get(cacheKey);     }      if (!currentCache.isCached) {       if (cacheReplacementPolicy) {         const {           maxSize,           strategy = cacheReplacementStrategies.lru         } = cacheReplacementPolicy;         if (allCacheRecords.size >= maxSize) {           const cachesToReplace = strategy(allCacheRecords);           cachesToReplace.forEach(invalidateByCache);         }       }       currentCache.result = originFn.call(this, ...args);       currentCache.isCached = true;        if (ttl != null) {         currentCache.invalidationTimeoutId = setTimeout(           () => invalidateByCache(currentCache),           ttl         );       }       allCacheRecords.add(currentCache);     }     currentCache.hits.push(+new Date());      if (!cacheRejectedPromise) {       Promise.resolve(currentCache.result).catch(() =>         invalidateByCache(currentCache)       );     }      return currentCache.result;   };    memoizedFn.invalidateCache = () => {     if (ttl != null) {       allCacheRecords.forEach(         cacheData => clearTimeout(cacheData.invalidationTimeoutId)       )     }     allCacheRecords.clear();     Object.assign(rootCache, createSubCache());   };    memoizedFn.invalidateCacheByArgs = (...args) => {     if (getContextKey)       throw new Error(\"Use invalidateCacheByContextAndArgs instead\");      let currentCache = rootCache;      for (const arg of transformArgs(...args)) {       const cacheKey = getKey(arg);        currentCache = currentCache.subCaches.get(cacheKey);       if (!currentCache) return;     }      invalidateByCache(currentCache);   };    memoizedFn.invalidateCacheByContextAndArgs = (context, ...args) => {     if (!getContextKey) throw new Error(\"Use invalidateCacheByArgs instead\");      let currentCache = rootCache;     const cotextKey = getContextKey(context);     currentCache = currentCache.subCaches.get(cotextKey); if (!currentCache) return;      for (const arg of transformArgs(...args)) {       const cacheKey = getKey(arg);        currentCache = currentCache.subCaches.get(cacheKey);       if (!currentCache) return;     }      invalidateByCache(currentCache);   };    return memoizedFn; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/21b74b4b4ccad634c5b3dc28cd93c0ab9e72c149\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/21b74b4b4ccad634c5b3dc28cd93c0ab9e72c149<\/a><\/p>\n<\/div>\n<\/details>\n<\/li>\n<\/ol>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451 \u0441 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u044e, \u043d\u043e \u0435\u0441\u0442\u044c \u0435\u0449\u0451 \u043f\u0430\u0440\u0443 \u0432\u0435\u0449\u0435\u0439, \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0435\u0441\u0442\u044c \u0436\u0435\u043b\u0430\u043d\u0438\u0435 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c.<\/p>\n<h2>\u041f\u0440\u0438\u043c\u0435\u0440 CRUD \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0435\u0439<\/h2>\n<p>\u0425\u043e\u0447\u0435\u0442\u0441\u044f \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442\u044c \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0440\u0430\u0437\u0443 \u043f\u0440\u0438\u043d\u0435\u0441\u0451\u0442 \u043f\u043e\u043b\u044c\u0437\u0443.<\/p>\n<p>\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c REST API \u0434\u043b\u044f \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 User \u0438 \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044b\u0447\u043d\u044b\u0435 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/CRUD\" rel=\"noopener noreferrer nofollow\">CRUD<\/a> \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043d\u0435\u0451. \u041a\u0430\u043a \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 <code>withMemo<\/code> \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0432\u043c\u0435\u0441\u0442\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438:<\/p>\n<pre><code class=\"javascript\">const createMemoizedCrudService = (endpoint) => {   const get = withMemo( (id) => fetch(`${endpoint}\/${id}`), { ttl: 5 * 60 * 1000, } \/\/ 5 \u043c\u0438\u043d\u0443\u0442 )    const getAll = withMemo( (paginationAndFiltersParams) => fetch(`${endpoint}?${ + new URLSearchParams(paginationAndFiltersParams)}`),  { ttl: 1 * 60 * 1000, } \/\/ 1 \u043c\u0438\u043d\u0443\u0442\u0430 })    const create = (id, newEntity) => {     getAll.invalidateCache();      return fetch(`${endpoint}`, {       method: 'POST',       body: JSON.stringify(newEntity),     })   }    const update = (id, updatedEntity) => {     getAll.invalidateCache();     get.invalidateCacheByArgs(id);      return fetch(`${endpoint}\/${id}`, {       method: 'PUT',       body: JSON.stringify(updatedEntity),     })   }    const bulkUpdate = (filters, updatedValues) => {     getAll.invalidateCache();     get.invalidateCache();      return fetch(`${endpoint}\/bulk`, {       method: 'PUT',       body: JSON.stringify({ filters, updatedValues }),     })   }    const remove = (id) => {     getAll.invalidateCache();     get.invalidateCacheByArgs(id);      return fetch(`${endpoint}\/${id}`, {       method: 'DELETE',     })   }    return {     get,     getAll,     create,     update,     bulkUpdate,     remove,   } }<\/code><\/pre>\n<p>\u0418 \u0432\u043e\u0442 \u0442\u0430\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c<\/p>\n<pre><code class=\"javascript\">const usersCrudService = createMemoizedCrudService('\/api\/users')  usersCrudService.get(1) \/\/ \u0437\u0430\u043f\u0440\u043e\u0441 usersCrudService.get(1) \/\/ \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0435\u0442, \u0431\u0435\u0440\u0451\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438\u0437 \u043a\u0435\u0448\u0430 usersCrudService.get(2) \/\/ \u0437\u0430\u043f\u0440\u043e\u0441 usersCrudService.getAll() \/\/ \u0437\u0430\u043f\u0440\u043e\u0441 usersCrudService.getAll() \/\/ \u043a\u044d\u0448 usersCrudService.update(1, {name: 'Victor'}) usersCrudService.get(1) \/\/ \u0437\u0430\u043f\u0440\u043e\u0441, \u0442\u0430\u043a \u043a\u0430\u043a update \u0441\u0431\u0440\u043e\u0441\u0438\u043b \u043a\u044d\u0448 usersCrudService.get(2) \/\/ \u043a\u044d\u0448 usersCrudService.getAll() \/\/ \u0437\u0430\u043f\u0440\u043e\u0441  \/\/ \u0447\u0435\u0440\u0435\u0437 5 \u043c\u0438\u043d\u0443\u0442 usersCrudService.get(2) \/\/ \u0437\u0430\u043f\u0440\u043e\u0441, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043a\u044d\u0448 \u0443\u0434\u0430\u043b\u0438\u043b\u0441\u044f \u043f\u043e \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u0443   \/\/ \u0434\u0440\u0443\u0433\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b const companiesCrudService = createMemoizedCrudService('\/api\/companies') const phonesCrudService = createMemoizedCrudService('\/api\/phones')<\/code><\/pre>\n<p>\u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u0432\u0438\u0434\u0435\u0442\u044c \u0432\u0430\u0448\u0438 \u0438\u0434\u0435\u0438 \u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445<\/p>\n<h2>\u041f\u0440\u043e \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u0442\u0435\u0441\u0442\u0430\u043c\u0438<\/h2>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0442\u0435\u0441\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438, \u0445\u043e\u0440\u043e\u0448\u043e \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u0448\u0435\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0442\u043e \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a) \u0413\u043b\u0430\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0432\u0448\u0435\u0439\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>withMemo<\/code> \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0443 \u043d\u0435\u0451 \u043c\u043d\u043e\u0433\u043e \u043e\u043f\u0446\u0438\u0439 \u0438 \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043c\u043d\u043e\u0433\u043e if\u2019\u043e\u0432. \u0412 \u0438\u0434\u0435\u0430\u043b\u0435 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u0441 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0435\u043c \u0432\u0441\u0435\u0445 \u043e\u043f\u0446\u0438\u0439,  \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u0442\u044c \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u043c, \u0447\u0442\u043e \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f. \u0410 \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0442\u043e\u0440\u043d\u043e\u043c\u0443 \u0432\u0437\u0440\u044b\u0432\u0443 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432.<\/p>\n<p>\u0422\u0443\u0442 \u043c\u043d\u0435 \u0432\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0432\u0438\u0434\u0435\u043e \u0441 \u043a\u0430\u043d\u0430\u043b\u0430 <strong>Fun Fun Function<\/strong> \u043d\u0430 \u044e\u0442\u0443\u0431\u0435, \u0432\u044b\u043f\u0443\u0441\u043a\u0438 \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u044f \u043a\u043e\u0434\u0430-\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u043b. \u0412\u0438\u0434\u0435\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u043b\u043e\u0441\u044c <a href=\"https:\/\/www.youtube.com\/watch?v=glZ1C-Yu5tw&amp;t=3s&amp;ab_channel=FunFunFunction\" rel=\"noopener noreferrer nofollow\"><strong>Settings are evil<\/strong><\/a> \u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u044d\u043c\u043e\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e \u0438 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u043b\u043e \u043e\u0431 \u044d\u0442\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0435.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0435\u0442\u0435 \u043a\u043e\u0434 \u043d\u0430\u0448\u0435\u0439 withMemo \u043a\u0430\u043a \u0435\u0441\u0442\u044c, \u0442\u043e \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0431\u0451\u0440\u0442\u043a\u0443 \u043d\u0430\u0434 \u043d\u0435\u0439, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u044b \u0437\u0430\u0444\u0438\u043a\u0441\u0438\u0440\u0443\u0435\u0442\u0435 \u0432\u0441\u0435 \u043e\u043f\u0446\u0438\u0438 \u0438 \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0430\u043f\u0438\u0448\u0435\u0442\u0435 \u0441\u0432\u043e\u0438 \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u0432\u0430\u043c\u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a.<\/p>\n<h2>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f<\/h2>\n<p>\u041d\u0430\u0434\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c, \u0447\u0442\u043e \u0434\u043b\u044f \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0438\u0441\u0442\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0418 \u0442\u043e \u043d\u0435 \u0432\u0441\u0435, \u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u043e \u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u044e\u0442 \u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0430. \u0410 \u0435\u0441\u043b\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0435 \u0447\u0438\u0441\u0442\u0430\u044f, \u0442\u043e \u043c\u044b \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u0438\u0441\u043a\u0443\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043a\u044d\u0448\u0430.<\/p>\n<p>\u041d\u0443\u0436\u043d\u043e \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0432 \u0433\u043e\u043b\u043e\u0432\u0435, \u0447\u0442\u043e \u0432 \u043b\u044e\u0431\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0435 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u0430. \u0425\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043f\u0440\u043e\u0448\u043b\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0432\u043b\u0435\u0447\u0451\u0442 \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0435 \u0440\u0430\u0441\u0445\u043e\u0434\u044b \u043f\u0430\u043c\u044f\u0442\u0438. \u0412\u044b\u0438\u0433\u0440\u044b\u0448 \u0432 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u043f\u0435\u0440\u0435\u0432\u0435\u0448\u0438\u0432\u0430\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u0435 \u0432 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u043c\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438, \u0435\u0441\u043b\u0438 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0438 \u0442\u0430\u043a \u0431\u044b\u0441\u0442\u0440\u0430\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0441\u043b\u043e\u0436\u0435\u043d\u0438\u044f) \u0438\u043b\u0438 \u0435\u0441\u043b\u0438 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0440\u0435\u0434\u043a\u043e. \u0411\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043d\u0435\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432,  \u0430 \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043f\u0440\u0438 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438 \u0442\u043e\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0441\u0442\u0438 \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u043e. \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u0432 \u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u043c\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438, \u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0438 \u0432\u044b\u0442\u0435\u0441\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u043a\u044d\u0448\u0430.<\/p>\n<p>\u041c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044f \u044d\u0442\u043e \u0432\u0441\u0435\u0433\u0434\u0430 \u043a\u043e\u043c\u043f\u0440\u043e\u043c\u0438\u0441\u0441 \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u043c\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u044c\u044e \u0438 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<h2>\u041c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0445  \u0444\u0443\u043d\u043a\u0446\u0438\u0439<\/h2>\n<p>\u0415\u0441\u043b\u0438 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u0430 \u043f\u043e\u0442\u043e\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0435\u0451 \u0432 withMemo \u043a\u0430\u043a \u0432 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435<\/p>\n<pre><code class=\"javascript\">const factorial = (x) => {   if (x === 0) {     return 1;   }   else {     return x * factorial(x - 1);   } }  const memoizedFactorial = withMemo(factorial)<\/code><\/pre>\n<p>\u0442\u043e \u0434\u043b\u044f \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0445 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u0430 \u043d\u0435 \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0435\u0451 \u0432\u0435\u0440\u0441\u0438\u044f.<\/p>\n<pre><code class=\"javascript\">memoizedFactorial(50) memoizedFactorial(49) \/\/ \u043d\u0435 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u044d\u0448\u043e\u043c \u0438 \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u0432\u0441\u0435 49 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 memoizedFactorial(51) \/\/ \u043d\u0435 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u044d\u0448\u043e\u043c \u0438 \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u0432\u0441\u0435 51 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 memoizedFactorial(50) \/\/ \u0432\u043e\u0437\u043c\u0451\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438\u0437 \u043a\u0435\u0448\u0430<\/code><\/pre>\n<p>\u041d\u043e \u044d\u0442\u043e \u043b\u0435\u0433\u043a\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c. \u0414\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c<\/p>\n<pre><code class=\"javascript\">const memoizedFactorial = withMemo( (x) => {   if (x === 0) {     return 1;   }   else {     return x * memoizedFactorial(x - 1);   } } )  memoizedFactorial(50) memoizedFactorial(49) \/\/ \u0432\u043e\u0437\u043c\u0451\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438\u0437 \u043a\u0435\u0448\u0430 memoizedFactorial(51) \/\/ \u0441\u0434\u0435\u043b\u0430\u0435\u0442 1 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u0438 \u0432\u0441\u0451 memoizedFactorial(50) \/\/ \u0432\u043e\u0437\u043c\u0451\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438\u0437 \u043a\u0435\u0448\u0430<\/code><\/pre>\n<h2>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430<\/h2>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5fa\/fa0\/3c1\/5fafa03c132e3f0e11ef2042c1b46531.png\" alt=\"\u041a\u0430\u043a \u043c\u043d\u043e\u0436\u0430\u0442\u0441\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u044b\" title=\"\u041a\u0430\u043a \u043c\u043d\u043e\u0436\u0430\u0442\u0441\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u044b\" width=\"500\" height=\"283\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5fa\/fa0\/3c1\/5fafa03c132e3f0e11ef2042c1b46531.png\"\/><\/p>\n<div><figcaption>\u041a\u0430\u043a \u043c\u043d\u043e\u0436\u0430\u0442\u0441\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u044b<\/figcaption><\/div>\n<\/figure>\n<p>\u042f \u043d\u0435 \u0441\u043c\u043e\u0433 \u0443\u0434\u0435\u0440\u0436\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c npm \u043f\u0430\u043a\u0435\u0442 \u0441 \u044d\u0442\u0438\u043c \u043a\u043e\u0434\u043e\u043c. \u0422\u0435\u043c \u0431\u043e\u043b\u0435\u0435, \u0447\u0442\u043e \u0432\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0443\u0436\u0435 \u043f\u043e\u0447\u0442\u0438 \u0441\u0434\u0435\u043b\u0430\u043d\u0430. \u0422\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0434\u043e \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u0451 \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u043d\u0430 TypeScript\u2019\u0435, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0439 \u0432\u044b\u0442\u0435\u0441\u043d\u0435\u043d\u0438\u044f \u043a\u044d\u0448\u0430, \u0447\u0443\u0442\u044c \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u043f\u0430\u0440\u0443 \u0442\u0435\u0441\u0442\u043e\u0432 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c. \u042d\u0442\u043e, \u043a\u0441\u0442\u0430\u0442\u0438, \u043c\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 \u043f\u0430\u043a\u0435\u0442 \u0432 npm.<\/p>\n<p>\u041f\u043e\u043a\u0430 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u043b, \u0441\u0434\u0435\u043b\u0430\u043b \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0432 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438. \u042f \u0434\u043e\u0431\u0430\u0432\u0438\u043b  \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u043a\u044d\u0448\u0430 \u0432\u0432\u0435\u0440\u0445 \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443, \u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b \u0432 \u0441\u0435\u043a\u0446\u0438\u0438 \u201c\u0412\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438\u201d. \u0412\u044b\u043f\u0438\u043b\u0438\u043b <code>allCacheRecors<\/code> \u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u043b \u043d\u0430 \u0447\u0438\u0441\u043b\u043e  <code>cacheSize<\/code> \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>getAllCacheRecors()<\/code>. \u0415\u0441\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b \u0434\u0435\u0442\u0430\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f, \u0442\u043e \u0432\u043e\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo<\/a><\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0432\u0441\u0435\u043c \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435. \u041a\u0442\u043e \u0441\u043c\u043e\u0433 \u043f\u0440\u043e\u0439\u0442\u0438 \u0432\u0435\u0441\u044c \u043f\u0443\u0442\u044c \u0441\u0442\u0440\u043e\u0433\u043e \u043f\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 &#8212; \u043c\u043e\u043b\u043e\u0434\u0446\u044b! \u0412\u044b &#8212; \u043b\u0443\u0447\u0448\u0438\u0435!<\/p>\n<p>\u0427\u0443\u0442\u044c \u043d\u0435 \u0437\u0430\u0431\u044b\u043b, \u0443 \u043d\u0430\u0441 \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 <a href=\"https:\/\/careers.textmagic.com\/\" rel=\"noopener noreferrer nofollow\">TextMagic \u0435\u0441\u0442\u044c \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0435 \u0432\u0430\u043a\u0430\u043d\u0441\u0438\u0438<\/a>. \u041f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0430\u043c\u0438)<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/720594\/\"> https:\/\/habr.com\/ru\/post\/720594\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>\u041f\u0440\u043e \u0441\u0442\u0430\u0442\u044c\u044e<\/h3>\n<p>\u041c\u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e \u0445\u043e\u0434\u0443 \u0447\u0442\u0435\u043d\u0438\u044f \u043e\u0447\u0435\u043d\u044c \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u0432 \u0441\u0435\u0440\u0432\u0438\u0441 <a href=\"http:\/\/codesandbox.io\/\" rel=\"noopener noreferrer nofollow\">codesandbox.io<\/a> \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u043d\u0438\u044f, \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0438\u0442\u0430\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435. \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0432\u0435\u043d\u043d\u043e, \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430, \u0430 \u043d\u0435 \u0441 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043b\u0430\u0441\u044c \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0439 \u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u0434\u043b\u044f \u0432\u0441\u0435\u0445. \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u0434\u0436\u0443\u043d \u0440\u0435\u0431\u044f\u0442. \u041d\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u043c\u044b \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0443\u0441\u0438\u043b\u0438\u0439 \u043e\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0441 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c. \u042f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u043b\u0441\u044f \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438 \u043b\u0438\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u0442\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b. \u0412\u0435\u0440\u044e, \u0447\u0442\u043e \u043a\u043e\u043c\u0443 \u043d\u0430\u0434\u043e \u0438 \u0441\u0430\u043c \u0437\u0430\u0433\u0443\u0433\u043b\u0438\u0442 \u043d\u0435\u0445\u0432\u0430\u0442\u0430\u044e\u0449\u0443\u044e \u0435\u043c\u0443 \u0442\u0435\u043e\u0440\u0438\u044e \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e. \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u044f \u0443 \u0432\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u043e\u0435 \u0438 \u0432\u0441\u0435\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0441\u043b\u043e\u0436\u0438\u0442\u0441\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432 \u0442\u0430\u043a\u0438\u0445 \u0442\u0435\u043c\u0430\u0445 \u043a\u0430\u043a \u0437\u0430\u043c\u044b\u043a\u0430\u043d\u0438\u0435, \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432\u044b\u0441\u0448\u0435\u0433\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0430, \u0447\u0438\u0441\u0442\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u0430\u0440\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, TDD, \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044f \u0438 property-based \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0410 \u0433\u043b\u0430\u0432\u043d\u043e\u0435 &#8212; \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043a\u0430\u043a \u0438 \u0433\u0434\u0435 \u044d\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c.<\/p>\n<h3>\u041f\u0440\u043e \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u044b<\/h3>\n<p>\u0424\u0440\u0430\u0437\u0430 \u201c\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u201d \u043e\u0431\u044b\u0447\u043d\u043e \u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0430\u0441\u0430 \u0447\u0435\u0433\u043e-\u0442\u043e. \u041d\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u0438\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u0437\u0434\u0435\u0441\u044c. \u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0442\u0435\u043c\u0435. \u041f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0432 \u0441\u0430\u043c\u043e\u043c\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e, \u043c\u044b \u043b\u0443\u0447\u0448\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f \u0432 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u044b\u0447\u043d\u043e \u0434\u0435\u043b\u0430\u044e\u0442 \u0447\u0430\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b \u0437\u0430 \u043d\u0430\u0441. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u044b  \u0441\u043c\u043e\u0436\u0435\u043c \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u043e\u043b\u044c\u0437\u044b \u0438\u0437 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0437\u043d\u0430\u043d\u0438\u044f \u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0432\u0430\u043c \u0434\u0435\u0431\u0430\u0436\u0438\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0445\u043e\u0442\u044f \u0431\u044b \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0437\u043d\u0430\u0442\u044c, \u0447\u0442\u043e \u043c\u043e\u0433\u043b\u043e \u043f\u043e\u0439\u0442\u0438 \u043d\u0435 \u0442\u0430\u043a. \u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u0448\u044c\u0441\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0441\u0430\u043c, \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u0435\u0449\u0438 \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u043e\u0449\u0435 \u0438 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u044e\u0442 \u0431\u044b\u0442\u044c \u043c\u0430\u0433\u0438\u0435\u0439. \u0410 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435, \u043a\u0430\u0437\u0430\u0432\u0448\u0438\u0435\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u043c\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f <a href=\"https:\/\/www.youtube.com\/watch?v=-5wpm-gesOY&amp;ab_channel=Computerphile\" rel=\"noopener noreferrer nofollow\">\u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u043f\u0438\u0442\u0430\u043d\u043d\u044b\u043c\u0438 \u043d\u044e\u0430\u043d\u0441\u0430\u043c\u0438<\/a>, \u0447\u0442\u043e \u0442\u044b \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0448\u044c\u0441\u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u0435\u043d \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044e \u043f\u0430\u043a\u0435\u0442\u0430 \u0437\u0430 \u0435\u0433\u043e \u0442\u0440\u0443\u0434.<\/p>\n<h3>\u041c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p>\u041c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u043d\u044b\u043c \u0441\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041a \u043a\u044d\u0448\u0443 \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u044c\u0441\u044f \u043b\u044e\u0431\u043e\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0431\u0443\u0434\u0443\u0449\u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043a \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0432\u044b\u0437\u043e\u0432\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0412 \u043e\u0431\u0449\u0435\u043c, \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043f\u043e\u0441\u043e\u0431 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u0430 \u043d\u0430\u0448\u0438\u0445 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c.<\/p>\n<p>\u041a\u0430\u043a \u0443\u0436\u0435 \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b\u043e\u0441\u044c, \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u043f\u043e\u043b\u043d\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439. \u0412\u043e\u0442 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c: <a href=\"https:\/\/lodash.com\/docs\/#memoize\" rel=\"noopener noreferrer nofollow\">\u043b\u043e\u0434\u0430\u0448\u0435\u0432\u0441\u043a\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f memoize<\/a>, <a href=\"https:\/\/www.npmjs.com\/package\/p-memoize\" rel=\"noopener noreferrer nofollow\">p-memoize<\/a>, <a href=\"https:\/\/caolan.github.io\/async\/v3\/docs.html#memoize\" rel=\"noopener noreferrer nofollow\">async<\/a>, <a href=\"https:\/\/www.npmjs.com\/package\/memoizee\" rel=\"noopener noreferrer nofollow\">memoizee<\/a> (\u043c\u043e\u0439 \u043b\u044e\u0431\u0438\u043c\u0447\u0438\u043a), <a href=\"https:\/\/www.npmjs.com\/package\/moize\" rel=\"noopener noreferrer nofollow\">moize<\/a>, <a href=\"https:\/\/www.npmjs.com\/package\/fast-memoize\" rel=\"noopener noreferrer nofollow\">fast-memoize<\/a>,  <a href=\"https:\/\/github.com\/developit\/decko#memoize\" rel=\"noopener noreferrer nofollow\">ES7\u00a0\u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440 <\/a><code>@memoize<\/code>\u00a0\u0438\u0437\u00a0decko.<\/p>\n<p>\u0414\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0432\u044b \u044f\u0432\u043d\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u044e, \u0442\u043e \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u044e\u0442 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0432\u044b \u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435\u0441\u044c \u043d\u0430 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0439 \u043e\u0441\u043d\u043e\u0432\u0435. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0432 \u043c\u0438\u0440\u0435 react\u2019\u0430 \u044d\u0442\u043e \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/github.com\/reduxjs\/reselect\" rel=\"noopener noreferrer nofollow\">reselect<\/a>, <a href=\"https:\/\/tanstack.com\/query\/v4\" rel=\"noopener noreferrer nofollow\">react-query<\/a>, <a href=\"https:\/\/www.apollographql.com\/docs\/react\/\" rel=\"noopener noreferrer nofollow\">apollo-client<\/a>, \u0445\u0443\u043a <a href=\"https:\/\/ru.reactjs.org\/docs\/hooks-reference.html#usememo\" rel=\"noopener noreferrer nofollow\">useMemo<\/a> \u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0432\u044b\u0441\u0448\u0435\u0433\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0430 <a href=\"https:\/\/ru.reactjs.org\/docs\/react-api.html#reactmemo\" rel=\"noopener noreferrer nofollow\">memo<\/a> \u0438 \u043c\u043d\u043e\u0433\u043e\u0435 \u0434\u0440\u0443\u0433\u043e\u0435.<\/p>\n<h2>\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0451\u0439<\/h2>\n<p>\u0412 CodeSandbox \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f <a href=\"https:\/\/codesandbox.io\/s\/starter-template-o02e2z?file=\/src\/withMemo.js\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0440\u0442\u043e\u0432\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d<\/a> \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c, \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438. \u0412 \u043d\u0451\u043c \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430: withMemo.js \u0438 withMemo.test.js<\/p>\n<p>\u041f\u0440\u043e\u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435 \u043f\u043e \u0441\u0442\u0430\u0442\u044c\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0448\u0430\u0433\u043e\u0432:<\/p>\n<ol>\n<li>\n<p>\u042f \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0447\u0442\u043e-\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043b\u0438 \u043a\u0430\u043a-\u0442\u043e \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0442\u0435\u043c \u044f \u0434\u0430\u044e \u0442\u0435\u0441\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 withMemo.test.js \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0435 \u0447\u0442\u043e-\u0442\u043e \u043d\u0430 \u0441\u0432\u043e\u0451 \u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u0438\u0435 \u0432 withMemo.js \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043d\u043e\u0432\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u043f\u0440\u043e\u0448\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u0430 \u0441\u0442\u0430\u0440\u044b\u0435 \u043d\u0435 \u0441\u043b\u043e\u043c\u0430\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u042f \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043e\u0442 \u0441\u0435\u0431\u044f \u0438, \u0435\u0441\u043b\u0438 \u043d\u0430\u0434\u043e, \u0442\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ol>\n<p>\u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 (\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432, \u0430 \u0437\u0430\u0442\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043b\u0438\u0441\u044c) \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f <a href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/450316\/\" rel=\"noopener noreferrer nofollow\">Test Driven Development (TDD)<\/a>.<\/p>\n<h2>\u041f\u043e\u0435\u0445\u0430\u043b\u0438<\/h2>\n<h2>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0435\u0437 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432<\/h2>\n<ol>\n<li>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439:<\/p>\n<pre><code class=\"javascript\">\/\/ \u0412\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e function generateDigitsOfPi() {     const DIGITS_COUNT = 10_001;     let q = 1n;     let r = 180n;     let t = 60n;     let i = 2n;     let str = '';     for (let j = 0; j &lt; DIGITS_COUNT; j++) {         let digit = ((i * 27n - 12n) * q + r * 5n) \/ (t * 5n);         str += digit         let u = i * 3n;         u = (u + 1n) * 3n * (u + 2n);         r = u * 10n * (q * (i * 5n - 2n) + r - t * digit);         q *= 10n * i * (i++ * 2n - 1n);         t *= u;     }      const arr = str.split('');     arr.splice(1, 0, '.');     return arr.join(''); }<\/code><\/pre>\n<p>\u042d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u0435\u0440\u043d\u0451\u0442 \u0447\u0438\u0441\u043b\u043e \u041f\u0438 \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c\u044e \u0432 10 000 \u0446\u0438\u0444\u0440 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439. \u0422\u0430\u043a\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442 \u0434\u043b\u044f \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0451 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0430. \u0410 \u043f\u043e\u0441\u043b\u0435 \u043c\u0435\u043c\u043e\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0434\u0435\u043b\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0422\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(     \"should call original function only ones and \" +       \"always return original result (no args)\",     () => {       const result = Math.random();       const fn = jest.fn(() => result);       const memoizedFn = withMemo(fn);        expect(fn).toHaveBeenCalledTimes(0);       expect(memoizedFn()).toBe(result);       expect(fn).toHaveBeenCalledTimes(1);       expect(memoizedFn()).toBe(result);       expect(fn).toHaveBeenCalledTimes(1);     }   );<\/code><\/pre>\n<p>\u0417\u0430\u043c\u0435\u0442\u0438\u043b\u0438, \u0447\u0442\u043e \u044f \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>result<\/code> \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0432\u043c\u0435\u0441\u0442\u043e \u0447\u0435\u0433\u043e-\u0442\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e? \u042d\u0442\u043e \u043e\u0434\u043d\u0430 \u0438\u0437 \u043f\u0440\u0430\u043a\u0442\u0438\u043a <a href=\"https:\/\/github.com\/dubzzz\/fast-check\/blob\/main\/packages\/fast-check\/documentation\/HandsOnPropertyBased.md\" rel=\"noopener noreferrer nofollow\">property-based \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/a>. \u0414\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u043e\u043d\u0430 \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0435\u0442\u0441\u044f \u0432 <a href=\"https:\/\/www.youtube.com\/watch?v=DlQZ4OsALgI&amp;ab_channel=AndrewBurgess\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u043c \u0432\u0438\u0434\u0435\u043e<\/a>. \u0422\u0430\u043a\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u0447\u0443\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u043f\u0438\u0441\u0430\u0442\u044c, \u0437\u0430\u0442\u043e \u043e\u043d\u0438 \u043f\u043e\u043a\u0440\u044b\u0432\u0430\u044e\u0442 \u0431\u043e\u043b\u0435\u0435 \u043e\u0431\u0449\u0438\u0435 \u0441\u043b\u0443\u0447\u0430\u0438. \u0410 <a href=\"https:\/\/github.com\/dubzzz\/fast-check\" rel=\"noopener noreferrer nofollow\">\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438<\/a> \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u043e\u0433\u043e\u043d\u044f\u0442\u044c \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u0442\u0435\u0441\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0441\u0430\u043c\u0438, \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044f \u0440\u0430\u0437\u043d\u044b\u0435 \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044f \u043a\u0440\u0430\u0439\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u0443\u0441\u0442\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438\u043b\u0438 \u043c\u0430\u0441\u0441\u0438\u0432\u044b) \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u0430\u043c\u0438 \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0437\u0430\u0431\u044b\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0443 <code>Tests<\/code><\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0418\u043d\u043e\u0433\u0434\u0430 codesandbox \u043d\u0435 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0442\u0435\u0441\u0442\u044b \u043f\u043e\u0441\u043b\u0435 \u0432\u043d\u0435\u0441\u0435\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439. \u041f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u044d\u0442\u043e.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">\/\/ \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432 \u0432\u0438\u0434\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e originFn export const withMemo = (originFn) => {   let cache;  \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f    \/\/ \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438   return () => { \/\/ \u0435\u0441\u043b\u0438 \u043a\u044d\u0448\u0430 \u043d\u0435\u0442, \u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044e \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0435\u0433\u043e     if (cache === undefined) {       cache = originFn();     }  \/\/ \u0432 \u043a\u043e\u043d\u0446\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u043a\u044d\u0448\u0430     return cache;   }; };<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u043d\u0438\u0447\u0435\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e \u043d\u0435\u0442. \u041e\u0431\u0440\u0430\u0442\u0438\u043c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u0438.<\/p>\n<p>\u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, withMemo \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <a href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/428570\/\" rel=\"noopener noreferrer nofollow\">\u0424\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u0432\u044b\u0441\u0448\u0435\u0433\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0430<\/a>. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043b\u0438\u0431\u043e \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0434\u0440\u0443\u0433\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432 \u0432\u0438\u0434\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430, \u043b\u0438\u0431\u043e \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e. \u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>withMemo<\/code> \u0434\u0435\u043b\u0430\u0435\u0442 \u0438 \u0442\u043e \u0438 \u0434\u0440\u0443\u0433\u043e\u0435.<\/p>\n<p>\u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, \u0432 \u044d\u0442\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0435\u0441\u0442\u044c <a href=\"https:\/\/learn.javascript.ru\/closure\" rel=\"noopener noreferrer nofollow\">\u0437\u0430\u043c\u044b\u043a\u0430\u043d\u0438\u0435<\/a>. \u041e\u043d\u043e \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 <code>cache<\/code>. \u0421\u043c\u044b\u0441\u043b \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u0437\u043e\u0432\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u044d\u0442\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f <code>cache<\/code>, \u043d\u043e \u043e\u043d\u0430 \u043d\u0435 \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a withMemo \u043e\u0442\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u0410 \u043d\u0435 \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u043d\u0430 \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e withMemo \u0432\u0435\u0440\u043d\u0443\u043b\u0430 \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0435\u0431\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u044d\u0442\u043e\u0439 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 (\u0442\u043e \u0435\u0441\u0442\u044c \u0438\u043c\u0435\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043d\u0435\u0451). \u041d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441\u0430\u043c \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u044d\u0442\u0443 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>cache<\/code>, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0441 \u043d\u0435\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<p>\u041f\u043e\u043d\u044f\u0442\u0438\u0435 \u0437\u0430\u043c\u044b\u043a\u0430\u043d\u0438\u044f \u0442\u0435\u0441\u043d\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 <a href=\"https:\/\/habr.com\/ru\/post\/517338\/\" rel=\"noopener noreferrer nofollow\">\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u044e \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445<\/a>. \u041e\u0431\u043b\u0430\u0441\u0442\u0438 \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u043b\u043e\u0436\u0435\u043d\u044b \u0434\u0440\u0443\u0433 \u0432 \u0434\u0440\u0443\u0433\u0430 \u0438 \u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0442 \u0434\u0440\u0435\u0432\u043e\u0432\u0438\u0434\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443. \u0412 \u044d\u0442\u043e\u043c \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f.<\/p>\n<\/li>\n<\/ol>\n<h2>\u0410 \u0435\u0441\u043b\u0438 undefined<\/h2>\n<ol>\n<li>\n<p>\u0412 \u043c\u043e\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u0448\u0430\u0433\u0430 \u0434\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>undefined<\/code>. \u0415\u0441\u043b\u0438 \u0432 \u043a\u044d\u0448\u0435 \u043b\u0435\u0436\u0438\u0442 \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0442\u043e \u043a\u044d\u0448 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043f\u0443\u0441\u0442\u044b\u043c. \u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u0441\u0442\u043e \u0431\u044b\u0442\u044c \u0438 \u0441 \u043d\u0438\u043c \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0432 \u043f\u043e\u0434\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432. \u041d\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u0441\u0451 \u0436\u0435 \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u043c \u044d\u0442\u043e\u0442 \u043a\u0440\u0430\u0439\u043d\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js<\/p>\n<pre><code class=\"javascript\">it(\"'undefined' is valid value\", () => {     const result = undefined;     const fn = jest.fn(() => result);     const memoizedFn = withMemo(fn);      expect(fn).toHaveBeenCalledTimes(0);     expect(memoizedFn()).toBe(result);     expect(fn).toHaveBeenCalledTimes(1);     expect(memoizedFn()).toBe(result);     expect(fn).toHaveBeenCalledTimes(1);   });<\/code><\/pre>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438, \u043f\u0440\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/github.com\/dubzzz\/fast-check\" rel=\"noopener noreferrer nofollow\">fast-check<\/a>, \u043e\u043d\u0430 \u0431\u044b \u0441\u0430\u043c\u0430 \u0443\u043a\u0430\u0437\u0430\u043b\u0430 \u043d\u0430\u043c \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e <code>withMemo<\/code> \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u044d\u0442\u043e\u043c \u043a\u0440\u0430\u0439\u043d\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435. \u041d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u0441\u0432\u043e\u044e \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c .<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435<\/p>\n<details class=\"spoiler\">\n<summary>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const withMemo = (originFn) => {   const cache = {     value: undefined,      isCached: false, \/\/ \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f   };    return () => {     if (!cache.isCached) {       cache.value = originFn();       cache.isCached = true;     }     return cache.value;   }; };<\/code><\/pre>\n<p>diff: <a href=\"https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/e6d6be554f0143efceb139cc135f7e2efbb8d692\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/KnightSlayer\/with-memo\/commit\/e6d6be554f0143efceb139cc135f7e2efbb8d692<\/a><\/p>\n<\/div>\n<\/details>\n<p>\u042f \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u0432\u0451\u043b \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0434\u043b\u044f \u044f\u0432\u043d\u043e\u0433\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u0438\u043b\u0438 \u043d\u0435\u0442.<\/p>\n<\/li>\n<\/ol>\n<h2>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0434\u0438\u043d \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442<\/h2>\n<ol>\n<li>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438, \u0432\u0441\u0451 \u0436\u0435, \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>generateDigitsOfPi<\/code> \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0445\u0430\u0440\u0434\u043a\u043e\u0434\u0438\u0442\u044c \u0447\u0438\u0441\u043b\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439 \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e <code>DIGITS_COUNT<\/code>, \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435  \u043a\u0430\u043a \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 <code>digitsCount<\/code>.<\/p>\n<pre><code class=\"javascript\">function generateDigitsOfPi(digitsCount) {     let q = 1n;     let r = 180n;     let t = 60n;     let i = 2n;     let str = '';     for (let j = 0; j &lt; digitsCount; j++) {         let digit = ((i * 27n - 12n) * q + r * 5n) \/ (t * 5n);         str += digit         let u = i * 3n;         u = (u + 1n) * 3n * (u + 2n);         r = u * 10n * (q * (i * 5n - 2n) + r - t * digit);         q *= 10n * i * (i++ * 2n - 1n);         t *= u;     }      const arr = str.split('')     arr.splice(1, 0, '.')     return arr.join('') }<\/code><\/pre>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u044d\u0442\u0438 \u0442\u0435\u0441\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.test.js.<\/p>\n<pre><code class=\"javascript\">it(\"should cache depending on argument\", () => {     const factor = Math.random();     const fn = jest.fn((arg) => arg * factor);     const memoizedFn = withMemo(fn);      expect(fn).toHaveBeenCalledTimes(0);      expect(memoizedFn(1)).toBe(factor);     expect(fn).toHaveBeenCalledTimes(1);      expect(memoizedFn(1)).toBe(factor);     expect(fn).toHaveBeenCalledTimes(1);      expect(memoizedFn(2)).toBe(2 * factor);     expect(fn).toHaveBeenCalledTimes(2);      expect(memoizedFn(2)).toBe(2 * factor);     expect(fn).toHaveBeenCalledTimes(2);      expect(memoizedFn(1)).toBe(factor);     expect(fn).toHaveBeenCalledTimes(2);   });<\/code><\/pre>\n<\/li>\n<li>\n<p>\u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 withMemo \u0432 \u0444\u0430\u0439\u043b\u0435 withMemo.js, \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0442\u0435\u0441\u0442\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u0440\u043e\u0448\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a<\/p>\n<details class=\"spoiler\">\n<summary> \u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const<\/code><\/pre>\n<\/div>\n<\/details>\n<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-346333","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/346333","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=346333"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/346333\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=346333"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=346333"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=346333"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}