前端设计模式

1、模块模式

在立即执行函数表达式中定义的变量和方法,在该函数外部是访问不到的,只能通过该函数提供的接口,”有限制的”进行访问;通过函数的作用域,解决了属性和方法的封装问题。 最常见的立即执行函数写法有以下两种:

(function(){ /* code */ }())

或者

(function(){ /* code */ })()

模块模式代码:

let Person = (function () {
    var age = "12";
    var name = "jerry";

    function getAge() {
        return age;
    }

    function getName() {
        return name;
    }
    return {
        getAge: getAge,
        getName: getName
    }
})()
console.log(age, 'age'); // 报错: Uncaught ReferenceError: age is not defined
console.log(name, 'name'); // 空字符串,为啥不报错?看底部备注
console.log(Person.age); // undefined
console.log(Person.name); // undefined
// 只能通过Person提供的接口访问相应的变量
console.log(Person.getName()); // jerry
console.log(Person.getAge()); // 12

2、构造函数模式

function Person(name, age) {
     this.name = name;
     this.age = age;
 }
 Person.prototype.printName = function () {
     console.log(this.name)
 }
 Person.prototype.printAge = function () {
     console.log(this.age)
 }

 function Student(name, age) {
     // 继承 Person 的属性
     Person.call(this, name, age)
 }

 function create(prototype) {
     function F() {}
     F.prototype = prototype
     return new F()
 }
 // 让Student的原型指向一个对象,该对象的原型指向了Person.prototype,通过这种方式继承 Person 的方法
 Student.prototype = create(Person.prototype)
 Student.prototype.printAge = function () {
     console.log(this.age)
 }
 let student = new Student('jerry', 12)
 student.printName() // "jerry"
 student.printAge() // "12"

3、混合模式

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.printName = function () {
    console.log(this.name)
}

function Student(name, age) {
    // 继承 Person 的属性
    Person.call(this, name, age)
}

function create(prototype) {
    function F() {}
    F.prototype = prototype
    return new F()
}
// 让Student的原型指向一个对象,该对象的原型指向了Person.prototype,通过这种方式继承 Person 的方法
Student.prototype = create(Person.prototype)
Student.prototype.printAge = function () {
    console.log(this.age)
}
let student = new Student('jerry', 12)
student.printName() // "jerry"
student.printAge() // 12

4、工厂模式

function Person(name, age) {
    let person = new Object()
    person.name = name
    person.age = age
    person.printName = function () {
        console.log(this.name)
    }
    person.printAge = function () {
        console.log(this.age)
    }
    return person
}
let person = Person('jerry', 12)
person.printName()
person.printAge()

5、单例模式

let Singleton = (function () {
     let instantiated

     function init() {
         /*定义单例代码*/
         return {
             publicMethod: function () {
                 console.log("Hello World");
             },
             publicProperty: "Test"
         }
     }
     return {
         getInstance: function () {
             if (!instantiated) {
                 instantiated = init()
             }
             return instantiated
         }
     }
 }())
 Singleton.getInstance().publicMethod()

单例之间的通讯: 建立两个独立的对象:jim&&lily,两者之间通过door直接通讯,如果没有新建door,有直接通讯。代码如下:

let jim = (function (argument) {
     let door
     let jimHome = function (msg) {
         this.doorbell = msg
     }
     let info = {
         sendMessage: function (msg) {
             if (!door) {
                 door = new jimHome(msg)
             }
             return door
         },
         coming: function () {
             return "來了來了"
         }
     }
     return info
 }())
 let lily = {
     callJim: function (msg) {
         let _xw = jim.sendMessage(msg)
         alert(_xw.doorbell)
         console.log(_xw.doorbell)
         _xw = null // 等待垃圾回收
         let coming = jim.coming()
         console.log(coming)
     }
 }
 lily.callJim("叮咙")

6、发布-订阅模式

订阅发布模式定义了一种一对多的依赖关系,让多个订阅者对象同时监听某一个主题对象。这个主题对象在自身主题变化时,会通知所有订阅者对象,使他们能够自动更新自己的状态。

将一个系统分割成一系列相互协作的类有一个很不好的副作用:需要维护相应对象间的一致性,这样会给维护、扩展和重用都带来不便。当一个对象的改变需要同时改变其他对象,而且他不知道具体有多少对象需要改变时,此时建议使用订阅发布模式。

应用场景:

DOM事件。DOM事件是一种典型的发布-订阅模式,对一个DOM节点的DOM事件进行监听;当操作DOM节点时,触发相应的事件并执行函数。

自定义时间。指定发布者,类似于一个对象(key:value);key表示事件的名称,value是一个数组;发布消息后,遍历value的数组,依次执行订阅者的回调函数。

应用Demo如下:

 let Event = (function () {
    var events = {}

    function on(evt, handler) {
        events[evt] = events[evt] || [];
        events[evt].push({
            handler: handler
        })
    }

    function fire(evt, args) {
        if (!events[evt]) {
            return
        }
        for (var i = 0; i < events[evt].length; i++) {
            events[evt][i].handler(args)
        }
    }

    function off(evt) {
        delete events[evt]
    }
    return {
        on: on,
        fire: fire,
        off: off
    }
}())
Event.on('change', function (val) {
    console.log('change事件,value is' + val)
})
Event.on('click', function (val) {
    console.log('click事件,value is ' + val)
})
Event.fire('change', 'jerry1')
Event.fire('click', 'jerry2')
Event.off('change')

备注:console.log(name, ‘name’)没有报错,是因为name是浏览器的窗口变量名,已存在于浏览器内部。

Comments: 1

「人生在世,留句话给我吧」

提交评论