{"id":320614,"date":"2020-09-28T13:42:50","date_gmt":"2020-09-28T20:42:50","guid":{"rendered":"https:\/\/css-tricks.com\/?p=320614"},"modified":"2020-10-29T13:57:05","modified_gmt":"2020-10-29T20:57:05","slug":"the-flavors-of-object-oriented-programming-in-javascript","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/the-flavors-of-object-oriented-programming-in-javascript\/","title":{"rendered":"The Flavors of Object-Oriented Programming (in JavaScript)"},"content":{"rendered":"\n

In my research, I’ve found there are four approaches to Object-Oriented Programming in JavaScript:<\/p>\n\n\n\n

\n
  1. Using Constructor functions<\/a><\/li>
  2. Using Classes<\/a><\/li>
  3. Using Objects linking to other objects (OLOO)<\/a><\/li>
  4. Using Factory functions<\/a><\/li><\/ol>\n<\/div><\/div>\n\n\n\n

    Which methods should I use? Which one is “the best” way? Here I’ll present my findings along with information that may help you decide which is right for you.<\/p>\n\n\n\n

    To make that decision, we’re not just going to look at the different flavors but compare conceptual aspects between them:<\/p>\n\n\n\n\n\n\n\n

    \n
    \n
    1. Classes vs. Factory functions \u2013 Inheritance<\/a><\/li>
    2. Classes vs. Factory functions \u2013 Encapsulation<\/a><\/li>
    3. Classes vs. Factory functions \u2013 this<\/code><\/a><\/li>
    4. Classes vs. Factory functions \u2013 Event listeners<\/a><\/li><\/ol>\n<\/div><\/div>\n\n\n\n

      Let’s start with a foundation of OOP in JavaScript.<\/p>\n<\/div><\/div>\n\n\n

      What is Object-Oriented Programming?<\/h3>\n\n\n

      Object-Oriented Programming is a way of writing code that allows you to create different objects from a common object. The common object is usually called a blueprint<\/em> while the created objects are called instances<\/em>.<\/p>\n\n\n\n

      Each instance has properties that are not shared with other instances. For example, if you have a Human blueprint, you can create human instances with different names.<\/p>\n\n\n\n

      The second aspect of Object-Oriented Programming is about structuring<\/em> code when you have multiple levels of blueprints. This is commonly called Inheritance or subclassing. <\/p>\n\n\n\n

      The third aspect of Object Oriented Programming is about encapsulation<\/em> where you hide certain pieces of information within the object so they’re not accessible.<\/p>\n\n\n\n

      If you need more than this brief intro, here\u2019s an article<\/a> that introduces this aspect of Object-Oriented Programming if you need help with it.<\/p>\n\n\n\n

      Let’s begin with the basics \u2014 an introduction to the four flavors of Object-Oriented Programming.<\/p>\n\n\n

      The four flavors of Object-Oriented Programming<\/h3>\n\n\n

      There are four ways to write Object-Oriented Programming in JavaScript. They are:<\/p>\n\n\n\n

      \n
      1. Using Constructor functions<\/a><\/li>
      2. Using Classes<\/a><\/li>
      3. Using Objects Linking to Other Objects (OLOO)<\/a><\/li>
      4. Using Factory functions<\/a><\/li><\/ol>\n<\/div><\/div>\n\n\n

        Using Constructor functions<\/strong><\/h4>\n\n\n

        Constructors are functions that contain a this<\/code> keyword.<\/p>\n\n\n\n

        function Human (firstName, lastName) {\n  this.firstName = firstName\n  this.lastName = lastName\n}<\/code><\/pre>\n\n\n\n

        this<\/code> lets you store (and access) unique values created for each instance. You can create an instance with the new<\/code> keyword.<\/p>\n\n\n\n

        const chris = new Human('Chris', 'Coyier')\nconsole.log(chris.firstName) \/\/ Chris\nconsole.log(chris.lastName) \/\/ Coyier\n\nconst zell = new Human('Zell', 'Liew')\nconsole.log(zell.firstName) \/\/ Zell\nconsole.log(zell.lastName) \/\/ Liew<\/code><\/pre>\n\n\n

        Class syntax<\/strong><\/h4>\n\n\n

        Classes are said to be the “syntactic sugar” of Constructor functions. As in, Classes are an easier way of writing Constructor functions.<\/p>\n\n\n\n

        There’s serious contention about whether Classes are bad (like this<\/a> and this<\/a>). We’re not going to dive into those arguments here. Instead, we’re just going to look at how to write code with Classes and decide whether Classes are better than constructors based on the code we write.<\/p>\n\n\n\n

        Classes can be written with the following syntax:<\/p>\n\n\n\n

        class Human {\n  constructor(firstName, lastName) {\n    this.firstName = firstName\n    this.lastName = lastName\n  }\n}<\/code><\/pre>\n\n\n\n

        Notice the constructor<\/code> function contains the same code as the Constructor syntax above? We need to do this since we want to initialize values into this<\/code>. (We can skip constructor<\/code> if we don’t need to initialize values. More on this later under Inheritance<\/a>).<\/p>\n\n\n\n

        At first glance, classes seem to be inferior to constructors \u2014 there’s more code to write! Hold your horses and don’t form a conclusion at this point. We have a lot more to cover. Classes begin to shine later.<\/p>\n\n\n\n

        As before, you can create an instance with the new<\/code> keyword.<\/p>\n\n\n\n

        const chris = new Human('Chris', 'Coyier')\n\nconsole.log(chris.firstName) \/\/ Chris\nconsole.log(chris.lastName) \/\/ Coyier<\/code><\/pre>\n\n\n

        Objects Linking to Other Objects (OLOO)<\/strong><\/h4>\n\n\n

        OLOO was coined and popularized by Kyle Simpson<\/a>. In OLOO, you define the blueprint as a normal object. You then use a method (often named init<\/code>, but that isn’t required in the way constructor<\/code> is to a Class) to initialize the instance.<\/p>\n\n\n\n

        const Human = {\n  init (firstName, lastName ) {\n    this.firstName = firstName\n    this.lastName = lastName\n  }\n}<\/code><\/pre>\n\n\n\n

        You use Object.create<\/code> to create an instance. After creating the instance, you need to run your init<\/code> function.<\/p>\n\n\n\n

        const chris = Object.create(Human)\nchris.init('Chris', 'Coyier')\n\nconsole.log(chris.firstName) \/\/ Chris\nconsole.log(chris.lastName) \/\/ Coyier<\/code><\/pre>\n\n\n\n

        You can chain init<\/code> after Object.create<\/code> if you returned this<\/code> inside init<\/code>.<\/p>\n\n\n\n

        const Human = {\n  init () {\n    \/\/ ...\n    return this \n  }\n}\n\nconst chris = Object.create(Human).init('Chris', 'Coyier')\nconsole.log(chris.firstName) \/\/ Chris\nconsole.log(chris.lastName) \/\/ Coyier<\/code><\/pre>\n\n\n

        Factory functions<\/h4>\n\n\n

        Factory functions are functions that return an object. You can return any object. You can even return a Class instance or OLOO instance \u2014 and it’ll still be a valid Factory function.<\/p>\n\n\n\n

        Here’s the simplest way to create Factory functions:<\/p>\n\n\n\n

        function Human (firstName, lastName) {\n  return {\n    firstName,\n    lastName\n  }\n}<\/code><\/pre>\n\n\n\n

        You don’t need new<\/code> to create instances with Factory functions. You simply call the function.<\/p>\n\n\n\n

        const chris = Human('Chris', 'Coyier')\n\nconsole.log(chris.firstName) \/\/ Chris\nconsole.log(chris.lastName) \/\/ Coyier<\/code><\/pre>\n\n\n\n
        \n\n\n\n

        Now that we’ve seen these four OOP setup possibilities, let’s look at how you declare properties and methods on each of them so we can get a little better understanding of working with them before getting to the bigger comparisons we’re trying to make.<\/p>\n\n\n\n


        \n\n\n

        Declaring properties and methods<\/h3>\n\n\n

        Methods are functions declared as an object’s property.<\/p>\n\n\n\n

        const someObject = {\n  someMethod () { \/* ... *\/ }\n}<\/code><\/pre>\n\n\n\n

        In Object-Oriented Programming, there are two ways to declare properties and methods:<\/p>\n\n\n\n

        1. Directly on the instance<\/li>
        2. In the Prototype<\/li><\/ol>\n\n\n\n

          Let’s learn to do both.<\/p>\n\n\n

          Declaring properties and methods with Constructors<\/strong><\/h4>\n\n\n

          If you want to declare a property directly on an instance, you can write the property inside the constructor function. Make sure to set it as the property for this<\/code>.<\/p>\n\n\n\n

          function Human (firstName, lastName) {\n  \/\/ Declares properties\n  this.firstName = firstName\n  this.lastname = lastName\n\n  \/\/ Declares methods\n  this.sayHello = function () {\n    console.log(`Hello, I'm ${firstName}`)\n  }\n}\n\nconst chris = new Human('Chris', 'Coyier')\nconsole.log(chris)<\/code><\/pre>\n\n\n\n
          \"\"\/<\/figure>\n\n\n\n

          Methods are commonly declared on the Prototype because Prototype allows instances to use the same method. It’s a smaller “code footprint.”<\/p>\n\n\n\n

          To declare properties on the Prototype, you need to use the prototype<\/code> property.<\/p>\n\n\n\n

          function Human (firstName, lastName) {\n  this.firstName = firstName\n  this.lastname = lastName\n}\n\n\/\/ Declaring method on a prototype\nHuman.prototype.sayHello = function () {\n  console.log(`Hello, I'm ${this.firstName}`)\n}<\/code><\/pre>\n\n\n\n
          \"\"\/<\/figure>\n\n\n\n

          It can be clunky if you want to declare multiple methods in a Prototype.<\/p>\n\n\n\n

          \/\/ Declaring methods on a prototype\nHuman.prototype.method1 = function () { \/*...*\/ }\nHuman.prototype.method2 = function () { \/*...*\/ }\nHuman.prototype.method3 = function () { \/*...*\/ }<\/code><\/pre>\n\n\n\n

          You can make things easier by using merging functions like Object.assign<\/code>.<\/p>\n\n\n\n

          Object.assign(Human.prototype, {\n  method1 () { \/*...*\/ },\n  method2 () { \/*...*\/ },\n  method3 () { \/*...*\/ }\n})<\/code><\/pre>\n\n\n\n

          Object.assign<\/code> does not support the merging of Getter and Setter functions. You need another tool. Here’s why<\/a>. And here\u2019s a tool<\/a> I created to merge objects with Getters and Setters.<\/p>\n\n\n

          Declaring properties and methods with Classes<\/strong><\/h4>\n\n\n

          You can declare properties for each instance inside the constructor<\/code> function.<\/p>\n\n\n\n

          class Human {\n  constructor (firstName, lastName) {\n    this.firstName = firstName\n      this.lastname = lastName\n\n      this.sayHello = function () {\n        console.log(`Hello, I'm ${firstName}`)\n      }\n  }\n}<\/code><\/pre>\n\n\n\n
          \"\"\/<\/figure>\n\n\n\n

          It’s easier to declare methods on the prototype. You write the method after constructor<\/code> like a normal function.<\/p>\n\n\n\n

          class Human (firstName, lastName) {\n  constructor (firstName, lastName) { \/* ... *\/ }\n\n  sayHello () {\n    console.log(`Hello, I'm ${this.firstName}`)\n  }\n}<\/code><\/pre>\n\n\n\n
          \"\"\/<\/figure>\n\n\n\n

          It’s easier to declare multiple methods on Classes compared to Constructors. You don’t need the Object.assign<\/code> syntax. You just write more functions.<\/p>\n\n\n\n

          Note:<\/strong> there’s no ,<\/code> between method declarations in a Class.<\/p>\n\n\n\n

          class Human (firstName, lastName) {\n  constructor (firstName, lastName) { \/* ... *\/ }\n\n  method1 () { \/*...*\/ }\n  method2 () { \/*...*\/ }\n  method3 () { \/*...*\/ }\n}<\/code><\/pre>\n\n\n

          Declaring properties and methods with OLOO<\/strong><\/h4>\n\n\n

          You use the same process for declaring properties and methods on an instance. You assign them as a property of this<\/code>.<\/p>\n\n\n\n

          const Human = {\n  init (firstName, lastName) {\n    this.firstName = firstName\n    this.lastName = lastName\n    this.sayHello = function () {\n      console.log(`Hello, I'm ${firstName}`)\n    }\n\n    return this\n  }\n}\n\nconst chris = Object.create(Human).init('Chris', 'Coyier')\nconsole.log(chris)<\/code><\/pre>\n\n\n\n
          \"\"\/<\/figure>\n\n\n\n

          To declare methods in the prototype, you write the method like a normal object.<\/p>\n\n\n\n

          const Human = {\n  init () { \/*...*\/ },\n  sayHello () {\n    console.log(`Hello, I'm ${this.firstName}`)\n  }\n}<\/code><\/pre>\n\n\n\n
          \"\"\/<\/figure>\n\n\n

          Declaring properties and methods with Factory functions<\/strong><\/h4>\n\n\n

          You can declare properties and methods directly by including them in the returned object.<\/p>\n\n\n\n

          function Human (firstName, lastName) {\n  return {\n    firstName,\n    lastName, \n    sayHello () {\n      console.log(`Hello, I'm ${firstName}`)\n    }\n  }\n}<\/code><\/pre>\n\n\n\n
          \"\"\/<\/figure>\n\n\n\n

          You cannot declare methods on the Prototype when you use Factory functions. If you really want methods on the prototype, you need to return a Constructor, Class, or OLOO instance. (Don’t do this since it doesn’t make any sense.)<\/p>\n\n\n\n

          \/\/ Do not do this\nfunction createHuman (...args) {\n  return new Human(...args)\n}<\/code><\/pre>\n\n\n

          Where to declare properties and methods<\/h3>\n\n\n

          Should you declare properties and methods directly on the instance? Or should you use prototype<\/code> as much as you can?<\/p>\n\n\n\n

          Many people take pride that JavaScript is a “Prototypal Language” (which means it uses prototypes). From this statement, you may make the assumption that using “Prototypes” is better.<\/p>\n\n\n\n

          The real answer is: It doesn’t matter.<\/strong><\/p>\n\n\n\n

          If you declare properties and methods on instances, each instance will take up slightly more memory. If you declare methods on Prototypes, the memory used by each instance will decrease, but not much. This difference is insignificant with computer processing power what it is today. Instead, you want to look at how easy it is to write code \u2014 and whether it is possible to use Prototypes in the first place.<\/p>\n\n\n\n

          For example, if you use Classes or OLOO, you’ll be better off using Prototypes since the code is easier to write. If you use Factory functions, you cannot use Prototypes. You can only create properties and methods directly on the instance.<\/p>\n\n\n\n

          I wrote a separate article on understanding JavaScript Prototypes<\/a> if you’re interested in finding out more.<\/p>\n\n\n

          Preliminary verdict<\/h3>\n\n\n

          We can make a few notes from the code we wrote above. These opinions are my own!<\/p>\n\n\n\n

          1. Classes are better than Constructors<\/strong> because its easier to write multiple methods on Classes.<\/li>
          2. OLOO is weird because of the Object.create<\/code> part.<\/strong> I gave OLOO a run for a while, but I always forget to write Object.create<\/code>. It’s weird enough for me not to use it.<\/li>
          3. Classes and Factry Functions are easiest to use.<\/strong> The problem is that Factory functions don’t support Prototypes. But like I said, this doesn’t really matter in production.<\/li><\/ol>\n\n\n\n

            We’re down to two. Should we choose Classes or Factory functions then? Let’s compare them!<\/p>\n\n\n\n


            \n\n\n

            Classes vs. Factory functions \u2014 Inheritance<\/strong><\/h3>\n\n\n

            To continue the discussion on Classes and Factory functions, we need to understand three more concepts that are tied closely to Object-Oriented Programming.<\/p>\n\n\n\n

            1. Inheritance<\/li>
            2. Encapsulation<\/li>
            3. this<\/code><\/li><\/ol>\n\n\n\n

              Let’s start with Inheritance.<\/p>\n\n\n

              What is Inheritance?<\/h4>\n\n\n

              Inheritance is a loaded word. Many people in the industry use Inheritance incorrectly, in my opinion. The word “inheritance” is used when you receive things from somewhere. For example:<\/p>\n\n\n\n