Javascript设计模式之原型模式详细

2021-09-30

目录
  • 1、原型模式
    • 示例一
    • 示例二
    • 示例三
  • 2、观察者模式

    1、原型模式

    原型模式用于在创建对象时,通过共享某个对象原型的属性和方法,从而达到提高性能、降低内存占用、代码复用的效果。

    示例一

    function Person(name) {
      this.name = name;
    
      this.config = {
        a: "1",
        b: "2",
      };
    
      this.hello = function () {
        console.info("hello");
      };
    }
    

    假如需要通过以上代码创建 100 个实例,那么将需要创建 100 个 config、100 个 hello,而这两个东西在每个实例里面是完全一样的。

    因此我们可以通过提取公共代码的方式进行油优化。

    const config = {
      a: "1",
      b: "2",
    };
    const hello = function () {
      console.info("hello");
    };
    function Person(name) {
      this.name = name;
    
      this.config = config;
    
      this.hello = hello
    }
    
    

    这样的方式使得无论创建多少个Person对象都只需要创建一个config、一个hello。 但是仍然污染全局变量、config被误修改、Person和其他代码耦合大、不易于代码扩展维护等问题。

    因此可以通过原型的方式进行优化。

    function Person() {}
    var p = new Person();
    
    
    

    该函数创建实例时原型图如下:

    示例二

    function Person(name) {
      this.name = name;
    
      this.config = {
        a: "1",
        b: "2",
      };
    
      this.hello = function () {
        console.info("hello");
      };
    }
    
    //此方式会重写prototype,造成constructor丢失,变为Object()。
    //可以使用Person.prototype.xx=yy的方式写,或者重新指定Person.prototype.constructor=Person
    Person.prototype = {
      version: 1.0,
      say: function (arg) {
        console.info(`${this.name} say ${arg}`);
      },
      constructor: Person,
    };
    var p1 = new Person("p1");
    var p2 = new Person("p2");
    
    console.info(p1.config == p2.config); //false
    console.info(p1.hello == p2.hello); //false
    console.info(p1.say === p2.say); //true
    p1.say("qq");
    p2.say("qq");
    console.info(p1.version === p2.version); //true
    console.info(p1.version);
    

    该函数创建实例时原型图如下:

    示例三

    function Person(name) {
      this.name = name;
    
      this.config = {
        a: "1",
        b: "2",
      };
    
      this.hello = function () {
        console.info("hello");
      };
    }
    
    //此方式会重写prototype,造成constructor丢失,变为Object()
    Person.prototype = {
      version: 1.0,
      say: function (arg) {
        console.info(`${this.name} say ${arg}`);
      },
    };
    
    function PersonA(name) {
      Person.call(this, name);
    }
    PersonA.prototype = Person.prototype;
    
    function PersonB(name) {
      Person.call(this, name);
    }
    PersonB.prototype = Person.prototype;
    var pA = new PersonA("pa");
    var pB = new PersonB("pb");
    
    console.info(pA.config == pB.config); //false  内部属性比较
    console.info(pA.hello == pB.hello); //false  内部属性比较
    console.info(pA.say === pB.say); //true  原型方法共享
    pA.say("qq");
    pB.say("qq");
    console.info(pA.version === pB.version); //true  原型属性共享
    console.info(pA.version); //1.0
    Person.prototype.version = 2.0; //修改原型共享属性
    console.info(pB.version); //2.0
    console.info(new Person().version); //2.0
    
    //修改原型共享方法
    PersonB.prototype.say = function (arg) {
      console.info(`v2--- ${this.name} say ${arg}`);
    };
    pB.say("qq");
    new Person("Person").say("ww");
    
    

    总结:

    js 在创建对象比较消耗内存、耗时长,可以通过减少内部属性创建的方式降低内存占用。

    而原型模式就是使用 javascript 语言的原型特性进行相同属性的共享,从而达到降低内存占用、提高对象创建效率。

    2、观察者模式

    观察者模式用于模块、组件之间通讯,通过提供统一的模式进行事件订阅、事件发布。从而达到模块、组件之间解耦,提高代码的可维护性。

    模块之间、组件之间通讯方式

    模块之间、组件之间采用直接引用通讯方式

    const moduleA = {
      say: function (msg) {
        console.info("A say " + msg);
      },
    
      letBrun: function () {
        //直接引用了moduleB
        moduleB.run();
      },
    };
    
    const moduleB = {
      run: function () {
        console.info("B run ");
      },
    
      letAsay: function () {
        //直接引用了moduleA
        moduleA.say("hello");
      },
    };
    
    moduleA.letBrun(); //B Run
    moduleB.letAsay(); //A say hello
    
    

    模块之间、组件之间采用父组件通讯方式

    const moduleA = {
      say: function (msg) {
        console.info("A say " + msg);
      },
    };
    
    const moduleB = {
      run: function () {
        console.info("B run ");
      },
    };
    
    const parentModule = {
      moduleA,
      moduleB,
    
      letBrun: function () {
        this.moduleB.run();
      },
    
      letAsay: function () {
        this.moduleA.say("hello");
      },
    };
    
    parentModule.letBrun(); //B Run
    parentModule.letAsay(); //A say hello
    
    

    事件模块实现通讯

    function Emitter() {
      this.events = {};
      this.res_oldAction = {}
      this.res_action_events = {}
    }
    
    //订阅资源
    Emitter.prototype.subscribe = function (res, action, fn) {
      if(!this.res_oldAction[res.name]){
     this.res_oldAction[res.name] = res[action]
     res[action] = (data) => {
          this.res_oldAction[res.name](data)
       const fns = this.res_action_events[res.name].action;
          for (let i = 0; i < fns.length; i++) {
            fns[i](data);
          }
        }
      }
      
      if(!this.res_action_events[res.name]){
     this.res_action_events[res.name] = {}
      }
      
      if(!this.res_action_events[res.name][action]){
     this.res_action_events[res.name][action] = []
      }
      
      this.res_action_events[res.name].action.push(fn)
    }
    
    //取消订阅资源
    Emitter.prototype.unsubscribe = function (res, action, fn) {
      const fns = this.res_action_events[res.name].action;
      for (let i = 0; i < fns.length; i++) {
     if (fns[i] === fn) {
       fns.splice(i, 1);
       i--;
     }
      }
    }
    
    Emitter.prototype.on = function (name, fn) {
      if (!this.events[name]) {
        this.events[name] = [];
      }
    
      this.events[name].push(fn);
    };
    
    Emitter.prototype.remove = function (name, fn) {
      if (!this.events[name]) {
        return;
      }
    
      const fns = this.events[name];
    
      for (let i = 0; i < fns.length; i++) {
        if (fns[i] === fn) {
          fns.splice(i, 1);
          i--;
        }
      }
    };
    
    Emitter.prototype.fire = function (name, data) {
      if (!this.events[name]) {
        return;
      }
    
      const fns = this.events[name];
    
      for (let i = 0; i < fns.length; i++) {
        fns[i](data);
      }
    };
    
    const emitter = new Emitter();
    
    //模块A中注册事件
    const methodA = (data) => {
      console.info("模块A接受到food消息:");
      console.info(data);
    };
    
    emitter.on("food", methodA);
    
    //模块B中注册事件
    const methodB = (data) => {
      console.info("模块B接受到food消息:");
      console.info(data);
    };
    emitter.on("food", methodB);
    
    //模块C中触发事件
    emitter.fire("food", "饭来了");
    
    //模块B中移除事件
    emitter.remove("food", methodB);
    
    //模块C中再次触发事件
    emitter.fire("food", "饭又来了");
    
    

    执行结果如下:

    模块 A 接受到 food 消息:

    饭来了

    模块 B 接受到 food 消息:

    饭来了

    模块 A 接受到 food 消息:

    饭又来了

    总结:

    js 组件模块的通讯方式一般分为3种(直接通讯、通过父组件通讯、通过事件模块通讯)。观察者模式用于模块、组件之间通讯,通过提供统一的模式进行事件订阅、事件发布,从而达到模块、组件之间解耦,提高代码的可维护性

    到此这篇关于Javascript设计模式之原型模式详细的文章就介绍到这了,更多相关Javascript原型模式内容请搜索北冥有鱼以前的文章或继续浏览下面的相关文章希望大家以后多多支持北冥有鱼!

    《Javascript设计模式之原型模式详细.doc》

    下载本文的Word格式文档,以方便收藏与打印。