Cara menggunakan OBJECT.CREATE di JavaScript

Dalam JavaScript kita hanya dapat mewarisi dari satu objek. Hanya ada satu [[Prototype]] untuk sebuah objek. Dan sebuah class hanya dapat meng-extend satu class lainnya

Show

    Namun terkadang terasa membatasi. Misalnya, kita memiliki kelas StreetSweeper dan kelas Bicycle, dan ingin membuat campuran dari keduanya. StreetSweepingBicycle

    Atau kami memiliki kelas User dan kelas

    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);
    0 yang menerapkan pembuatan acara, dan kami ingin menambahkan fungsionalitas
    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);
    0 ke User, sehingga pengguna kami dapat memancarkan acara

    Ada konsep yang bisa membantu disini, namanya "mixin"

    Sebagaimana didefinisikan di Wikipedia, mixin adalah kelas yang berisi metode yang dapat digunakan oleh kelas lain tanpa harus mewarisinya

    Dengan kata lain, mixin menyediakan metode yang mengimplementasikan perilaku tertentu, tetapi kami tidak menggunakannya sendiri, kami menggunakannya untuk menambahkan perilaku ke kelas lain

    Cara termudah untuk mengimplementasikan mixin dalam JavaScript adalah membuat objek dengan metode yang berguna, sehingga kita dapat dengan mudah menggabungkannya menjadi prototipe kelas apa pun

    Misalnya di sini mixin

    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);
    3 digunakan untuk menambahkan beberapa "ucapan" untuk User

    // mixin
    let sayHiMixin = {
      sayHi() {
        alert(`Hello ${this.name}`);
      },
      sayBye() {
        alert(`Bye ${this.name}`);
      }
    };
    
    // penggunaan:
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!

    Tidak ada pewarisan, tetapi penyalinan metode sederhana. Jadi, User dapat mewarisi dari kelas lain dan juga menyertakan mixin untuk "mencampur" metode tambahan, seperti ini

    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);

    Mixin dapat memanfaatkan warisan itu sendiri

    Misalnya, di sini

    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);
    3 mewarisi dari
    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);
    7

    let sayMixin = {
      say(phrase) {
        alert(phrase);
      }
    };
    
    let sayHiMixin = {
      __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)
    
      sayHi() {
        // panggil metode induk
        super.say(`Hello ${this.name}`); // (*)
      },
      sayBye() {
        super.say(`Bye ${this.name}`); // (*)
      }
    };
    
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!

    Perhatikan bahwa panggilan ke metode induk

    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);
    _8 dari
    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);
    3 (pada baris berlabel
    let sayMixin = {
      say(phrase) {
        alert(phrase);
      }
    };
    
    let sayHiMixin = {
      __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)
    
      sayHi() {
        // panggil metode induk
        super.say(`Hello ${this.name}`); // (*)
      },
      sayBye() {
        super.say(`Bye ${this.name}`); // (*)
      }
    };
    
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!
    0) mencari metode dalam prototipe mixin, bukan kelas

    Ini diagramnya (lihat bagian kanan)

    Itu karena metode

    let sayMixin = {
      say(phrase) {
        alert(phrase);
      }
    };
    
    let sayHiMixin = {
      __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)
    
      sayHi() {
        // panggil metode induk
        super.say(`Hello ${this.name}`); // (*)
      },
      sayBye() {
        super.say(`Bye ${this.name}`); // (*)
      }
    };
    
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!
    1 dan
    let sayMixin = {
      say(phrase) {
        alert(phrase);
      }
    };
    
    let sayHiMixin = {
      __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)
    
      sayHi() {
        // panggil metode induk
        super.say(`Hello ${this.name}`); // (*)
      },
      sayBye() {
        super.say(`Bye ${this.name}`); // (*)
      }
    };
    
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!
    2 awalnya dibuat di
    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);
    3. Jadi, meskipun disalin, properti internal
    let sayMixin = {
      say(phrase) {
        alert(phrase);
      }
    };
    
    let sayHiMixin = {
      __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)
    
      sayHi() {
        // panggil metode induk
        super.say(`Hello ${this.name}`); // (*)
      },
      sayBye() {
        super.say(`Bye ${this.name}`); // (*)
      }
    };
    
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!
    4 referensi
    class User extends Person {
      // ...
    }
    
    Object.assign(User.prototype, sayHiMixin);
    3, seperti yang ditunjukkan pada gambar di atas

    Karena

    let sayMixin = {
      say(phrase) {
        alert(phrase);
      }
    };
    
    let sayHiMixin = {
      __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)
    
      sayHi() {
        // panggil metode induk
        super.say(`Hello ${this.name}`); // (*)
      },
      sayBye() {
        super.say(`Bye ${this.name}`); // (*)
      }
    };
    
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!
    _6 mencari metode induk di
    let sayMixin = {
      say(phrase) {
        alert(phrase);
      }
    };
    
    let sayHiMixin = {
      __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)
    
      sayHi() {
        // panggil metode induk
        super.say(`Hello ${this.name}`); // (*)
      },
      sayBye() {
        super.say(`Bye ${this.name}`); // (*)
      }
    };
    
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!
    7, itu berarti mencari
    let sayMixin = {
      say(phrase) {
        alert(phrase);
      }
    };
    
    let sayHiMixin = {
      __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)
    
      sayHi() {
        // panggil metode induk
        super.say(`Hello ${this.name}`); // (*)
      },
      sayBye() {
        super.say(`Bye ${this.name}`); // (*)
      }
    };
    
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!
    8, bukan
    let sayMixin = {
      say(phrase) {
        alert(phrase);
      }
    };
    
    let sayHiMixin = {
      __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)
    
      sayHi() {
        // panggil metode induk
        super.say(`Hello ${this.name}`); // (*)
      },
      sayBye() {
        super.say(`Bye ${this.name}`); // (*)
      }
    };
    
    class User {
      constructor(name) {
        this.name = name;
      }
    }
    
    // salin metodenya
    Object.assign(User.prototype, sayHiMixin);
    
    // sekarang User dapat berkata hi
    new User("Dude").sayHi(); // Hello Dude!
    9

    Sekarang mari kita buat mixin untuk kehidupan nyata

    Fitur penting dari banyak objek browser (misalnya) adalah mereka dapat menghasilkan peristiwa. Acara adalah cara yang bagus untuk "menyiarkan informasi" kepada siapa saja yang menginginkannya. Jadi mari kita buat mixin yang memungkinkan kita dengan mudah menambahkan fungsionalitas terkait acara ke kelas/objek apa pun

    • Mixin akan memberikan metode
      let eventMixin = {
        /**
         * Berlangganan ke peristiwa, menggunakan:
         *  menu.on('select', function(item) { .. }
         */
        on(eventName, handler) {
          if (!this._eventHandlers) this._eventHandlers = {};
          if (!this._eventHandlers[eventName]) {
            this._eventHandlers[eventName] = [];
          }
          this._eventHandlers[eventName].push(handler);
        },
      
        /**
         * Membatalkan langganan, menggunakan:
         *  menu.off('select', handler)
         */
        off(eventName, handler) {
          let handlers = this._eventHandlers?.[eventName];
          if (!handlers) return;
          for (let i = 0; i < handlers.length; i++) {
            if (handlers[i] === handler) {
              handlers.splice(i--, 1);
            }
          }
        },
      
        /**
         * menghasilkan peristiwa dengan nama dan data yang diberikan
         *  this.trigger('select', data1, data2);
         */
        trigger(eventName, ...args) {
          if (!this._eventHandlers?.[eventName]) {
            return; // tidak ada penangan untuk nama peristiwa itu
          }
      
          // panggil penangannya
          this._eventHandlers[eventName].forEach((handler) =>
            handler.apply(this, args)
          );
        },
      };
      _0 untuk "menghasilkan acara" ketika sesuatu yang penting terjadi padanya. Argumen
      let eventMixin = {
        /**
         * Berlangganan ke peristiwa, menggunakan:
         *  menu.on('select', function(item) { .. }
         */
        on(eventName, handler) {
          if (!this._eventHandlers) this._eventHandlers = {};
          if (!this._eventHandlers[eventName]) {
            this._eventHandlers[eventName] = [];
          }
          this._eventHandlers[eventName].push(handler);
        },
      
        /**
         * Membatalkan langganan, menggunakan:
         *  menu.off('select', handler)
         */
        off(eventName, handler) {
          let handlers = this._eventHandlers?.[eventName];
          if (!handlers) return;
          for (let i = 0; i < handlers.length; i++) {
            if (handlers[i] === handler) {
              handlers.splice(i--, 1);
            }
          }
        },
      
        /**
         * menghasilkan peristiwa dengan nama dan data yang diberikan
         *  this.trigger('select', data1, data2);
         */
        trigger(eventName, ...args) {
          if (!this._eventHandlers?.[eventName]) {
            return; // tidak ada penangan untuk nama peristiwa itu
          }
      
          // panggil penangannya
          this._eventHandlers[eventName].forEach((handler) =>
            handler.apply(this, args)
          );
        },
      };
      1 adalah nama acara, secara opsional diikuti dengan argumen tambahan dengan data acara
    • Juga metode
      let eventMixin = {
        /**
         * Berlangganan ke peristiwa, menggunakan:
         *  menu.on('select', function(item) { .. }
         */
        on(eventName, handler) {
          if (!this._eventHandlers) this._eventHandlers = {};
          if (!this._eventHandlers[eventName]) {
            this._eventHandlers[eventName] = [];
          }
          this._eventHandlers[eventName].push(handler);
        },
      
        /**
         * Membatalkan langganan, menggunakan:
         *  menu.off('select', handler)
         */
        off(eventName, handler) {
          let handlers = this._eventHandlers?.[eventName];
          if (!handlers) return;
          for (let i = 0; i < handlers.length; i++) {
            if (handlers[i] === handler) {
              handlers.splice(i--, 1);
            }
          }
        },
      
        /**
         * menghasilkan peristiwa dengan nama dan data yang diberikan
         *  this.trigger('select', data1, data2);
         */
        trigger(eventName, ...args) {
          if (!this._eventHandlers?.[eventName]) {
            return; // tidak ada penangan untuk nama peristiwa itu
          }
      
          // panggil penangannya
          this._eventHandlers[eventName].forEach((handler) =>
            handler.apply(this, args)
          );
        },
      };
      _2 yang menambahkan fungsi
      let eventMixin = {
        /**
         * Berlangganan ke peristiwa, menggunakan:
         *  menu.on('select', function(item) { .. }
         */
        on(eventName, handler) {
          if (!this._eventHandlers) this._eventHandlers = {};
          if (!this._eventHandlers[eventName]) {
            this._eventHandlers[eventName] = [];
          }
          this._eventHandlers[eventName].push(handler);
        },
      
        /**
         * Membatalkan langganan, menggunakan:
         *  menu.off('select', handler)
         */
        off(eventName, handler) {
          let handlers = this._eventHandlers?.[eventName];
          if (!handlers) return;
          for (let i = 0; i < handlers.length; i++) {
            if (handlers[i] === handler) {
              handlers.splice(i--, 1);
            }
          }
        },
      
        /**
         * menghasilkan peristiwa dengan nama dan data yang diberikan
         *  this.trigger('select', data1, data2);
         */
        trigger(eventName, ...args) {
          if (!this._eventHandlers?.[eventName]) {
            return; // tidak ada penangan untuk nama peristiwa itu
          }
      
          // panggil penangannya
          this._eventHandlers[eventName].forEach((handler) =>
            handler.apply(this, args)
          );
        },
      };
      3 sebagai pendengar acara dengan nama yang diberikan. Ini akan dipanggil saat acara dengan
      let eventMixin = {
        /**
         * Berlangganan ke peristiwa, menggunakan:
         *  menu.on('select', function(item) { .. }
         */
        on(eventName, handler) {
          if (!this._eventHandlers) this._eventHandlers = {};
          if (!this._eventHandlers[eventName]) {
            this._eventHandlers[eventName] = [];
          }
          this._eventHandlers[eventName].push(handler);
        },
      
        /**
         * Membatalkan langganan, menggunakan:
         *  menu.off('select', handler)
         */
        off(eventName, handler) {
          let handlers = this._eventHandlers?.[eventName];
          if (!handlers) return;
          for (let i = 0; i < handlers.length; i++) {
            if (handlers[i] === handler) {
              handlers.splice(i--, 1);
            }
          }
        },
      
        /**
         * menghasilkan peristiwa dengan nama dan data yang diberikan
         *  this.trigger('select', data1, data2);
         */
        trigger(eventName, ...args) {
          if (!this._eventHandlers?.[eventName]) {
            return; // tidak ada penangan untuk nama peristiwa itu
          }
      
          // panggil penangannya
          this._eventHandlers[eventName].forEach((handler) =>
            handler.apply(this, args)
          );
        },
      };
      1 yang diberikan dipicu, dan dapatkan argumen dari panggilan
      let eventMixin = {
        /**
         * Berlangganan ke peristiwa, menggunakan:
         *  menu.on('select', function(item) { .. }
         */
        on(eventName, handler) {
          if (!this._eventHandlers) this._eventHandlers = {};
          if (!this._eventHandlers[eventName]) {
            this._eventHandlers[eventName] = [];
          }
          this._eventHandlers[eventName].push(handler);
        },
      
        /**
         * Membatalkan langganan, menggunakan:
         *  menu.off('select', handler)
         */
        off(eventName, handler) {
          let handlers = this._eventHandlers?.[eventName];
          if (!handlers) return;
          for (let i = 0; i < handlers.length; i++) {
            if (handlers[i] === handler) {
              handlers.splice(i--, 1);
            }
          }
        },
      
        /**
         * menghasilkan peristiwa dengan nama dan data yang diberikan
         *  this.trigger('select', data1, data2);
         */
        trigger(eventName, ...args) {
          if (!this._eventHandlers?.[eventName]) {
            return; // tidak ada penangan untuk nama peristiwa itu
          }
      
          // panggil penangannya
          this._eventHandlers[eventName].forEach((handler) =>
            handler.apply(this, args)
          );
        },
      };
      5
    • …Dan metode
      let eventMixin = {
        /**
         * Berlangganan ke peristiwa, menggunakan:
         *  menu.on('select', function(item) { .. }
         */
        on(eventName, handler) {
          if (!this._eventHandlers) this._eventHandlers = {};
          if (!this._eventHandlers[eventName]) {
            this._eventHandlers[eventName] = [];
          }
          this._eventHandlers[eventName].push(handler);
        },
      
        /**
         * Membatalkan langganan, menggunakan:
         *  menu.off('select', handler)
         */
        off(eventName, handler) {
          let handlers = this._eventHandlers?.[eventName];
          if (!handlers) return;
          for (let i = 0; i < handlers.length; i++) {
            if (handlers[i] === handler) {
              handlers.splice(i--, 1);
            }
          }
        },
      
        /**
         * menghasilkan peristiwa dengan nama dan data yang diberikan
         *  this.trigger('select', data1, data2);
         */
        trigger(eventName, ...args) {
          if (!this._eventHandlers?.[eventName]) {
            return; // tidak ada penangan untuk nama peristiwa itu
          }
      
          // panggil penangannya
          this._eventHandlers[eventName].forEach((handler) =>
            handler.apply(this, args)
          );
        },
      };
      _6 yang menghapus pendengar
      let eventMixin = {
        /**
         * Berlangganan ke peristiwa, menggunakan:
         *  menu.on('select', function(item) { .. }
         */
        on(eventName, handler) {
          if (!this._eventHandlers) this._eventHandlers = {};
          if (!this._eventHandlers[eventName]) {
            this._eventHandlers[eventName] = [];
          }
          this._eventHandlers[eventName].push(handler);
        },
      
        /**
         * Membatalkan langganan, menggunakan:
         *  menu.off('select', handler)
         */
        off(eventName, handler) {
          let handlers = this._eventHandlers?.[eventName];
          if (!handlers) return;
          for (let i = 0; i < handlers.length; i++) {
            if (handlers[i] === handler) {
              handlers.splice(i--, 1);
            }
          }
        },
      
        /**
         * menghasilkan peristiwa dengan nama dan data yang diberikan
         *  this.trigger('select', data1, data2);
         */
        trigger(eventName, ...args) {
          if (!this._eventHandlers?.[eventName]) {
            return; // tidak ada penangan untuk nama peristiwa itu
          }
      
          // panggil penangannya
          this._eventHandlers[eventName].forEach((handler) =>
            handler.apply(this, args)
          );
        },
      };
      3

    Setelah menambahkan mixin, objek

    let eventMixin = {
      /**
       * Berlangganan ke peristiwa, menggunakan:
       *  menu.on('select', function(item) { .. }
       */
      on(eventName, handler) {
        if (!this._eventHandlers) this._eventHandlers = {};
        if (!this._eventHandlers[eventName]) {
          this._eventHandlers[eventName] = [];
        }
        this._eventHandlers[eventName].push(handler);
      },
    
      /**
       * Membatalkan langganan, menggunakan:
       *  menu.off('select', handler)
       */
      off(eventName, handler) {
        let handlers = this._eventHandlers?.[eventName];
        if (!handlers) return;
        for (let i = 0; i < handlers.length; i++) {
          if (handlers[i] === handler) {
            handlers.splice(i--, 1);
          }
        }
      },
    
      /**
       * menghasilkan peristiwa dengan nama dan data yang diberikan
       *  this.trigger('select', data1, data2);
       */
      trigger(eventName, ...args) {
        if (!this._eventHandlers?.[eventName]) {
          return; // tidak ada penangan untuk nama peristiwa itu
        }
    
        // panggil penangannya
        this._eventHandlers[eventName].forEach((handler) =>
          handler.apply(this, args)
        );
      },
    };
    _8 akan dapat membuat acara
    let eventMixin = {
      /**
       * Berlangganan ke peristiwa, menggunakan:
       *  menu.on('select', function(item) { .. }
       */
      on(eventName, handler) {
        if (!this._eventHandlers) this._eventHandlers = {};
        if (!this._eventHandlers[eventName]) {
          this._eventHandlers[eventName] = [];
        }
        this._eventHandlers[eventName].push(handler);
      },
    
      /**
       * Membatalkan langganan, menggunakan:
       *  menu.off('select', handler)
       */
      off(eventName, handler) {
        let handlers = this._eventHandlers?.[eventName];
        if (!handlers) return;
        for (let i = 0; i < handlers.length; i++) {
          if (handlers[i] === handler) {
            handlers.splice(i--, 1);
          }
        }
      },
    
      /**
       * menghasilkan peristiwa dengan nama dan data yang diberikan
       *  this.trigger('select', data1, data2);
       */
      trigger(eventName, ...args) {
        if (!this._eventHandlers?.[eventName]) {
          return; // tidak ada penangan untuk nama peristiwa itu
        }
    
        // panggil penangannya
        this._eventHandlers[eventName].forEach((handler) =>
          handler.apply(this, args)
        );
      },
    };
    9 saat pengunjung masuk. Dan objek lain, katakanlah,
    // Membuat kelas
    class Menu {
      choose(value) {
        this.trigger("select", value);
      }
    }
    // Tambahkan mixin dengan metode terkait peristiwa
    Object.assign(Menu.prototype, eventMixin);
    
    let menu = new Menu();
    
    // tambahkan penangan, untuk dipanggil saat seleksi:
    menu.on("select", value => alert(`Value selected: ${value}`));
    
    // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
    // Value selected: 123
    menu.choose("123");
    _0 mungkin ingin mendengarkan acara semacam itu untuk memuat kalender untuk orang yang masuk

    Atau,

    // Membuat kelas
    class Menu {
      choose(value) {
        this.trigger("select", value);
      }
    }
    // Tambahkan mixin dengan metode terkait peristiwa
    Object.assign(Menu.prototype, eventMixin);
    
    let menu = new Menu();
    
    // tambahkan penangan, untuk dipanggil saat seleksi:
    menu.on("select", value => alert(`Value selected: ${value}`));
    
    // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
    // Value selected: 123
    menu.choose("123");
    _1 dapat menghasilkan peristiwa
    // Membuat kelas
    class Menu {
      choose(value) {
        this.trigger("select", value);
      }
    }
    // Tambahkan mixin dengan metode terkait peristiwa
    Object.assign(Menu.prototype, eventMixin);
    
    let menu = new Menu();
    
    // tambahkan penangan, untuk dipanggil saat seleksi:
    menu.on("select", value => alert(`Value selected: ${value}`));
    
    // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
    // Value selected: 123
    menu.choose("123");
    2 saat item menu dipilih, dan objek lain dapat mengatur penangan untuk bereaksi terhadap peristiwa itu. Dan seterusnya

    Ini kodenya

    let eventMixin = {
      /**
       * Berlangganan ke peristiwa, menggunakan:
       *  menu.on('select', function(item) { .. }
       */
      on(eventName, handler) {
        if (!this._eventHandlers) this._eventHandlers = {};
        if (!this._eventHandlers[eventName]) {
          this._eventHandlers[eventName] = [];
        }
        this._eventHandlers[eventName].push(handler);
      },
    
      /**
       * Membatalkan langganan, menggunakan:
       *  menu.off('select', handler)
       */
      off(eventName, handler) {
        let handlers = this._eventHandlers?.[eventName];
        if (!handlers) return;
        for (let i = 0; i < handlers.length; i++) {
          if (handlers[i] === handler) {
            handlers.splice(i--, 1);
          }
        }
      },
    
      /**
       * menghasilkan peristiwa dengan nama dan data yang diberikan
       *  this.trigger('select', data1, data2);
       */
      trigger(eventName, ...args) {
        if (!this._eventHandlers?.[eventName]) {
          return; // tidak ada penangan untuk nama peristiwa itu
        }
    
        // panggil penangannya
        this._eventHandlers[eventName].forEach((handler) =>
          handler.apply(this, args)
        );
      },
    };

    • // Membuat kelas
      class Menu {
        choose(value) {
          this.trigger("select", value);
        }
      }
      // Tambahkan mixin dengan metode terkait peristiwa
      Object.assign(Menu.prototype, eventMixin);
      
      let menu = new Menu();
      
      // tambahkan penangan, untuk dipanggil saat seleksi:
      menu.on("select", value => alert(`Value selected: ${value}`));
      
      // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
      // Value selected: 123
      menu.choose("123");
      3 – menyetel fungsi
      let eventMixin = {
        /**
         * Berlangganan ke peristiwa, menggunakan:
         *  menu.on('select', function(item) { .. }
         */
        on(eventName, handler) {
          if (!this._eventHandlers) this._eventHandlers = {};
          if (!this._eventHandlers[eventName]) {
            this._eventHandlers[eventName] = [];
          }
          this._eventHandlers[eventName].push(handler);
        },
      
        /**
         * Membatalkan langganan, menggunakan:
         *  menu.off('select', handler)
         */
        off(eventName, handler) {
          let handlers = this._eventHandlers?.[eventName];
          if (!handlers) return;
          for (let i = 0; i < handlers.length; i++) {
            if (handlers[i] === handler) {
              handlers.splice(i--, 1);
            }
          }
        },
      
        /**
         * menghasilkan peristiwa dengan nama dan data yang diberikan
         *  this.trigger('select', data1, data2);
         */
        trigger(eventName, ...args) {
          if (!this._eventHandlers?.[eventName]) {
            return; // tidak ada penangan untuk nama peristiwa itu
          }
      
          // panggil penangannya
          this._eventHandlers[eventName].forEach((handler) =>
            handler.apply(this, args)
          );
        },
      };
      3 untuk dijalankan saat peristiwa dengan nama itu terjadi. Secara teknis, ada
      // Membuat kelas
      class Menu {
        choose(value) {
          this.trigger("select", value);
        }
      }
      // Tambahkan mixin dengan metode terkait peristiwa
      Object.assign(Menu.prototype, eventMixin);
      
      let menu = new Menu();
      
      // tambahkan penangan, untuk dipanggil saat seleksi:
      menu.on("select", value => alert(`Value selected: ${value}`));
      
      // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
      // Value selected: 123
      menu.choose("123");
      _5 properti yang menyimpan daftar penangan untuk setiap nama acara, dan itu hanya menambahkannya ke daftar
    • // Membuat kelas
      class Menu {
        choose(value) {
          this.trigger("select", value);
        }
      }
      // Tambahkan mixin dengan metode terkait peristiwa
      Object.assign(Menu.prototype, eventMixin);
      
      let menu = new Menu();
      
      // tambahkan penangan, untuk dipanggil saat seleksi:
      menu.on("select", value => alert(`Value selected: ${value}`));
      
      // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
      // Value selected: 123
      menu.choose("123");
      6 – hapus fungsi dari register handler
    • // Membuat kelas
      class Menu {
        choose(value) {
          this.trigger("select", value);
        }
      }
      // Tambahkan mixin dengan metode terkait peristiwa
      Object.assign(Menu.prototype, eventMixin);
      
      let menu = new Menu();
      
      // tambahkan penangan, untuk dipanggil saat seleksi:
      menu.on("select", value => alert(`Value selected: ${value}`));
      
      // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
      // Value selected: 123
      menu.choose("123");
      7 – menghasilkan acara. semua penangan dari
      // Membuat kelas
      class Menu {
        choose(value) {
          this.trigger("select", value);
        }
      }
      // Tambahkan mixin dengan metode terkait peristiwa
      Object.assign(Menu.prototype, eventMixin);
      
      let menu = new Menu();
      
      // tambahkan penangan, untuk dipanggil saat seleksi:
      menu.on("select", value => alert(`Value selected: ${value}`));
      
      // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
      // Value selected: 123
      menu.choose("123");
      8 dipanggil, dengan register argumen
      // Membuat kelas
      class Menu {
        choose(value) {
          this.trigger("select", value);
        }
      }
      // Tambahkan mixin dengan metode terkait peristiwa
      Object.assign(Menu.prototype, eventMixin);
      
      let menu = new Menu();
      
      // tambahkan penangan, untuk dipanggil saat seleksi:
      menu.on("select", value => alert(`Value selected: ${value}`));
      
      // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
      // Value selected: 123
      menu.choose("123");
      9

    Penggunaan

    // Membuat kelas
    class Menu {
      choose(value) {
        this.trigger("select", value);
      }
    }
    // Tambahkan mixin dengan metode terkait peristiwa
    Object.assign(Menu.prototype, eventMixin);
    
    let menu = new Menu();
    
    // tambahkan penangan, untuk dipanggil saat seleksi:
    menu.on("select", value => alert(`Value selected: ${value}`));
    
    // memicu peristiwa => penangan di atas berjalan dan menunjukkan:
    // Value selected: 123
    menu.choose("123");

    Sekarang, jika kita ingin kode bereaksi terhadap opsi menu, kita dapat mendengarkannya dengan [[Prototype]]0

    Dan mixin [[Prototype]]1 membuatnya mudah untuk menambahkan perilaku itu ke sebanyak mungkin kelas yang kita inginkan, tanpa mengganggu rantai pewarisan

    Mixin – adalah istilah umum untuk pemrograman berorientasi objek. kelas yang berisi metode untuk kelas lain

    Beberapa bahasa lain memungkinkan pewarisan berganda. JavaScript tidak mendukung banyak pewarisan, tetapi mixin dapat diimplementasikan dengan menyalin metode ke dalam prototipe

    Kita bisa menggunakan mixin sebagai cara untuk menambahkan kelas dengan menambahkan beberapa perilaku, seperti penanganan event seperti yang telah kita lihat di atas

    Mixin dapat menjadi titik konflik jika mereka secara tidak sengaja mengganti metode kelas yang ada. Jadi umumnya orang harus memikirkan dengan hati-hati tentang metode penamaan mixin, untuk meminimalkan kemungkinan itu terjadi