[{"data":1,"prerenderedAt":1211},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjavascript-mixins-composition-explained":3},{"id":4,"title":5,"body":6,"description":1196,"difficulty":1197,"extension":1198,"framework":1199,"frameworkSlug":1200,"meta":1201,"navigation":186,"order":177,"path":1202,"qaPath":1203,"seo":1204,"stem":1205,"subtopic":1206,"topic":1207,"topicSlug":1208,"updated":1209,"__hash__":1210},"blog\u002Fblog\u002Fjavascript-mixins-composition-explained.md","JavaScript Mixins & Composition — Sharing Behavior Beyond Single Inheritance",{"type":7,"value":8,"toc":1185},"minimark",[9,14,57,60,64,75,283,299,303,306,454,457,461,471,723,736,740,758,932,935,939,945,1035,1038,1042,1045,1067,1100,1103,1107,1110,1131,1134,1138,1178,1181],[10,11,13],"h2",{"id":12},"the-limits-of-single-inheritance","The limits of single inheritance",[15,16,17,18,22,23,27,28,32,33,36,37,40,41,44,45,48,49,52,53,56],"p",{},"JavaScript classes support only ",[19,20,21],"strong",{},"single inheritance",": a class can ",[24,25,26],"code",{},"extend"," exactly one\nparent. That's fine for strict \"is-a\" hierarchies, but real software often needs to share\n",[29,30,31],"em",{},"capabilities"," across unrelated types. A ",[24,34,35],{},"Robot"," and a ",[24,38,39],{},"Car"," might both need\n\"serializable,\" a ",[24,42,43],{},"User"," and an ",[24,46,47],{},"Order"," might both need \"timestamped\" — yet they don't share\na common ancestor. Forcing them into one inheritance tree leads to the infamous deep, brittle\nhierarchies and \"god base classes.\" ",[19,50,51],{},"Mixins"," and ",[19,54,55],{},"composition"," solve this by letting you\ncombine independent behaviors without a single rigid lineage.",[15,58,59],{},"The guiding principle, often summarized as \"favor composition over inheritance,\" is that\nbuilding objects from small, focused pieces is more flexible than carving an ever-deeper\nclass tree.",[10,61,63],{"id":62},"what-is-a-mixin","What is a mixin?",[15,65,66,67,70,71,74],{},"A ",[19,68,69],{},"mixin"," is a reusable bundle of methods that you mix into a class or object, independent\nof the inheritance chain. The simplest form copies methods onto a prototype with\n",[24,72,73],{},"Object.assign",".",[76,77,82],"pre",{"className":78,"code":79,"language":80,"meta":81,"style":81},"language-js shiki shiki-themes github-light github-dark","const serializable = {\n  toJSON() { return JSON.stringify(this) },\n  fromJSON(s) { return Object.assign(this, JSON.parse(s)) }\n}\n\nclass User {\n  constructor(name) { this.name = name }\n}\nObject.assign(User.prototype, serializable)   \u002F\u002F mix in the behavior\n\nnew User('Ada').toJSON()   \u002F\u002F '{\"name\":\"Ada\"}'\n","js","",[24,83,84,104,134,175,181,188,199,223,228,252,257],{"__ignoreMap":81},[85,86,89,93,97,100],"span",{"class":87,"line":88},"line",1,[85,90,92],{"class":91},"szBVR","const",[85,94,96],{"class":95},"sj4cs"," serializable",[85,98,99],{"class":91}," =",[85,101,103],{"class":102},"sVt8B"," {\n",[85,105,107,111,114,117,120,122,125,128,131],{"class":87,"line":106},2,[85,108,110],{"class":109},"sScJk","  toJSON",[85,112,113],{"class":102},"() { ",[85,115,116],{"class":91},"return",[85,118,119],{"class":95}," JSON",[85,121,74],{"class":102},[85,123,124],{"class":109},"stringify",[85,126,127],{"class":102},"(",[85,129,130],{"class":95},"this",[85,132,133],{"class":102},") },\n",[85,135,137,140,142,146,149,151,154,157,159,161,164,167,169,172],{"class":87,"line":136},3,[85,138,139],{"class":109},"  fromJSON",[85,141,127],{"class":102},[85,143,145],{"class":144},"s4XuR","s",[85,147,148],{"class":102},") { ",[85,150,116],{"class":91},[85,152,153],{"class":102}," Object.",[85,155,156],{"class":109},"assign",[85,158,127],{"class":102},[85,160,130],{"class":95},[85,162,163],{"class":102},", ",[85,165,166],{"class":95},"JSON",[85,168,74],{"class":102},[85,170,171],{"class":109},"parse",[85,173,174],{"class":102},"(s)) }\n",[85,176,178],{"class":87,"line":177},4,[85,179,180],{"class":102},"}\n",[85,182,184],{"class":87,"line":183},5,[85,185,187],{"emptyLinePlaceholder":186},true,"\n",[85,189,191,194,197],{"class":87,"line":190},6,[85,192,193],{"class":91},"class",[85,195,196],{"class":109}," User",[85,198,103],{"class":102},[85,200,202,205,207,210,212,214,217,220],{"class":87,"line":201},7,[85,203,204],{"class":91},"  constructor",[85,206,127],{"class":102},[85,208,209],{"class":144},"name",[85,211,148],{"class":102},[85,213,130],{"class":95},[85,215,216],{"class":102},".name ",[85,218,219],{"class":91},"=",[85,221,222],{"class":102}," name }\n",[85,224,226],{"class":87,"line":225},8,[85,227,180],{"class":102},[85,229,231,234,236,238,240,242,245,248],{"class":87,"line":230},9,[85,232,233],{"class":102},"Object.",[85,235,156],{"class":109},[85,237,127],{"class":102},[85,239,43],{"class":95},[85,241,74],{"class":102},[85,243,244],{"class":95},"prototype",[85,246,247],{"class":102},", serializable)   ",[85,249,251],{"class":250},"sJ8bj","\u002F\u002F mix in the behavior\n",[85,253,255],{"class":87,"line":254},10,[85,256,187],{"emptyLinePlaceholder":186},[85,258,260,263,265,267,271,274,277,280],{"class":87,"line":259},11,[85,261,262],{"class":91},"new",[85,264,196],{"class":109},[85,266,127],{"class":102},[85,268,270],{"class":269},"sZZnC","'Ada'",[85,272,273],{"class":102},").",[85,275,276],{"class":109},"toJSON",[85,278,279],{"class":102},"()   ",[85,281,282],{"class":250},"\u002F\u002F '{\"name\":\"Ada\"}'\n",[15,284,285,286,288,289,291,292,294,295,298],{},"Now any ",[24,287,43],{}," instance has ",[24,290,276],{},", even though ",[24,293,43],{}," doesn't extend anything. The same\n",[24,296,297],{},"serializable"," object can be mixed into completely unrelated classes.",[10,300,302],{"id":301},"object-level-mixins","Object-level mixins",[15,304,305],{},"You can also mix capabilities directly into a single object rather than a whole class —\nhandy for one-off composition.",[76,307,309],{"className":78,"code":308,"language":80,"meta":81,"style":81},"const comparable = {\n  equals(other) { return this.id === other.id }\n}\nconst loggable = {\n  log() { console.log(`[${this.constructor.name}] ${this.id}`) }\n}\n\nconst entity = Object.assign({ id: 1 }, comparable, loggable)\nentity.equals({ id: 1 })   \u002F\u002F true\n",[24,310,311,322,348,352,363,406,410,414,436],{"__ignoreMap":81},[85,312,313,315,318,320],{"class":87,"line":88},[85,314,92],{"class":91},[85,316,317],{"class":95}," comparable",[85,319,99],{"class":91},[85,321,103],{"class":102},[85,323,324,327,329,332,334,336,339,342,345],{"class":87,"line":106},[85,325,326],{"class":109},"  equals",[85,328,127],{"class":102},[85,330,331],{"class":144},"other",[85,333,148],{"class":102},[85,335,116],{"class":91},[85,337,338],{"class":95}," this",[85,340,341],{"class":102},".id ",[85,343,344],{"class":91},"===",[85,346,347],{"class":102}," other.id }\n",[85,349,350],{"class":87,"line":136},[85,351,180],{"class":102},[85,353,354,356,359,361],{"class":87,"line":177},[85,355,92],{"class":91},[85,357,358],{"class":95}," loggable",[85,360,99],{"class":91},[85,362,103],{"class":102},[85,364,365,368,371,374,376,379,381,383,386,388,390,393,395,397,400,403],{"class":87,"line":183},[85,366,367],{"class":109},"  log",[85,369,370],{"class":102},"() { console.",[85,372,373],{"class":109},"log",[85,375,127],{"class":102},[85,377,378],{"class":269},"`[${",[85,380,130],{"class":95},[85,382,74],{"class":269},[85,384,385],{"class":95},"constructor",[85,387,74],{"class":269},[85,389,209],{"class":102},[85,391,392],{"class":269},"}] ${",[85,394,130],{"class":95},[85,396,74],{"class":269},[85,398,399],{"class":102},"id",[85,401,402],{"class":269},"}`",[85,404,405],{"class":102},") }\n",[85,407,408],{"class":87,"line":190},[85,409,180],{"class":102},[85,411,412],{"class":87,"line":201},[85,413,187],{"emptyLinePlaceholder":186},[85,415,416,418,421,423,425,427,430,433],{"class":87,"line":225},[85,417,92],{"class":91},[85,419,420],{"class":95}," entity",[85,422,99],{"class":91},[85,424,153],{"class":102},[85,426,156],{"class":109},[85,428,429],{"class":102},"({ id: ",[85,431,432],{"class":95},"1",[85,434,435],{"class":102}," }, comparable, loggable)\n",[85,437,438,441,444,446,448,451],{"class":87,"line":230},[85,439,440],{"class":102},"entity.",[85,442,443],{"class":109},"equals",[85,445,429],{"class":102},[85,447,432],{"class":95},[85,449,450],{"class":102}," })   ",[85,452,453],{"class":250},"\u002F\u002F true\n",[15,455,456],{},"Each source's methods are copied in; later sources override earlier ones on key conflicts —\nthe same \"last wins\" rule as object spread.",[10,458,460],{"id":459},"functional-mixins-class-factories","Functional mixins (class factories)",[15,462,463,464,467,468,74],{},"The most powerful mixin pattern is a ",[19,465,466],{},"function that takes a base class and returns a new\nsubclass"," extending it. This composes cleanly and supports ",[24,469,470],{},"super",[76,472,474],{"className":78,"code":473,"language":80,"meta":81,"style":81},"const Timestamped = (Base) => class extends Base {\n  constructor(...args) {\n    super(...args)\n    this.createdAt = Date.now()\n  }\n  touch() { this.updatedAt = Date.now() }\n}\n\nconst Serializable = (Base) => class extends Base {\n  serialize() { return JSON.stringify(this) }\n}\n\nclass Model {}\nclass Document extends Serializable(Timestamped(Model)) {}   \u002F\u002F stack mixins\n\nconst doc = new Document()\ndoc.createdAt    \u002F\u002F a timestamp\ndoc.serialize()  \u002F\u002F works too\n",[24,475,476,508,523,535,554,559,580,584,588,613,634,638,643,654,677,682,699,708],{"__ignoreMap":81},[85,477,478,480,483,485,488,491,494,497,500,503,506],{"class":87,"line":88},[85,479,92],{"class":91},[85,481,482],{"class":109}," Timestamped",[85,484,99],{"class":91},[85,486,487],{"class":102}," (",[85,489,490],{"class":144},"Base",[85,492,493],{"class":102},") ",[85,495,496],{"class":91},"=>",[85,498,499],{"class":91}," class",[85,501,502],{"class":91}," extends",[85,504,505],{"class":109}," Base",[85,507,103],{"class":102},[85,509,510,512,514,517,520],{"class":87,"line":106},[85,511,204],{"class":91},[85,513,127],{"class":102},[85,515,516],{"class":91},"...",[85,518,519],{"class":144},"args",[85,521,522],{"class":102},") {\n",[85,524,525,528,530,532],{"class":87,"line":136},[85,526,527],{"class":95},"    super",[85,529,127],{"class":102},[85,531,516],{"class":91},[85,533,534],{"class":102},"args)\n",[85,536,537,540,543,545,548,551],{"class":87,"line":177},[85,538,539],{"class":95},"    this",[85,541,542],{"class":102},".createdAt ",[85,544,219],{"class":91},[85,546,547],{"class":102}," Date.",[85,549,550],{"class":109},"now",[85,552,553],{"class":102},"()\n",[85,555,556],{"class":87,"line":183},[85,557,558],{"class":102},"  }\n",[85,560,561,564,566,568,571,573,575,577],{"class":87,"line":190},[85,562,563],{"class":109},"  touch",[85,565,113],{"class":102},[85,567,130],{"class":95},[85,569,570],{"class":102},".updatedAt ",[85,572,219],{"class":91},[85,574,547],{"class":102},[85,576,550],{"class":109},[85,578,579],{"class":102},"() }\n",[85,581,582],{"class":87,"line":201},[85,583,180],{"class":102},[85,585,586],{"class":87,"line":225},[85,587,187],{"emptyLinePlaceholder":186},[85,589,590,592,595,597,599,601,603,605,607,609,611],{"class":87,"line":230},[85,591,92],{"class":91},[85,593,594],{"class":109}," Serializable",[85,596,99],{"class":91},[85,598,487],{"class":102},[85,600,490],{"class":144},[85,602,493],{"class":102},[85,604,496],{"class":91},[85,606,499],{"class":91},[85,608,502],{"class":91},[85,610,505],{"class":109},[85,612,103],{"class":102},[85,614,615,618,620,622,624,626,628,630,632],{"class":87,"line":254},[85,616,617],{"class":109},"  serialize",[85,619,113],{"class":102},[85,621,116],{"class":91},[85,623,119],{"class":95},[85,625,74],{"class":102},[85,627,124],{"class":109},[85,629,127],{"class":102},[85,631,130],{"class":95},[85,633,405],{"class":102},[85,635,636],{"class":87,"line":259},[85,637,180],{"class":102},[85,639,641],{"class":87,"line":640},12,[85,642,187],{"emptyLinePlaceholder":186},[85,644,646,648,651],{"class":87,"line":645},13,[85,647,193],{"class":91},[85,649,650],{"class":109}," Model",[85,652,653],{"class":102}," {}\n",[85,655,657,659,662,664,666,668,671,674],{"class":87,"line":656},14,[85,658,193],{"class":91},[85,660,661],{"class":109}," Document",[85,663,502],{"class":91},[85,665,594],{"class":109},[85,667,127],{"class":102},[85,669,670],{"class":109},"Timestamped",[85,672,673],{"class":102},"(Model)) {}   ",[85,675,676],{"class":250},"\u002F\u002F stack mixins\n",[85,678,680],{"class":87,"line":679},15,[85,681,187],{"emptyLinePlaceholder":186},[85,683,685,687,690,692,695,697],{"class":87,"line":684},16,[85,686,92],{"class":91},[85,688,689],{"class":95}," doc",[85,691,99],{"class":91},[85,693,694],{"class":91}," new",[85,696,661],{"class":109},[85,698,553],{"class":102},[85,700,702,705],{"class":87,"line":701},17,[85,703,704],{"class":102},"doc.createdAt    ",[85,706,707],{"class":250},"\u002F\u002F a timestamp\n",[85,709,711,714,717,720],{"class":87,"line":710},18,[85,712,713],{"class":102},"doc.",[85,715,716],{"class":109},"serialize",[85,718,719],{"class":102},"()  ",[85,721,722],{"class":250},"\u002F\u002F works too\n",[15,724,725,726,728,729,731,732,735],{},"This \"class factory\" approach is more robust than ",[24,727,73],{}," because each mixin becomes\na real link in the prototype chain — so ",[24,730,470],{}," works, ",[24,733,734],{},"instanceof","-style checks behave, and\nordering is explicit. Libraries and frameworks favor this pattern.",[10,737,739],{"id":738},"why-mixins-beat-deep-hierarchies","Why mixins beat deep hierarchies",[15,741,742,743,163,746,749,750,753,754,757],{},"Consider modeling abilities like ",[29,744,745],{},"flying",[29,747,748],{},"swimming",", and ",[29,751,752],{},"walking"," across animals. With\nsingle inheritance you'd contort the tree (",[24,755,756],{},"FlyingSwimmingAnimal","?). With mixins each ability\nis an independent piece you compose as needed.",[76,759,761],{"className":78,"code":760,"language":80,"meta":81,"style":81},"const CanFly  = (B) => class extends B { fly()  { return 'flying'  } }\nconst CanSwim = (B) => class extends B { swim() { return 'swimming'} }\n\nclass Animal {}\nclass Duck extends CanFly(CanSwim(Animal)) {}   \u002F\u002F both abilities, no awkward tree\nclass Fish extends CanSwim(Animal) {}            \u002F\u002F just one\n\nnew Duck().fly()    \u002F\u002F 'flying'\nnew Duck().swim()   \u002F\u002F 'swimming'\n",[24,762,763,806,844,848,857,879,896,900,917],{"__ignoreMap":81},[85,764,765,767,770,773,775,778,780,782,784,786,789,792,795,798,800,803],{"class":87,"line":88},[85,766,92],{"class":91},[85,768,769],{"class":109}," CanFly",[85,771,772],{"class":91},"  =",[85,774,487],{"class":102},[85,776,777],{"class":144},"B",[85,779,493],{"class":102},[85,781,496],{"class":91},[85,783,499],{"class":91},[85,785,502],{"class":91},[85,787,788],{"class":109}," B",[85,790,791],{"class":102}," { ",[85,793,794],{"class":109},"fly",[85,796,797],{"class":102},"()  { ",[85,799,116],{"class":91},[85,801,802],{"class":269}," 'flying'",[85,804,805],{"class":102},"  } }\n",[85,807,808,810,813,815,817,819,821,823,825,827,829,831,834,836,838,841],{"class":87,"line":106},[85,809,92],{"class":91},[85,811,812],{"class":109}," CanSwim",[85,814,99],{"class":91},[85,816,487],{"class":102},[85,818,777],{"class":144},[85,820,493],{"class":102},[85,822,496],{"class":91},[85,824,499],{"class":91},[85,826,502],{"class":91},[85,828,788],{"class":109},[85,830,791],{"class":102},[85,832,833],{"class":109},"swim",[85,835,113],{"class":102},[85,837,116],{"class":91},[85,839,840],{"class":269}," 'swimming'",[85,842,843],{"class":102},"} }\n",[85,845,846],{"class":87,"line":136},[85,847,187],{"emptyLinePlaceholder":186},[85,849,850,852,855],{"class":87,"line":177},[85,851,193],{"class":91},[85,853,854],{"class":109}," Animal",[85,856,653],{"class":102},[85,858,859,861,864,866,868,870,873,876],{"class":87,"line":183},[85,860,193],{"class":91},[85,862,863],{"class":109}," Duck",[85,865,502],{"class":91},[85,867,769],{"class":109},[85,869,127],{"class":102},[85,871,872],{"class":109},"CanSwim",[85,874,875],{"class":102},"(Animal)) {}   ",[85,877,878],{"class":250},"\u002F\u002F both abilities, no awkward tree\n",[85,880,881,883,886,888,890,893],{"class":87,"line":190},[85,882,193],{"class":91},[85,884,885],{"class":109}," Fish",[85,887,502],{"class":91},[85,889,812],{"class":109},[85,891,892],{"class":102},"(Animal) {}            ",[85,894,895],{"class":250},"\u002F\u002F just one\n",[85,897,898],{"class":87,"line":201},[85,899,187],{"emptyLinePlaceholder":186},[85,901,902,904,906,909,911,914],{"class":87,"line":225},[85,903,262],{"class":91},[85,905,863],{"class":109},[85,907,908],{"class":102},"().",[85,910,794],{"class":109},[85,912,913],{"class":102},"()    ",[85,915,916],{"class":250},"\u002F\u002F 'flying'\n",[85,918,919,921,923,925,927,929],{"class":87,"line":230},[85,920,262],{"class":91},[85,922,863],{"class":109},[85,924,908],{"class":102},[85,926,833],{"class":109},[85,928,279],{"class":102},[85,930,931],{"class":250},"\u002F\u002F 'swimming'\n",[15,933,934],{},"Each capability is defined once and combined à la carte. Adding a new ability doesn't require\nreshaping a hierarchy — you just write another mixin.",[10,936,938],{"id":937},"composition-with-plain-objects-delegation","Composition with plain objects (delegation)",[15,940,941,942,944],{},"Beyond mixins, pure ",[19,943,55],{}," builds an object's behavior by holding other objects and\ndelegating to them, rather than inheriting at all.",[76,946,948],{"className":78,"code":947,"language":80,"meta":81,"style":81},"function createPlayer(name) {\n  const health = createHealth(100)     \u002F\u002F composed pieces\n  const inventory = createInventory()\n  return {\n    name,\n    takeDamage: health.decrease,        \u002F\u002F delegate\n    addItem: inventory.add,\n  }\n}\n",[24,949,950,964,988,1002,1009,1014,1022,1027,1031],{"__ignoreMap":81},[85,951,952,955,958,960,962],{"class":87,"line":88},[85,953,954],{"class":91},"function",[85,956,957],{"class":109}," createPlayer",[85,959,127],{"class":102},[85,961,209],{"class":144},[85,963,522],{"class":102},[85,965,966,969,972,974,977,979,982,985],{"class":87,"line":106},[85,967,968],{"class":91},"  const",[85,970,971],{"class":95}," health",[85,973,99],{"class":91},[85,975,976],{"class":109}," createHealth",[85,978,127],{"class":102},[85,980,981],{"class":95},"100",[85,983,984],{"class":102},")     ",[85,986,987],{"class":250},"\u002F\u002F composed pieces\n",[85,989,990,992,995,997,1000],{"class":87,"line":136},[85,991,968],{"class":91},[85,993,994],{"class":95}," inventory",[85,996,99],{"class":91},[85,998,999],{"class":109}," createInventory",[85,1001,553],{"class":102},[85,1003,1004,1007],{"class":87,"line":177},[85,1005,1006],{"class":91},"  return",[85,1008,103],{"class":102},[85,1010,1011],{"class":87,"line":183},[85,1012,1013],{"class":102},"    name,\n",[85,1015,1016,1019],{"class":87,"line":190},[85,1017,1018],{"class":102},"    takeDamage: health.decrease,        ",[85,1020,1021],{"class":250},"\u002F\u002F delegate\n",[85,1023,1024],{"class":87,"line":201},[85,1025,1026],{"class":102},"    addItem: inventory.add,\n",[85,1028,1029],{"class":87,"line":225},[85,1030,558],{"class":102},[85,1032,1033],{"class":87,"line":230},[85,1034,180],{"class":102},[15,1036,1037],{},"This \"has-a\" style keeps each piece independently testable and swappable. It avoids the\nprototype chain entirely and is often the most flexible approach for complex domain objects.",[10,1039,1041],{"id":1040},"pitfalls-of-mixins","Pitfalls of mixins",[15,1043,1044],{},"Mixins are powerful but have sharp edges to respect:",[76,1046,1048],{"className":78,"code":1047,"language":80,"meta":81,"style":81},"\u002F\u002F name collisions — a later mixin silently overwrites an earlier method\nObject.assign(proto, mixinA, mixinB)   \u002F\u002F if both define save(), B wins silently\n",[24,1049,1050,1055],{"__ignoreMap":81},[85,1051,1052],{"class":87,"line":88},[85,1053,1054],{"class":250},"\u002F\u002F name collisions — a later mixin silently overwrites an earlier method\n",[85,1056,1057,1059,1061,1064],{"class":87,"line":106},[85,1058,233],{"class":102},[85,1060,156],{"class":109},[85,1062,1063],{"class":102},"(proto, mixinA, mixinB)   ",[85,1065,1066],{"class":250},"\u002F\u002F if both define save(), B wins silently\n",[1068,1069,1070,1077,1091],"ul",{},[1071,1072,1073,1076],"li",{},[19,1074,1075],{},"Name collisions:"," two mixins defining the same method clobber each other quietly. Keep\nmixin method names specific, or detect collisions in development.",[1071,1078,1079,1082,1083,1086,1087,1090],{},[19,1080,1081],{},"Hidden dependencies:"," a mixin that assumes ",[24,1084,1085],{},"this.id"," or ",[24,1088,1089],{},"this.save()"," exists couples it\nto its host invisibly. Document what each mixin requires.",[1071,1092,1093,1096,1097,1099],{},[19,1094,1095],{},"Order sensitivity:"," with class-factory mixins, the stacking order affects ",[24,1098,470],{},"\nchains. Be deliberate about sequence.",[15,1101,1102],{},"These aren't reasons to avoid mixins — just to use focused, well-named, self-contained ones.",[10,1104,1106],{"id":1105},"choosing-your-tool","Choosing your tool",[15,1108,1109],{},"A quick decision guide:",[1068,1111,1112,1120,1125],{},[1071,1113,1114,1119],{},[19,1115,1116],{},[24,1117,1118],{},"extends"," — a genuine, stable \"is-a\" relationship with one clear parent.",[1071,1121,1122,1124],{},[19,1123,51],{}," — orthogonal capabilities shared across unrelated classes (serializable,\nobservable, timestamped).",[1071,1126,1127,1130],{},[19,1128,1129],{},"Composition\u002Fdelegation"," — complex objects assembled from independent, swappable parts;\nwhen you want maximum flexibility and testability.",[15,1132,1133],{},"Most well-designed systems use all three: shallow inheritance for the core type, mixins for\ncross-cutting traits, and composition for assembling behavior. Reaching first for deep\ninheritance is the usual mistake.",[10,1135,1137],{"id":1136},"key-takeaways","Key takeaways",[1068,1139,1140,1143,1160,1163,1166,1172],{},[1071,1141,1142],{},"JavaScript allows only single inheritance; mixins and composition share behavior across\nunrelated types.",[1071,1144,1145,1146,1148,1149,1152,1153,1156,1157,1159],{},"A mixin is a reusable method bundle, applied via ",[24,1147,73],{}," (flat copy) or as a\n",[19,1150,1151],{},"class factory"," ",[24,1154,1155],{},"(Base) => class extends Base {...}"," (real prototype link, ",[24,1158,470],{},"-aware).",[1071,1161,1162],{},"Class-factory mixins stack cleanly to compose multiple independent capabilities.",[1071,1164,1165],{},"Pure composition (has-a, delegation) often beats inheritance for complex, evolving objects.",[1071,1167,1168,1169,1171],{},"Watch for mixin pitfalls: name collisions, hidden ",[24,1170,130],{}," dependencies, and order\nsensitivity.",[1071,1173,1174,1175,1177],{},"Favor composition over deep inheritance; combine ",[24,1176,1118],{},", mixins, and composition by\npurpose.",[15,1179,1180],{},"Mastering these patterns frees you from forcing every relationship into a single class tree —\nthe hallmark of flexible, maintainable object-oriented JavaScript.",[1182,1183,1184],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":81,"searchDepth":106,"depth":106,"links":1186},[1187,1188,1189,1190,1191,1192,1193,1194,1195],{"id":12,"depth":106,"text":13},{"id":62,"depth":106,"text":63},{"id":301,"depth":106,"text":302},{"id":459,"depth":106,"text":460},{"id":738,"depth":106,"text":739},{"id":937,"depth":106,"text":938},{"id":1040,"depth":106,"text":1041},{"id":1105,"depth":106,"text":1106},{"id":1136,"depth":106,"text":1137},"Learn mixins and composition in JavaScript — why single inheritance is limiting, how to build mixins with functions and Object.assign, and why composition often beats deep class hierarchies.","medium","md","JavaScript","javascript",{},"\u002Fblog\u002Fjavascript-mixins-composition-explained","\u002Fjavascript\u002Fclasses\u002Fmixins-composition",{"title":5,"description":1196},"blog\u002Fjavascript-mixins-composition-explained","Mixins & Composition","Classes & OOP","classes","2026-06-18","IAfdvCSc_YCdIaPSqKo8cEVpTOa8ejlIF2Z04MD-DoE",1781808673080]