You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four,
1584
-
you should prefer composition over inheritance where you can. There are lots of
1585
-
good reasons to use inheritance and lots of good reasons to use composition.
1586
-
The main point for this maxim is that if your mind instinctively goes for
1587
-
inheritance, try to think if composition could model your problem better. In some
1588
-
cases it can.
1589
-
1590
-
You might be wondering then, "when should I use inheritance?" It
1591
-
depends on your problem at hand, but this is a decent list of when inheritance
1592
-
makes more sense than composition:
1593
-
1594
-
1. Your inheritance represents an "is-a" relationship and not a "has-a"
1595
-
relationship (Animal->Human vs. User->UserDetails).
1596
-
2. You can reuse code from the base classes (Humans can move like all animals).
1597
-
3. You want to make global changes to derived classes by changing a base class.
1598
-
(Change the caloric expenditure of all animals when they move).
1584
+
you should prefer composition over inheritance where you can.
1585
+
1586
+
Important thing is that JavaScript uses prototypal inheritance. It means new objects are instantiated by creating delegation links using OLOO (Objects Linking to Other Objects). Therfore you should be aware that JS does not offer real classes you may now from _Java_, _C#_ or other _object-oriented languages_. You may think: _"Hold on! There are `class` and `extends` keywords so I should be able to use classical inheritance."_. These keywords are _syntactic sugar_ and they are **not** introducing a new object-oriented inheritance model to the language (#[MDN: Classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)).
1587
+
1588
+
The composition/inheritance topic is comprehensively covered in following articles:
1589
+
-["Common Misconceptions About Inheritance in JavaScript"](https://medium.com/javascript-scene/common-misconceptions-about-inheritance-in-javascript-d5d9bab29b0a)
1590
+
-["3 Different Kinds of Prototypal Inheritance: ES6+ Edition"](https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9)
1591
+
-["What's the Difference Between Class and Prototypal Inheritance?"](https://medium.com/javascript-scene/master-the-javascript-interview-what-s-the-difference-between-class-prototypal-inheritance-e4cd0a7562e9)
After you read mentioned articles you know that JavaScript uses **prototypal inheritance** and you as a developer should favor **composition** over inheritance. You also know that both `class` and `extends` keywords are only syntactic sugar and there is [a better option](https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3#d03c) of instantiating new objects. This option is called **factory function** which allows you to **compose** any objects you need into new prototype.
1599
1595
1600
1596
**Bad:**
1601
1597
```javascript
1602
-
classEmployee {
1603
-
constructor(name, email) {
1604
-
this.name= name;
1605
-
this.email= email;
1598
+
classAirplane {
1599
+
constructor(initialState) {
1600
+
const { name } = initialState
1601
+
1602
+
this.name= name
1606
1603
}
1607
1604
1608
-
// ...
1605
+
getName() {
1606
+
return`${this.name}`
1607
+
}
1609
1608
}
1610
1609
1611
-
// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
1612
-
classEmployeeTaxDataextendsEmployee {
1613
-
constructor(ssn, salary) {
1614
-
super();
1615
-
this.ssn= ssn;
1616
-
this.salary= salary;
1610
+
constdefaultAirbusA380State= {
1611
+
name:'Airbus A380'
1612
+
}
1613
+
classAirbusA380extendsAirplane {
1614
+
constructor(initialState) {
1615
+
conststate=Object.assign(
1616
+
{},
1617
+
defaultAirbusA380State,
1618
+
initialState
1619
+
)
1620
+
const { airline } = state
1621
+
1622
+
super(state)
1623
+
1624
+
this.airline= airline
1617
1625
}
1618
1626
1619
-
// ...
1627
+
getName() {
1628
+
return`${super.getName()} of ${this.airline}`
1629
+
}
1620
1630
}
1621
-
```
1622
-
1623
-
**Good**:
1624
-
```javascript
1625
-
classEmployee {
1626
-
constructor(name, email) {
1627
-
this.name= name;
1628
-
this.email= email;
1629
1631
1632
+
constdefaultCessnaState= {
1633
+
name:'Cessna'
1634
+
}
1635
+
classCessnaextendsAirplane {
1636
+
constructor(initialState) {
1637
+
conststate=Object.assign(
1638
+
{},
1639
+
defaultCessnaState,
1640
+
initialState
1641
+
)
1642
+
1643
+
super(state)
1630
1644
}
1645
+
}
1646
+
1647
+
// create instances of airplanes
1648
+
constemiratesAirbus=newAirbusA380({
1649
+
airline:'Emirates'
1650
+
})
1651
+
1652
+
constprivateCessnaJet=newCessna()
1631
1653
1632
-
setTaxData(ssn, salary) {
1633
-
this.taxData=newEmployeeTaxData(ssn, salary);
1654
+
console.log(emiratesAirbus.getName()) // prints "Airbus A380 of Emirates"
1655
+
console.log(privateCessnaJet.getName()) // prints "Cessna"
1656
+
```
1657
+
1658
+
**Good:**
1659
+
```javascript
1660
+
// define base objects
1661
+
constAirplane= {
1662
+
getName() {
1663
+
return`${this.name}`
1634
1664
}
1635
-
// ...
1636
1665
}
1637
1666
1638
-
classEmployeeTaxData {
1639
-
constructor(ssn, salary) {
1640
-
this.ssn= ssn;
1641
-
this.salary= salary;
1667
+
constAirbusA380= {
1668
+
name:'Airbus A380',
1669
+
getName() {
1670
+
return`${Airplane.getName.call(this)} of ${this.airline}`
1642
1671
}
1672
+
}
1643
1673
1644
-
// ...
1674
+
constCessna= {
1675
+
name:'Cessna'
1645
1676
}
1677
+
1678
+
// define factory functions (airbusA380, cessna)
1679
+
constairbusA380= (state) =>Object.assign(
1680
+
{},
1681
+
Airplane,
1682
+
AirbusA380,
1683
+
state
1684
+
)
1685
+
1686
+
constcessna= (state) =>Object.assign(
1687
+
{},
1688
+
Airplane,
1689
+
Cessna,
1690
+
state
1691
+
)
1692
+
1693
+
// create instances of airplanes
1694
+
constemiratesAirbus=airbusA380({
1695
+
airline:'Emirates'
1696
+
})
1697
+
1698
+
constprivateCessnaJet=cessna()
1699
+
1700
+
console.log(emiratesAirbus.getName()) // prints "Airbus A380 of Emirates"
1701
+
console.log(privateCessnaJet.getName()) // prints "Cessna"
0 commit comments