blog icon indicating copy to clipboard operation
blog copied to clipboard

JS Design Patterns

Open nanyang24 opened this issue 7 years ago • 0 comments

JS设计模式

常见 设计模式有

  1. 构造函数模式 constructor
  2. 工厂模式 factory
  3. 混合模式 mixin
  4. 模块模式 module
  5. 单例模式 singleton
  6. 订阅发布模式 subscibe & pulish
  7. 观察者模式 observer

构造函数模式 constructor

使用场景:创建一个复杂的对象,可以添加方法

new 函数调用,返回新的对象。每次都是新的引用 方法放在原型链上

//构造函数

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function () {
    return this.name;
};
var student = new Person("若愚", 30);
var student1 = new Person("slash", 30); 

工厂模式 factory

使用场景:创建一个简单的对象

存在的意义是 每次创建新的引用,生产出来的数据结构一致

//工厂模式

function createPerson(name) {
    var person = {
        name: name,
        sayName: function () {
            console.log(this.name);
        }
    };
    return person;
}; // 开辟新内存

// createPerson('a') 
// createPerson('b')

混合模式 mixin

使用场景:可继承的公用方法

通过继承来实现

将一些可重用的代码放在父类上,子类通过继承得到属性和方法,并能根据需要自身添加新的属性、方法

// 混合模式 mixin == > 实现继承 

// 实例1js 
var Person = function (name, age) {
    this.name = name;
    this.age = age;
};
Person.prototype.sayName = function () {
    console.log(this.name);
}

var Student = function (name, age, score) {
    // 实例属性实现继承 
    Person.call(this, name, age);
    this.score = score;
};

// 将Person.prototype挂在Student.prototype的原型链第一层 
// 原型链实现继承 

Student.prototype = Object.create(Person.prototype);
Student.prototype.getName = function () {
    return this.name;
}

模块模式 module

 使用场景:需要封装的组件

封闭作用域,通过闭包读取内部变量

暴露给外界的仅仅是return选择的内容

//模块模式

//通过闭包,实现作用域的隔离来实现一个模块 

var Person = (function () {
    var name = 'ruoyu';

    function sayName() {
        console.log(name);
    };  //词法作用域:一个函数可以访问到的上下文环境由它所定义的位置决定
    // 词法作用域 
    return {
        name: name,
        sayName: sayName
    }
})();

单例模式 singleton

使用场景:只需出现一次,独一无二的组件

匿名函数(lambda函数)划分命名空间,避免全局污染

可以被实例化,且实例化一次。

指向同一个引用,只开辟一次内存空间

//单例模式 singleton单例

// 匿名函数 
var People = (function () {
    var instance;

    function init(name) {
        return {
            name: name
        };
    };
    //词法作用域 
    return {
        createPeople: function (name) {
            if (!instance) {            // 如果没有instance就初始化
                instance = init(name);  // 所以第一次调用就初始化,之后不再执行这段代码
            }
            return instance;        //单例就是只开辟了一个内存空间
        }                           //第一次传入的值被储存下来
    };                              //之后生成的实例全都指向同样的地方
}());

People.createPeople('jirengu') === People.createPeople('hello') //true //{name:'jirengu'} {name:'jirengu'}

订阅发布模式 subscibe & pulish

// 订阅发布模式 

//逻辑:
  // subscribe publish $('.btn').on('click', function (event) {
  //     console.log('clicked')
  // }); === > {
  //     'click': [fn]
  // }
  // $('.btn').on('mouseover', function (event) { 
  // $('.btn').trigger('click',event); ====>  [fn].forEach(fn(event)); 
  // })
  
 
var EventCenter = (function () {      // 新建订阅中心
    //我们如何去实现 
    var F = function () {
        this.eventPool = {};          // 新建缓存列表,存储订阅者的回调函数
    };
    F.prototype.on = function (name, callback) {  // 添加消息:绑定事件
        this.eventPool[name] = this.eventPool[name] || []; //检查事件列表events{}里是否有这个key
        this.eventPool[name].push(callback);    在选定事件里增加一个函数
    };
    F.prototype.trigger = function (name) {
        if (!this.eventPool[name]) {
            this.eventPool[name] = [];
        }
        this.eventPool[name].forEach(function (fn) {  //如果存在,遍历列表events,依次执行处理程序
            fn()
        })
    };
    return F;
}());

 var e = new EventCenter();
 e.on('hello', function () {
     console.log('hello')
 });
 
 //  === > 存 { 'hello': [fn] }

 e.trigger('hello'); 
 // hello ===> 取后执行
 

观察者模式 observer

//观察者模式 observer

$('input').change(function () {
    $('input').observers.forEach(function (observer) {
        observer();
    })
});

$('input').observers = [];

$('input').subscribe = function (fn) {
    this.observers.push(fn);
};

//  API使用方式 
//  $('.input').subscribe(fn) ==> 记录日志 
//  $('input').subscribe(fn1) ==> 发起请求 
//  $('input').subscribe(fn2) ==> 改变UI

订阅发布 模式 与 观察者模式 的区别

很多的资料都认为二者是一样的 其实仔细考虑,还是有很重要的不同点

  • 观察者模式 创建一个需要观察的事件时,要先到subscribe里面进行注册,这个函数将此事件推送到需要通知的数组里,然后,当订阅的内部状态发生变化时,把这个变化通知所有的观察者。 随意注册事件,调用事件,不会对事情做区分

  • 发布-订阅模式 角色为发布者(publisher)和订阅者(subscriber),pub和sub之间没有直接的耦合关系,pub发布一个消息事件(event),sub订阅感兴趣的消息事件,sub也可以取消订阅。 是对特定一系列的订阅事件进行统一管理

初步得出结论:

观察者模式中的观察者是和主题对象紧耦合的。 发布-订阅模式中的发布者和订阅者是松耦合的,发布者和订阅者是通过事件发生联系的。 在开发大型项目的时候,订阅/发布模式会让业务更清晰。

JS设计模式漫谈 Design-Patterns-Tutorial


使用发布订阅模式写一个事件管理器,可以实现如下方式调用

Event.on('change', function(val){
    console.log('change...  now val is ' + val);  
});
Event.fire('change', '饥人谷');
Event.off('changer');

代码

var eventCenter = (function () {
    var e = function () {
        this.eventPool = [];
    }
    e.prototype = {
        on: function (name, callback) {
            this.eventPool[name] = this.eventPool[name] || [];
            this.eventPool[name].push(callback);
        },
        fire: function (name, value) {
            if (!this.eventPool[name]) return;
            this.eventPool[name].forEach(function (e) {
                e(value);
            });
        },
        off: function (name) {
            delete this.eventPool[name];
        }
    };
    return e;
})()

//测试
var Event = new eventCenter()
Event.on('change', function (val) {
    console.log('change...  now val is ' + val);
});
Event.fire('change', 'aaaaaa');
Event.off('change');
Event.on('change1', function (val) {
    console.log('第二次~' + val);
});
Event.fire('change1', 'bbbbbb');

nanyang24 avatar Sep 01 '17 08:09 nanyang24