本小节主要讲解三种常用的设计模式和命名空间,第一种是工厂模式,第二种是单利模式,第三种是观察者模式
设计模式概述
是为了解决在开发中可能遇到的需求(相似),而提出的一套解决方法.
设计模式要求:
- 在开发中整个系统需要一套设计模式(架构师)
- 来源:建筑(建房子)领域
- 设计模式的四人帮: Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides
- 设计模式:总共有23种.
- **设计模式类型:单利(例)模式 |观察者模式 | 代理模式 |工厂模式 |适配器模式 |桥接模式 | .... **
- 设计模式的书:<设计模式><大话设计模式|大话数据结构><23种常见的设计模式>
工厂模式
批量创建大量的同类型的对象
优点 :
- 可以通过统一的借口来创建对象,根据传入的参数不同来创建不同的对象,易于扩展和维护,稳定性更好
核心过程 :
- 提供一个父构造函数
- 设置这个父构造函数的原型对象(属性|方法)
- 在父构造函数身上添加静态工厂方法
1.需要接收传入的参数(要生产的产品的类型)
2.判断 是否支持生产
3.设置子构造函数的原型对象
4.把新创建的对象返回
-
定制合作伙伴
-
直接使用父构造函数的静态工厂方法来创建指定的产品对象
-
示例代码 :
<script>
//1. 提供一个父构造函数
function PhoneMake(){};
//2. 设置这个父构造函数的原型对象(属性|方法)
PhoneMake.prototype.logDes = function(){
console.log("我们的口号是:" + this.des);
}
//3. 在父构造函数身上添加静态工厂方法
PhoneMake.factory = function(typeStr){
// 3.1 需要接收传入的参数(要生产的产品的类型)
var productType = typeStr;
//var Dog = PhoneMake[productType];
//3.2 判断 是否支持生产
if (typeof PhoneMake[productType] != "function")
{
//抛出一个异常
throw "对不起,我们工厂和这个品牌没有商务合作,不能生产!"
}
//3.3 设置子构造函数的原型对象
//为了获得构造函数原型对象的方法
PhoneMake[productType].prototype = new PhoneMake();
//3.4 设置子构造函数的原型对象
var newProduct = new PhoneMake[productType]();
//3.5 把新创建的对象返回
return newProduct;
}
//4. 定制合作伙伴
PhoneMake.iphone = function(){
this.des = "最安全最稳定的系统,最垃圾的体验"
}
PhoneMake.oppo = function(){
this.des = "充电两小时,通话五分钟"
}
PhoneMake.vivo = function(){
this.des = "照亮你的美,你本来就很美"
}
PhoneMake.meizu = function(){
this.des = "我就是我,不一样的魅族"
}
//5. 直接使用父构造函数的静态工厂方法来创建指定的产品对象
var iphone = PhoneMake.factory("iphone");
var vivo = PhoneMake.factory("vivo");
var oppo = PhoneMake.factory("oppo");
var meizu = PhoneMake.factory("meizu");
var xiaomi = PhoneMake.factory("xiaomi");
iphone.logDes();
vivo.logDes();
oppo.logDes();
meizu.logDes();
</script>
单利模式
在整个程序的运行过程中,一个类只有一个实例对象
js中的单利模式
- js没有类(ES6才有的) ,js实现单利模式(限定讨论的范围)
1.字面量
2.内置构造函数(Array Date Function Object)
3.工厂函数
4.自定义构造函数(单利模式)
- js是什么样的语言
1.弱类型,脚本,轻量级,面向对象,基于原型(对象),解释行语言.函数式.
2.js到底是不是一门面向对象(类)的语言?
3.js是一门支持面向对象的语言.(封装|继承|多态)
单利模式实现之后的表现
var p1 = new 构造函数()
var p2 = new 构造函数()
p1 == p2
- 示例代码 :
<script>
function Person(){
//首先创建一个空的对象
//默认把新的对象赋值给this
//把新对象返回
}
var p1 = new Person();
var p2 = new Person();
console.log(p1 == p2);
</script>
单利模式的实现方式01 ----> 全局变量
全局变量来保存对象实现单利模式
- 提供一个全局的变量
- 提供一个构造函数Person
- 在构造函数内部先判断全局变量是否有值,如果有那么就直接返回
- 如果没有,那么就把this赋值给全局变量
- 通过this设置属性和方法
存在的问题
-
使用一个全局变量来保存单利对象,该全局变量在整个作用域中都可以被访问或者是修改,可能会轻易的被覆盖或者是修改.
-
修改之后,创建出来的实例对象就不再是之前的那个单利对象了.
-
示例代码 :
<script>
var instance;
function Person(){
if(instance)
{
console.log("对象已经被创建,直接把之前创建好的对象返回");
return instance;
}
instance = this;
this.name = "奥特曼";
this.age = 1000;
console.log("第一次创建对象,创建对象之后并返回");
}
var p1 = new Person();
var p2 = new Person();
console.log(p1 == p2);
var p3 = new Person();
instance = "demo";
var p4 = new Person();
console.log(p4);
console.log(p4 == p1);
</script>
单利模式的实现方式02 ----> 静态属性
静态成员:直接添加到构造函数身上的属性或者是方法
存在的问题
-
构造函数的静态属性其实也可能被修改,因此这种方法也不安全
-
示例代码 :
<script>
function Person(){
//判断对象是否已经被创建
if (Person.instance)
{
console.log("之前已经创建过对象,直接返回");
return Person.instance;
}
this.name = "大黄蜂"
Person.instance = this;
console.log("第一次创建");
}
var p1 = new Person();
var p2 = new Person();
console.log(p1 == p2);
instance = "demo";
var p3 = new Person();
console.log(p1 == p3);
Person.instance = "123";
var p4 = new Person();
console.log(p4 == p1);
</script>
单利模式的实现方式03 ----> 惰性函数
核心过程
-
提供一个构造函数
-
在构造函数内部声明一个私有的变量
-
使用惰性函数定义更新构造函数的实现(直接把instance返回)
-
设置原型对象[新构造函数的原型对象 = 旧构造函数的原型对象]
构造函数,prototype == 对象.proto -
使用新的构造函数创建实例对象,并且赋值给instance
-
修正对象的构造函数指向
-
通过instance设置实例属性和方法
-
示例代码 :
<script>
// 01 提供一个构造函数
function Person(){
//this01
//02 在构造函数内部声明一个私有的变量
var instance;
//03 使用惰性函数定义更新构造函数的实现(直接把instance返回)
Person = function(){
//内部默认会创建一个空的对象 this02
return instance;
}
//04 设置原型对象[新构造函数的原型对象 = 旧构造函数的原型对象]
//原型链继承:Man.prototype = new Person();
//原型式继承:Man.prototype = Person.prototype;
//Person.prototype = this.__proto__; //非标准(代码中不要出现)
Person.prototype = this;
//05 使用新的构造函数创建实例对象,并且赋值给instance
instance = new Person();
//instance = this;
//06 修正对象的构造函数指向
instance.constructor = Person;
// 07 通过instance设置实例属性和方法
instance.name = "我很好听";
// 08 把instance返回
return instance;
}
Person.prototype.des = "描述信息";
var p1 = new Person();
Person.prototype.hi = "hi";
var p2 = new Person();
console.log(p1 == p2);
console.log(p1.constructor == Person); //true
console.log(p1.des);
console.log(p1.hi);
</script>
单利模式的实现方式04 ----> 全局变量 + 即时函数
- 示例代码 :
<script>
var Person;
(function(){
var instance;
Person = function (){
if(instance)
{
return instance;
}
this.name = "momo";
instance = this;
}
})();
var p1 = new Person();
var p2 = new Person();
console.log(p1 == p2);
</script>
观察者模式
观察者模式举例说明
-
男生A和男生B同时都喜欢女生C,他们想时时监视女生C的动向,动态,所以他们找了女生C的闺蜜作为观察者帮他们监视实时动态.这样不管女生C有什么最新的动态都会被男生A和男生B监听到,开发中就是男生多了点,动态多了点,核心内容就是这样
-
要求:
女神:rose(发布者)
男生:jack(订阅者)
男生:tom(订阅者)
- 过程:
创建或者是设置一个发布者
创建订阅者对象
注册订阅者
测试(发状态)
- 示例代码1 : 一个发布者,两个订阅者,关注的是一个状态
<script>
//01 创建或者是设置一个发布者
var rose = {
user:[],
addUser:function(fn){
if (typeof fn != "function")
{
throw "不支持该操作!";
}
this.user.push(fn);
},
removeUser:function(fn){
for (var i = 0; i < this.user.length; i++) {
if(this.user[i] == fn)
{
console.log(fn + "取消了订阅");
this.user.splice(i,1);
}
}
},
eat:function(){
for (var i = 0; i < this.user.length; i++) {
this.user[i]();
}
}
}
// 02 创建订阅者对象
var jack = {
eat_jack:function(){
console.log("我陪你去吃拉面吧 ---jack");
}
}
var tom = {
eat_tom:function(){
console.log("我陪你去吃寿司吧 ---tom");
}
}
// 03 注册订阅者
rose.addUser(jack.eat_jack);
rose.addUser(tom.eat_tom);
//04 发布者状态改变
rose.eat();
rose.removeUser(jack.eat_jack);
rose.eat();
</script>
- 示例代码2 : 多个状态
<script>
//01 创建或者是设置一个发布者
var publisher = {
addUser:function(fn,type){
var type = type || "eat";
if (typeof fn != "function")
{
throw "不支持该操作!";
}
this.user[type].push(fn);
},
removeUser:function(fn,type){
var type = type || "eat";
for (var i = 0; i < this.user[type].length; i++) {
if(this.user[type][i] == fn)
{
console.log(fn + "取消了订阅");
this.user[type].splice(i,1);
}
}
},
eat:function(){
for (var i = 0; i < this.user["eat"].length; i++) {
this.user["eat"][i]();
}
},
sleep:function(){
for (var i = 0; i < this.user["sleep"].length; i++) {
//console.log(this.user,"++++");
this.user["sleep"][i]();
}
}
}
var rose = {};
//封装一个函数用来快速的让某个指定对象成为发布者
function makePublisher(o){
for(var i in publisher)
{
if (publisher.hasOwnProperty(i) && typeof publisher[i] == "function" ){
o[i] = publisher[i];
}
}
o.user = {
eat:[],
sleep:[]
};
}
makePublisher(rose);
// 02 创建订阅者对象
var jack = {
eat_jack:function(){
console.log("我陪你去吃拉面吧 ---jack");
},
sleep_jack:function(){
console.log("晚安 rose ---jack");
}
}
// 03 注册订阅者
rose.addUser(jack.eat_jack,"eat"); //关注rose肚子饿不饿
rose.addUser(jack.sleep_jack,"sleep"); //关注rose困不困
rose.eat();
rose.sleep();
- 示例代码3 : 通用性处理
<script>
//01 创建或者是设置一个发布者
var publisher = {
addUser:function(fn,type){
var type = type || "eat";
if (this.user[type] == undefined)
{
this.user[type] = [];
}
if (typeof fn != "function")
{
throw "不支持该操作!";
}
this.user[type].push(fn);
},
removeUser:function(fn,type){
this.publish(type,fn);
},
publish:function(type,fn){
var type = type || "eat";
for (var i = 0; i < this.user[type].length; i++) {
//判断当前是要取消订阅还是要发布状态
if (typeof fn == "function")
{
if(this.user[type][i] == fn)
{
console.log(fn + "取消了订阅");
this.user[type].splice(i,1);
}
}else
{
this.user[type][i]();
}
}
}
}
var rose = {
eat:function(){
this.publish("eat");
},
sleep:function(){
this.publish("sleep");
},
read:function(){
this.publish("read");
}
};
function makePublisher(o){
for(var i in publisher)
{
if (publisher.hasOwnProperty(i) && typeof publisher[i] == "function" ){
o[i] = publisher[i];
}
}
o.user = {
eat:[],
sleep:[]
};
}
makePublisher(rose);
var jack = {
eat_jack:function(){
console.log("我陪你去吃拉面吧 ---jack");
},
sleep_jack:function(){
console.log("晚安 rose ---jack");
}
}
var tom = {
eat_tom:function(){
console.log("我买给你吧 ---tom");
},
sleep_tom:function(){
console.log("今晚的太阳很好看 ---tom");
},
read_tom:function(){
console.log("你也在学习js吗?");
}
}
rose.addUser(jack.eat_jack,"eat");
rose.addUser(tom.sleep_tom,"sleep");
rose.addUser(tom.read_tom,"read");
rose.eat();
rose.sleep();
rose.read();
</script>
- 示例代码4 : 订阅者成为发布者
<script>
//01 创建或者是设置一个发布者
var publisher = {
addUser:function(fn,type){
var type = type || "eat";
if (this.user[type] == undefined)
{
this.user[type] = [];
}
if (typeof fn != "function")
{
throw "不支持该操作!";
}
this.user[type].push(fn);
},
removeUser:function(fn,type){
this.publish(type,fn);
},
publish:function(type,fn){
var type = type || "eat";
for (var i = 0; i < this.user[type].length; i++) {
//判断当前是要取消订阅还是要发布状态
if (typeof fn == "function")
{
if(this.user[type][i] == fn)
{
console.log(fn + "取消了订阅");
this.user[type].splice(i,1);
}
}else
{
this.user[type][i]();
}
}
}
}
var rose = {
eat:function(){
this.publish("eat");
},
sleep:function(){
this.publish("sleep");
},
read:function(){
this.publish("read");
},
lol_rose:function(){
console.log("你怎么又在打游戏?还是游戏比较重要一些?")
}
};
function makePublisher(o){
for(var i in publisher)
{
if (publisher.hasOwnProperty(i) && typeof publisher[i] == "function" ){
o[i] = publisher[i];
}
}
o.user = {
eat:[],
sleep:[]
};
}
makePublisher(rose);
var jack = {
eat_jack:function(){
console.log("我陪你去吃拉面吧 ---jack");
},
sleep_jack:function(){
console.log("晚安 rose ---jack");
},
statusLol:function(){
this.publish("lol");
}
}
var tom = {
eat_tom:function(){
console.log("我买给你吧 ---tom");
},
sleep_tom:function(){
console.log("今晚的太阳很好看 ---tom");
},
read_tom:function(){
console.log("你也在学习js吗?");
},
lol_rose:function(){
console.log("好兄弟,终于来啦");
}
}
rose.addUser(jack.eat_jack,"eat");
rose.addUser(tom.sleep_tom,"sleep");
rose.addUser(tom.read_tom,"read");
rose.eat();
rose.sleep();
rose.read();
//设置jack成为发布者,状态(lol)
makePublisher(jack);
jack.addUser(rose.lol_rose,"lol");
jack.addUser(tom.lol_rose,"lol");
jack.statusLol();
</script>
备忘模式(函数结构缓存)
特定场合:
- 计算的函数f()
- 某些参数需要进行大规模反复的计算,可以考虑吧计算的结果保存起来
f(n) ..... =>1000m
代码中某些参数可能会反复计算
f(10) ===>ssddd
f(10) ===>ssddd
使用一个缓存对象cacheObj{key-value}
- 思路:
1.提供一个全局的对象(缓存对象),key-value
2.当我们传递参数需要进行计算(逻辑)的时候,先检查缓存对象中是否有对应的结果
3.如果有缓存数据,那么就直接使用(可以节省时间,提高效率)
4.如果没有缓存数据,那么这个时候再执行计算操作,处理得到结果之后,把这个数据保存起来
5.函数的参数作为缓存对象的key,把函数计算的结果作为这个key对应的值
- 示例代码 :
<script>
var cache = {};
function f1(str){
//.....
if (cache[str] != undefined)
{
console.log("已经存在缓存数据,直接返回");
return cache[str];
}
//....如果缓存中有数据,那么函数体后面的代码就不再执行(节省时间)
//执行耗时操作...
var result = str + " hello world!";
cache[str] = result;
console.log("第一次调用函数传入参数,返回结果");
return result;
}
console.log(f1("demo")); //
console.log(f1("demo")); //
console.log(f1("demo"));
</script>
<script>
function f1(str){
//.....
if (f1.cache[str] != undefined)
{
console.log("已经存在缓存数据,直接返回");
return f1.cache[str];
}
//....如果缓存中有数据,那么函数体后面的代码就不再执行(节省时间)
//执行耗时操作...
var result = str + " hello world!";
f1.cache[str] = result;
console.log("第一次调用函数传入参数,返回结果");
return result;
}
f1.cache = {};
console.log(f1("test")); //
console.log(f1("test")); //
console.log(f1("test"));
</script>
命名空间模式:
写法:就是把所有的东西都写在一个对象里面.
命名:命名空间的名称一般是项目的名称或者是简写,要求所有的字符都大写
- 示例代码 :
<script>
//01 普通的变量
var a = "a";
var b = "b";
//02 对象
var obj = {
name:"xxx"
}
function func (){
console.log("func");
}
function Person(){
this.name = "默认的名称";
}
function func (){
console.log("func");
}
</script>
<script>
var MOMO = {};
//01 普通的变量
MOMO.a = "a";
MOMO.b = "b";
//02 对象
MOMO.obj = {
name:"zahngsan"
}
MOMO.func = function(){
console.log("func");
}
MOMO.Person = function(){
this.name = "默认的名称";
}
console.log(new MOMO.Person());
</script>
<script>
// var MOMO ={};
// MOMO.obj = {
// name:"张三"
// };
//
// MOMO.obj = "demodede";
//在使用或者是对属性进行赋值之前,会先做一个检查(检查改属性或者是方法是否存在)
//var MOMO = {}; 不推荐
//02 更安全的处理方式:麻烦
// if (MOMO == undefined)
// {
// var MOMO = {};
// }
var MOMO = MOMO || {}; //逻辑或 如果MOMO为真那么返回MOMO,否则就返回{}
//if (MOMO.name == undefined)
if ( typeof MOMO.name == 'undefined')
{
MOMO.name = "测试的名称";
}
if ( typeof MOMO.age == 'undefined')
{
MOMO.age = 20;
}
//100属性
</script>
通用的命名空间函数
在命名空间上面提供一个方法(nameSpace)
- 得到调用函数传入的字符串
- 把字符串转换为字符串数组
- 把MOMO最外层去掉(删除)
- 遍历
- 示例代码 :
<script>
var MOMO = MOMO || {};
MOMO.namespace = function(stringParam){
var str = stringParam;
var parts = str.split("."); //根据字符串切割字符串变成一个数组
var parent = MOMO;
console.log(parts);
if(parts[0] == "MOMO")
{
parts = parts.slice(1) //作用:删除第一个元素返回一个新的数组
}
for (var i = 0; i < parts.length; i++) {
//MOMO.name
//name.des
//des.abc
if (parent[parts[i]] == undefined)
{
parent[parts[i]] = {};
}
//更新父节点
parent = parent[parts[i]];
}
}
MOMO.namespace("MOMO.name.des.abc");
console.log(MOMO);
MOMO.namespace("MOMO.a.b.c.d.e.f.d.g.h.j.k.s.d.g.h.j.a.s.d.f.we.r");
console.log(MOMO);
MOMO.namespace("abc.des.abc");
console.log(MOMO);
</script>
网友评论