A place to cache linked articles (think custom and personal wayback machine)
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

index.html 52KB

před 4 roky
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Functional Mixins in ECMAScript 2015 (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="http://raganwald.com/2015/06/17/functional-mixins.html">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. Functional Mixins in ECMAScript 2015 (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="http://raganwald.com/2015/06/17/functional-mixins.html">Source originale du contenu</a></h3>
  445. <p>In <a href="http://raganwald.com/2015/06/10/mixins.html">Prototypes are Objects</a>, we saw that you can emulate “mixins” using <code>Object.assign</code> on the prototypes that underly JavaScript “classes.” We’ll revisit this subject now and spend more time looking at mixing functionality into classes.</p>
  446. <p>First, a quick recap: In JavaScript, a “class” is implemented as a constructor function and its prototype, whether you write it directly, or use the <code>class</code> keyword. Instances of the class are created by calling the constructor with <code>new</code>. They “inherit” shared behaviour from the constructor’s <code>prototype</code> property.<sup id="fnref:delegate"><a href="#fn:delegate" class="footnote">1</a></sup></p>
  447. <h3 id="the-object-mixin-pattern">the object mixin pattern</h3>
  448. <p>One way to share behaviour scattered across multiple classes, or to untangle behaviour by factoring it out of an overweight prototype, is to extend a prototype with a <em>mixin</em>.</p>
  449. <p>Here’s a class of todo items:</p>
  450. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">class</span> <span class="nx">Todo</span> <span class="p">{</span>
  451. <span class="nx">constructor</span> <span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
  452. <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span> <span class="o">||</span> <span class="s1">'Untitled'</span><span class="p">;</span>
  453. <span class="k">this</span><span class="p">.</span><span class="nx">done</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
  454. <span class="p">}</span>
  455. <span class="k">do</span> <span class="p">()</span> <span class="p">{</span>
  456. <span class="k">this</span><span class="p">.</span><span class="nx">done</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
  457. <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
  458. <span class="p">}</span>
  459. <span class="nx">undo</span> <span class="p">()</span> <span class="p">{</span>
  460. <span class="k">this</span><span class="p">.</span><span class="nx">done</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
  461. <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
  462. <span class="p">}</span>
  463. <span class="p">}</span></code></pre></div>
  464. <p>And a “mixin” that is responsible for colour-coding:</p>
  465. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">Coloured</span> <span class="o">=</span> <span class="p">{</span>
  466. <span class="nx">setColourRGB</span> <span class="p">({</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">})</span> <span class="p">{</span>
  467. <span class="k">this</span><span class="p">.</span><span class="nx">colourCode</span> <span class="o">=</span> <span class="p">{</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">};</span>
  468. <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
  469. <span class="p">},</span>
  470. <span class="nx">getColourRGB</span> <span class="p">()</span> <span class="p">{</span>
  471. <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">colourCode</span><span class="p">;</span>
  472. <span class="p">}</span>
  473. <span class="p">};</span></code></pre></div>
  474. <p>Mixing colour coding into our Todo prototype is straightforward:</p>
  475. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">(</span><span class="nx">Todo</span><span class="p">.</span><span class="nx">prototype</span><span class="p">,</span> <span class="nx">Coloured</span><span class="p">);</span>
  476. <span class="k">new</span> <span class="nx">Todo</span><span class="p">(</span><span class="s1">'test'</span><span class="p">)</span>
  477. <span class="p">.</span><span class="nx">setColourRGB</span><span class="p">({</span><span class="nx">r</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">g</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">b</span><span class="o">:</span> <span class="mi">3</span><span class="p">})</span>
  478. <span class="c1">//=&gt; {"name":"test","done":false,"colourCode":{"r":1,"g":2,"b":3}}</span></code></pre></div>
  479. <p>We can “upgrade” it to have a <a href="http://raganwald.com/2015/06/04/classes-are-expressions.html">private property</a> if we wish:</p>
  480. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">colourCode</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s2">"colourCode"</span><span class="p">);</span>
  481. <span class="kr">const</span> <span class="nx">Coloured</span> <span class="o">=</span> <span class="p">{</span>
  482. <span class="nx">setColourRGB</span> <span class="p">({</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">})</span> <span class="p">{</span>
  483. <span class="k">this</span><span class="p">[</span><span class="nx">colourCode</span><span class="p">]</span><span class="o">=</span> <span class="p">{</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">};</span>
  484. <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
  485. <span class="p">},</span>
  486. <span class="nx">getColourRGB</span> <span class="p">()</span> <span class="p">{</span>
  487. <span class="k">return</span> <span class="k">this</span><span class="p">[</span><span class="nx">colourCode</span><span class="p">];</span>
  488. <span class="p">}</span>
  489. <span class="p">};</span></code></pre></div>
  490. <p>So far, very easy and very simple. This is a <em>pattern</em>, a recipe for solving a certain problem using a particular organization of code.</p>
  491. <p><a href="https://www.flickr.com/photos/chrisjrn/3771031871"><img src="/assets/images/macchiato.jpg" alt="Macchiato"/></a></p>
  492. <h3 id="functional-mixins">functional mixins</h3>
  493. <p>The object mixin we have above works properly, but our little recipe had two distinct steps: Define the mixin and then extend the class prototype. Angus Croll pointed out that it’s more elegant to define a mixin as a function rather than an object. He calls this a <a href="https://javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins/" title="A fresh look at JavaScript Mixins">functional mixin</a>. Here’s <code>Coloured</code> again, recast in functional form:</p>
  494. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">Coloured</span> <span class="o">=</span> <span class="p">(</span><span class="nx">target</span><span class="p">)</span> <span class="o">=&gt;</span>
  495. <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="p">{</span>
  496. <span class="nx">setColourRGB</span> <span class="p">({</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">})</span> <span class="p">{</span>
  497. <span class="k">this</span><span class="p">.</span><span class="nx">colourCode</span> <span class="o">=</span> <span class="p">{</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">};</span>
  498. <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
  499. <span class="p">},</span>
  500. <span class="nx">getColourRGB</span> <span class="p">()</span> <span class="p">{</span>
  501. <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">colourCode</span><span class="p">;</span>
  502. <span class="p">}</span>
  503. <span class="p">});</span>
  504. <span class="nx">Coloured</span><span class="p">(</span><span class="nx">Todo</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span></code></pre></div>
  505. <p>We can make ourselves a <em>factory function</em> that also names the pattern:</p>
  506. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">FunctionalMixin</span> <span class="o">=</span> <span class="p">(</span><span class="nx">behaviour</span><span class="p">)</span> <span class="o">=&gt;</span>
  507. <span class="nx">target</span> <span class="o">=&gt;</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">behaviour</span><span class="p">);</span></code></pre></div>
  508. <p>This allows us to define functional mixins neatly:</p>
  509. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">Coloured</span> <span class="o">=</span> <span class="nx">FunctionalMixin</span><span class="p">({</span>
  510. <span class="nx">setColourRGB</span> <span class="p">({</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">})</span> <span class="p">{</span>
  511. <span class="k">this</span><span class="p">.</span><span class="nx">colourCode</span> <span class="o">=</span> <span class="p">{</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">};</span>
  512. <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
  513. <span class="p">},</span>
  514. <span class="nx">getColourRGB</span> <span class="p">()</span> <span class="p">{</span>
  515. <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">colourCode</span><span class="p">;</span>
  516. <span class="p">}</span>
  517. <span class="p">});</span></code></pre></div>
  518. <h3 id="enumerability">enumerability</h3>
  519. <p>If we look at the way <code>class</code> defines prototypes, we find that the methods defined are not enumerable by default. This works around a common error where programmers iterate over the keys of an instance and fail to test for <code>.hasOwnProperty</code>.</p>
  520. <p>Our object mixin pattern does not work this way, the methods defined in a mixin <em>are</em> enumerable by default, and if we carefully defined them to be non-enumerable, <code>Object.assign</code> wouldn’t mix them into the target prototype, because <code>Object.assign</code> only assigns enumerable properties.</p>
  521. <p>And thus:</p>
  522. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">Coloured</span><span class="p">(</span><span class="nx">Todo</span><span class="p">.</span><span class="nx">prototype</span><span class="p">)</span>
  523. <span class="kr">const</span> <span class="nx">urgent</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Todo</span><span class="p">(</span><span class="s2">"finish blog post"</span><span class="p">);</span>
  524. <span class="nx">urgent</span><span class="p">.</span><span class="nx">setColourRGB</span><span class="p">({</span><span class="nx">r</span><span class="o">:</span> <span class="mi">256</span><span class="p">,</span> <span class="nx">g</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">b</span><span class="o">:</span> <span class="mi">0</span><span class="p">});</span>
  525. <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">property</span> <span class="k">in</span> <span class="nx">urgent</span><span class="p">)</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">property</span><span class="p">);</span>
  526. <span class="c1">// =&gt;</span>
  527. <span class="nx">name</span>
  528. <span class="nx">done</span>
  529. <span class="nx">colourCode</span>
  530. <span class="nx">setColourRGB</span>
  531. <span class="nx">getColourRGB</span></code></pre></div>
  532. <p>As we can see, the <code>setColourRGB</code> and <code>getColourRGB</code> methods are enumerated, although the <code>do</code> and <code>undo</code> methods are not. This can be a problem with naïve code: we can’t always rewrite all the <em>other</em> code to carefully use <code>.hasOwnProperty</code>.</p>
  533. <p>One benefit of functional mixins is that we can solve this problem and transparently make mixins behave like <code>class</code>:</p>
  534. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">FunctionalMixin</span> <span class="o">=</span> <span class="p">(</span><span class="nx">behaviour</span><span class="p">)</span> <span class="o">=&gt;</span>
  535. <span class="kd">function</span> <span class="p">(</span><span class="nx">target</span><span class="p">)</span> <span class="p">{</span>
  536. <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">property</span> <span class="nx">of</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyNames</span><span class="p">(</span><span class="nx">behaviour</span><span class="p">))</span>
  537. <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">property</span><span class="p">,</span> <span class="p">{</span> <span class="nx">value</span><span class="o">:</span> <span class="nx">behaviour</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="p">})</span>
  538. <span class="k">return</span> <span class="nx">target</span><span class="p">;</span>
  539. <span class="p">}</span></code></pre></div>
  540. <p>The above code supports methods with ordinary string names, but sometimes methods are declared with symbols (typically to create private methods). Although we won’t discuss that pattern yet, we can support those too:</p>
  541. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">FunctionalMixin</span> <span class="o">=</span> <span class="p">(</span><span class="nx">behaviour</span><span class="p">)</span> <span class="o">=&gt;</span>
  542. <span class="kd">function</span> <span class="p">(</span><span class="nx">target</span><span class="p">)</span> <span class="p">{</span>
  543. <span class="kr">const</span> <span class="nx">instanceKeys</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyNames</span><span class="p">(</span><span class="nx">behaviour</span><span class="p">)</span>
  544. <span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertySymbols</span><span class="p">(</span><span class="nx">behaviour</span><span class="p">));</span>
  545. <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">property</span> <span class="nx">of</span> <span class="nx">instanceKeys</span><span class="p">)</span>
  546. <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">property</span><span class="p">,</span> <span class="p">{</span> <span class="nx">value</span><span class="o">:</span> <span class="nx">behaviour</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="p">})</span>
  547. <span class="k">return</span> <span class="nx">target</span><span class="p">;</span>
  548. <span class="p">}</span></code></pre></div>
  549. <p>Writing this out as a pattern would be tedious and error-prone. Encapsulating the behaviour into a function is a small win.</p>
  550. <p><a href="https://www.flickr.com/photos/yellowskyphotography/7449919584"><img src="/assets/images/jbts.jpg" alt="Just Below the Surface"/></a></p>
  551. <h3 id="mixin-responsibilities">mixin responsibilities</h3>
  552. <p>Like classes, mixins are metaobjects: They define behaviour for instances. In addition to defining behaviour in the form of methods, classes are also responsible for initializing instances. But sometimes, classes and metaobjects handle additional responsibilities.</p>
  553. <p>For example, sometimes a particular concept is associated with some well-known constants. When using a class, can be handy to namespace such values in the class itself:</p>
  554. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">class</span> <span class="nx">Todo</span> <span class="p">{</span>
  555. <span class="nx">constructor</span> <span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
  556. <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span> <span class="o">||</span> <span class="nx">Todo</span><span class="p">.</span><span class="nx">DEFAULT_NAME</span><span class="p">;</span>
  557. <span class="k">this</span><span class="p">.</span><span class="nx">done</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
  558. <span class="p">}</span>
  559. <span class="k">do</span> <span class="p">()</span> <span class="p">{</span>
  560. <span class="k">this</span><span class="p">.</span><span class="nx">done</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
  561. <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
  562. <span class="p">}</span>
  563. <span class="nx">undo</span> <span class="p">()</span> <span class="p">{</span>
  564. <span class="k">this</span><span class="p">.</span><span class="nx">done</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
  565. <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
  566. <span class="p">}</span>
  567. <span class="p">}</span>
  568. <span class="nx">Todo</span><span class="p">.</span><span class="nx">DEFAULT_NAME</span> <span class="o">=</span> <span class="s1">'Untitled'</span><span class="p">;</span>
  569. <span class="c1">// If we are sticklers for read-only constants, we could write:</span>
  570. <span class="c1">// Object.defineProperty(Todo, 'DEFAULT_NAME', {value: 'Untitled'});</span></code></pre></div>
  571. <p>We can’t really do the same thing with simple mixins, because all of the properties in a simple mixin end up being mixed into the prototype of instances we create by default. For example, let’s say we want to define <code>Coloured.RED</code>, <code>Coloured.GREEN</code>, and <code>Coloured.BLUE</code>. But we don’t want any specific coloured instance to define <code>RED</code>, <code>GREEN</code>, or <code>BLUE</code>.</p>
  572. <p>Again, we can solve this problem by building a functional mixin. Our <code>FunctionalMixin</code> factory function will accept an optional dictionary of read-only mixin properties, provided they are associated with a special key:</p>
  573. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">shared</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s2">"shared"</span><span class="p">);</span>
  574. <span class="kd">function</span> <span class="nx">FunctionalMixin</span> <span class="p">(</span><span class="nx">behaviour</span><span class="p">)</span> <span class="p">{</span>
  575. <span class="kr">const</span> <span class="nx">instanceKeys</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyNames</span><span class="p">(</span><span class="nx">behaviour</span><span class="p">)</span>
  576. <span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertySymbols</span><span class="p">(</span><span class="nx">behaviour</span><span class="p">))</span>
  577. <span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">key</span> <span class="o">=&gt;</span> <span class="nx">key</span> <span class="o">!==</span> <span class="nx">shared</span><span class="p">);</span>
  578. <span class="kr">const</span> <span class="nx">sharedBehaviour</span> <span class="o">=</span> <span class="nx">behaviour</span><span class="p">[</span><span class="nx">shared</span><span class="p">]</span> <span class="o">||</span> <span class="p">{};</span>
  579. <span class="kr">const</span> <span class="nx">sharedKeys</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyNames</span><span class="p">(</span><span class="nx">sharedBehaviour</span><span class="p">)</span>
  580. <span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertySymbols</span><span class="p">(</span><span class="nx">sharedBehaviour</span><span class="p">));</span>
  581. <span class="kd">function</span> <span class="nx">mixin</span> <span class="p">(</span><span class="nx">target</span><span class="p">)</span> <span class="p">{</span>
  582. <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">property</span> <span class="nx">of</span> <span class="nx">instanceKeys</span><span class="p">)</span>
  583. <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">property</span><span class="p">,</span> <span class="p">{</span> <span class="nx">value</span><span class="o">:</span> <span class="nx">behaviour</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="p">});</span>
  584. <span class="k">return</span> <span class="nx">target</span><span class="p">;</span>
  585. <span class="p">}</span>
  586. <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">property</span> <span class="nx">of</span> <span class="nx">sharedKeys</span><span class="p">)</span>
  587. <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">mixin</span><span class="p">,</span> <span class="nx">property</span><span class="p">,</span> <span class="p">{</span>
  588. <span class="nx">value</span><span class="o">:</span> <span class="nx">sharedBehaviour</span><span class="p">[</span><span class="nx">property</span><span class="p">],</span>
  589. <span class="nx">enumerable</span><span class="o">:</span> <span class="nx">sharedBehaviour</span><span class="p">.</span><span class="nx">propertyIsEnumerable</span><span class="p">(</span><span class="nx">property</span><span class="p">)</span>
  590. <span class="p">});</span>
  591. <span class="k">return</span> <span class="nx">mixin</span><span class="p">;</span>
  592. <span class="p">}</span>
  593. <span class="nx">FunctionalMixin</span><span class="p">.</span><span class="nx">shared</span> <span class="o">=</span> <span class="nx">shared</span><span class="p">;</span></code></pre></div>
  594. <p>And now we can write:</p>
  595. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">Coloured</span> <span class="o">=</span> <span class="nx">FunctionalMixin</span><span class="p">({</span>
  596. <span class="nx">setColourRGB</span> <span class="p">({</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">})</span> <span class="p">{</span>
  597. <span class="k">this</span><span class="p">.</span><span class="nx">colourCode</span> <span class="o">=</span> <span class="p">{</span><span class="nx">r</span><span class="p">,</span> <span class="nx">g</span><span class="p">,</span> <span class="nx">b</span><span class="p">};</span>
  598. <span class="k">return</span> <span class="k">this</span><span class="p">;</span>
  599. <span class="p">},</span>
  600. <span class="nx">getColourRGB</span> <span class="p">()</span> <span class="p">{</span>
  601. <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">colourCode</span><span class="p">;</span>
  602. <span class="p">},</span>
  603. <span class="p">[</span><span class="nx">FunctionalMixin</span><span class="p">.</span><span class="nx">shared</span><span class="p">]</span><span class="o">:</span> <span class="p">{</span>
  604. <span class="nx">RED</span><span class="o">:</span> <span class="p">{</span> <span class="nx">r</span><span class="o">:</span> <span class="mi">255</span><span class="p">,</span> <span class="nx">g</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">b</span><span class="o">:</span> <span class="mi">0</span> <span class="p">},</span>
  605. <span class="nx">GREEN</span><span class="o">:</span> <span class="p">{</span> <span class="nx">r</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">g</span><span class="o">:</span> <span class="mi">255</span><span class="p">,</span> <span class="nx">b</span><span class="o">:</span> <span class="mi">0</span> <span class="p">},</span>
  606. <span class="nx">BLUE</span><span class="o">:</span> <span class="p">{</span> <span class="nx">r</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">g</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">b</span><span class="o">:</span> <span class="mi">255</span> <span class="p">},</span>
  607. <span class="p">}</span>
  608. <span class="p">});</span>
  609. <span class="nx">Coloured</span><span class="p">(</span><span class="nx">Todo</span><span class="p">.</span><span class="nx">prototype</span><span class="p">)</span>
  610. <span class="kr">const</span> <span class="nx">urgent</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Todo</span><span class="p">(</span><span class="s2">"finish blog post"</span><span class="p">);</span>
  611. <span class="nx">urgent</span><span class="p">.</span><span class="nx">setColourRGB</span><span class="p">(</span><span class="nx">Coloured</span><span class="p">.</span><span class="nx">RED</span><span class="p">);</span>
  612. <span class="nx">urgent</span><span class="p">.</span><span class="nx">getColourRGB</span><span class="p">()</span>
  613. <span class="c1">//=&gt; {"r":255,"g":0,"b":0}</span></code></pre></div>
  614. <h3 id="mixin-methods">mixin methods</h3>
  615. <p>Such properties need not be values. Sometimes, classes have methods. And likewise, sometimes it makes sense for a mixin to have its own methods. One example concerns <code>instanceof</code>.</p>
  616. <p>In earlier versions of ECMAScript, <code>instanceof</code> is an operator that checks to see whether the prototype of an instance matches the prototype of a constructor function. It works just fine with “classes,” but it does not work “out of the box” with mixins:</p>
  617. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">urgent</span> <span class="k">instanceof</span> <span class="nx">Todo</span>
  618. <span class="c1">//=&gt; true</span>
  619. <span class="nx">urgent</span> <span class="k">instanceof</span> <span class="nx">Coloured</span>
  620. <span class="c1">//=&gt; false</span></code></pre></div>
  621. <p>To handle this and some other issues where programmers are creating their own notion of dynamic types, or managing prototypes directly with <code>Object.create</code> and <code>Object.setPrototypeOf</code>, ECMAScript 2015 provides a way to override the built-in <code>instanceof</code> behaviour: An object can define a method associated with a well-known symbol, <code>Symbol.instanceOf</code>.</p>
  622. <p>We can test this quickly:<sup id="fnref:but"><a href="#fn:but" class="footnote">2</a></sup></p>
  623. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">Coloured</span><span class="p">[</span><span class="nx">Symbol</span><span class="p">.</span><span class="nx">instanceOf</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="nx">instance</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kc">true</span>
  624. <span class="nx">urgent</span> <span class="k">instanceof</span> <span class="nx">Coloured</span>
  625. <span class="c1">//=&gt; true</span>
  626. <span class="p">{}</span> <span class="k">instanceof</span> <span class="nx">Coloured</span>
  627. <span class="c1">//=&gt; true</span></code></pre></div>
  628. <p>Of course, that is not semantically correct. But using this technique, we can write:</p>
  629. <div class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">shared</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s2">"shared"</span><span class="p">);</span>
  630. <span class="kd">function</span> <span class="nx">FunctionalMixin</span> <span class="p">(</span><span class="nx">behaviour</span><span class="p">)</span> <span class="p">{</span>
  631. <span class="kr">const</span> <span class="nx">instanceKeys</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyNames</span><span class="p">(</span><span class="nx">behaviour</span><span class="p">)</span>
  632. <span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertySymbols</span><span class="p">(</span><span class="nx">behaviour</span><span class="p">))</span>
  633. <span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">key</span> <span class="o">=&gt;</span> <span class="nx">key</span> <span class="o">!==</span> <span class="nx">shared</span><span class="p">);</span>
  634. <span class="kr">const</span> <span class="nx">sharedBehaviour</span> <span class="o">=</span> <span class="nx">behaviour</span><span class="p">[</span><span class="nx">shared</span><span class="p">]</span> <span class="o">||</span> <span class="p">{};</span>
  635. <span class="kr">const</span> <span class="nx">sharedKeys</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyNames</span><span class="p">(</span><span class="nx">sharedBehaviour</span><span class="p">)</span>
  636. <span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertySymbols</span><span class="p">(</span><span class="nx">sharedBehaviour</span><span class="p">));</span>
  637. <span class="kr">const</span> <span class="nx">typeTag</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s2">"isA"</span><span class="p">);</span>
  638. <span class="kd">function</span> <span class="nx">mixin</span> <span class="p">(</span><span class="nx">target</span><span class="p">)</span> <span class="p">{</span>
  639. <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">property</span> <span class="nx">of</span> <span class="nx">instanceKeys</span><span class="p">)</span>
  640. <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">property</span><span class="p">,</span> <span class="p">{</span> <span class="nx">value</span><span class="o">:</span> <span class="nx">behaviour</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="p">});</span>
  641. <span class="nx">target</span><span class="p">[</span><span class="nx">typeTag</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
  642. <span class="k">return</span> <span class="nx">target</span><span class="p">;</span>
  643. <span class="p">}</span>
  644. <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">property</span> <span class="nx">of</span> <span class="nx">sharedKeys</span><span class="p">)</span>
  645. <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">mixin</span><span class="p">,</span> <span class="nx">property</span><span class="p">,</span> <span class="p">{</span>
  646. <span class="nx">value</span><span class="o">:</span> <span class="nx">sharedBehaviour</span><span class="p">[</span><span class="nx">property</span><span class="p">],</span>
  647. <span class="nx">enumerable</span><span class="o">:</span> <span class="nx">sharedBehaviour</span><span class="p">.</span><span class="nx">propertyIsEnumerable</span><span class="p">(</span><span class="nx">property</span><span class="p">)</span>
  648. <span class="p">});</span>
  649. <span class="nx">mixin</span><span class="p">[</span><span class="nx">Symbol</span><span class="p">.</span><span class="nx">instanceOf</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="nx">instance</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="o">!!</span><span class="nx">instance</span><span class="p">[</span><span class="nx">typeTag</span><span class="p">];</span>
  650. <span class="k">return</span> <span class="nx">mixin</span><span class="p">;</span>
  651. <span class="p">}</span>
  652. <span class="nx">FunctionalMixin</span><span class="p">.</span><span class="nx">shared</span> <span class="o">=</span> <span class="nx">shared</span><span class="p">;</span>
  653. <span class="nx">urgent</span> <span class="k">instanceof</span> <span class="nx">Coloured</span>
  654. <span class="c1">//=&gt; true</span>
  655. <span class="p">{}</span> <span class="k">instanceof</span> <span class="nx">Coloured</span>
  656. <span class="c1">//=&gt; false</span></code></pre></div>
  657. <p>Do you need to implement <code>instanceof</code>? Quite possibly not. “Rolling your own polymorphism” is usually a last resort. But it can be handy for writing test cases, and a few daring framework developers might be working on multiple dispatch and pattern-matching for functions.</p>
  658. <h3 id="summary">summary</h3>
  659. <p>The charm of the object mixin pattern is its simplicity: It really does not need an abstraction wrapped around an object literal and <code>Object.assign</code>.</p>
  660. <p>However, behaviour defined with the mixin pattern is <em>slightly</em> different than behaviour defined with the <code>class</code> keyword. Two examples of these differences are enumerability and mixin properties (such as constants and mixin methods like <code>[Symbol.instanceof]</code>).</p>
  661. <p>Functional mixins provide an opportunity to implement such functionality, at the cost of some complexity in the <code>FunctionalMixin</code> function that creates functional mixins.</p>
  662. <p>As a general rule, it’s best to have things behave as similarly as possible in the domain code, and this sometimes does involve some extra complexity in the infrastructure code. But that is more of a guideline than a hard-and-fast rule, and for this reason there is a place for both the object mixin pattern <em>and</em> functional mixins in JavaScript.</p>
  663. </article>
  664. </section>
  665. <nav id="jumpto">
  666. <p>
  667. <a href="/david/blog/">Accueil du blog</a> |
  668. <a href="http://raganwald.com/2015/06/17/functional-mixins.html">Source originale</a> |
  669. <a href="/david/stream/2019/">Accueil du flux</a>
  670. </p>
  671. </nav>
  672. <footer>
  673. <div>
  674. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  675. <p>
  676. Bonjour/Hi!
  677. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  678. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  679. </p>
  680. <p>
  681. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  682. </p>
  683. <p>
  684. Voici quelques articles choisis :
  685. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  686. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  687. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  688. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  689. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  690. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  691. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  692. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  693. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  694. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  695. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  696. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  697. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  698. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  699. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  700. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  701. </p>
  702. <p>
  703. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  704. </p>
  705. <p>
  706. Je ne traque pas ta navigation mais mon
  707. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  708. conserve des logs d’accès.
  709. </p>
  710. </div>
  711. </footer>
  712. <script type="text/javascript">
  713. ;(_ => {
  714. const jumper = document.getElementById('jumper')
  715. jumper.addEventListener('click', e => {
  716. e.preventDefault()
  717. const anchor = e.target.getAttribute('href')
  718. const targetEl = document.getElementById(anchor.substring(1))
  719. targetEl.scrollIntoView({behavior: 'smooth'})
  720. })
  721. })()
  722. </script>