设计模式的定义是:
在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案
通俗一点说,设计模式是在某种场合下对某个问题的一种解决方案。如果再通俗一点说,设 计模式就是给面向对象软件开发中的一些好的设计取个名字。
设计模式不是高深技术,奇技淫巧,只是一种设计思想,针对不同的业务场景,最本质的目的是解耦,为了可扩展性和健壮性。
学习模式的作用
在软件设计中,模式是一些经过了大量实际项目验证的优秀解决方案。熟悉这些模式 的程序员,对某些模式的理解也许形成了条件反射。当合适的场景出现时,他们可以很快地找到 某种模式作为解决方案。
1.0 构造器模式
假设我们要存储一个公司的员工信息
let people1 = {
name:'小明',
age:'18'
}
let people2 = {
name:'小红',
age:'19'
}
这才两个人,两个属性,假设有30个属性,300人呢,我们总不能写300个person
对象吧 代码太冗余了
我们创建一个构造函数
function Person(name,age){
this.name = name;
this.age = age;
}
let person1 = new Person('小明',18)
let person2 = new Person('小红',19)
有多少人我们就 new一下就行了多舒服,当然了我们可以给这些人加一些属性 和方法
比如加一个性别 再加一下自我介绍
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
this.selfIntroduce = function(){
console.log(`我叫${this.name},年龄${this.age},性别${this.sex}`)
}
}
// 新建一个员工
let person1 = new Person('小明',18,'男')
person1.selfIntroduce() // 我叫小明,年龄18,性别男
这以后再来多少员工我都不怕了
缺点:
- 构造函数里面的方法需要单独开辟内存空间,当实例比较多的时候会造成内存浪费
所以就有了下面的模式
2.0 原型模式
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.selfIntroduce = function(){
console.log(`我叫${this.name},年龄${this.age},性别${this.sex}`)
}
// 新建一个员工
let person1 = new Person('小明',18,'男')
person1.selfIntroduce() // 我叫小明,年龄18,性别男
这样就不会造成内存浪费了
上面这两种呀我们的es6 完美的用class融合了这两种的写法
class Person{
constructor(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
selfIntroduce(){
console.log(`我叫${this.name},年龄${this.age},性别${this.sex}`)
}
}
let person1 = new Person('小明',18,'男')
person1.selfIntroduce() // 我叫小明,年龄18,性别男
demo: 比如我们页面有很多tab选项卡,用原型的模式处理一下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{margin:0;padding: 0}
ul li{list-style: none}
ul{
display: flex;
}
li{
display: inline-block;
width: 100px;
height:50px;
line-height: 50px;
font-size: 20px;
text-align: center;
border:1px solid red;
box-sizing: border-box;
}
.header li{
cursor: pointer;
}
.header .active{
background: palegreen;
}
.box li{
display: none;
}
.box .active{
width: 600px;
height:300px;
display: block;
background: palegoldenrod;
}
</style>
</head>
<body>
<div class="content1">
<ul class="header">
<li class="active">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
<ul class="box">
<li class="active">1111</li>
<li>22222</li>
<li>333333</li>
<li>44444</li>
<li>55555</li>
<li>6666</li>
</ul>
</div>
<div class="content2">
<ul class="header">
<li class="active">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
<ul class="box">
<li class="active">1111</li>
<li>22222</li>
<li>333333</li>
<li>44444</li>
<li>55555</li>
<li>6666</li>
</ul>
</div>
<script>
function Tabs(elm,type){
this.elm = document.querySelector(elm)
this.headerItems = this.elm.querySelectorAll('.header li')
this.boxItems = this.elm.querySelectorAll('.box li')
this.type = type
console.log(this.headerItems,this.boxItems)
this.change()
}
Tabs.prototype.change = function(){
for(let i=0;i<this.headerItems.length;i++){
this.headerItems[i].addEventListener(this.type,()=>{
for(let m=0;m<this.headerItems.length; m++){
this.headerItems[m].classList.remove('active')
this.boxItems[m].classList.remove('active')
}
this.headerItems[i].classList.add('active')
this.boxItems[i].classList.add('active')
})
}
}
new Tabs('.content1','click')
new Tabs('.content2','mouseenter')
</script>
</body>
</html>
3.0 工厂模式
由一个工厂对象决定创建某一种产品对象类的实例,主要用来创建同一类对象
举例说明,比如我们写后台管理的权限功能,不同用户会展示不同的页面
- superAdmin 用户 进入有 home-page,user-page,news-page 三个页面
- admin 用户 进入有 home-page,user-page 两个页面
- editor 用户 进入有 home-page,news-page 两个页面
function User(role,pages){
this.role = role
this.pages = pages
}
// 用户工厂
function UserFactory(role){
switch(role){
case 'superAdmin':
return new User('superAdmin',['home-page','user-page','news-page'])
break;
case 'admin':
return new User('admin',['home-page','user-page'])
break;
case 'editor':
return new User('editor',['home-page','news-page'])
break;
default:
throw Error('你没权限')
}
}
let user = UserFactory('admin')
es6写法
class User{
constructor(role,pages){
this.role = role
this.pages = pages
}
// 变成静态方法
static UserFactory(role){
switch(role){
case 'superAdmin':
return new User('superAdmin',['home-page','user-page','news-page'])
break;
case 'admin':
return new User('admin',['home-page','user-page'])
break;
case 'editor':
return new User('editor',['home-page','news-page'])
break;
default:
throw Error('你没权限')
}
}
}
let user = User.UserFactory('admin')
简单的工厂的优点:
只要正确传入参数,就能获取到你需要的对象,具体里面干了啥不需要知道,
缺点:
创建的对象数量少,对象的创建逻辑不复杂时用,如果复杂就难以维护。
4.0单例模式
单例模式的定义是:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器 中的window对象等。在js开发中,单例模式的用途同样非常广泛。试想一下,当我们单击 登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮, 这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。
用闭包产生一个单一的对象
let singleUser = (function(){
let init;
function User(name,age){
this.name = name;
this.age = age;
}
return function(name,age){
if(!init){
init = new User(name,age)
}
return init
}
})()
let user1 = singleUser('小明',18) // {name: '小明', age: 18}
let user2 = singleUser('小红',19) // {name: '小明', age: 18}
es6 方法写一下
class singleUser{
constructor(name,age) {
if(!singleUser.init){
this.name = name;
this.age = age;
singleUser.init = this
}
return singleUser.init
}
}
let user3 = new singleUser('小小',18 ) // {name: '小小', age: 18}
let user4 = new singleUser('大大',19 ) // {name: '小小', age: 18}
demo 创建一个频繁关闭和打开的弹框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.box{
position: fixed;
width: 200px;
height:200px;
line-height: 200px;
text-align: center;
left:50%;
top:50%;
transform: translate(-50%,-50%);
background: palegoldenrod;
}
</style>
</head>
<body>
<!--<div class="box">对话框</div>-->
<button id="open">打开</button>
<button id="close">关闭</button>
<script>
let singleBox = (function(){
let init = null;
return function(){
if(!init){
init = document.createElement('div')
init.innerHTML = '对话框'
init.classList.add('box')
init.style.display = 'none'
document.body.appendChild(init)
}
return init
}
})()
document.querySelector('#open').onclick = function(){
// 创建 box框
let box = singleBox()
// 显示 box框
box.style.display = 'block'
}
document.querySelector('#close').onclick = function(){
// 创建 box框
let box = singleBox()
// 隐藏 box框
box.style.display = 'none'
}
</script>
</body>
</html>
单例模式主要解决一个全局使用的类频繁的被创建和销毁,占用内存或空间。
5.0 装饰器模式
装饰者(decorator)模式能够在不改变对象自身的基础上,动态的给某个对象添加额外的职责,不会影响原有接口的功能。
JavaScript中的装饰器模式
var Plane = {
fire:function(){
console.log('发射普通的子弹');
}
}
var oneDecorator = function(){
console.log('发射导弹!');
}
var fire = Plane.fire;
Plane.fire = function(){
fire();
oneDecorator()
}
Plane.fire();
使用AOP(面向切面编程)装饰函数
主要是以为在JavaScript中会存在随着函数的调用,this
的指向发生变化,导致执行结果发生变化。
封装before函数
在需要执行的函数之前执行某个新添加的功能函数
//是新添加的函数在旧函数之前执行
Function.prototype.before=function (beforefn) {
var _this= this; //保存旧函数的引用
return function () { //返回包含旧函数和新函数的“代理”函数
beforefn.apply(this,arguments); //执行新函数,且保证this不被劫持,新函数接受的参数
// 也会被原封不动的传入旧函数,新函数在旧函数之前执行
return _this.apply(this,arguments);
};
};
封装 after 函数
在需要执行的函数之后执行某个新添加的功能函数
//新添加的函数在旧函数之后执行
Function.prototype.after=function (afterfn) {
var _this=this;
return function () {
var ret=_this.apply(this,arguments);
afterfn.apply(this,arguments);
return ret;
};
};
表单验证
Function.prototype.before=function (beforefn) {
var _this= this; //保存旧函数的引用
return function () { //返回包含旧函数和新函数的“代理”函数
beforefn.apply(this,arguments); //执行新函数,且保证this不被劫持,新函数接受的参数
// 也会被原封不动的传入旧函数,新函数在旧函数之前执行
return _this.apply(this,arguments);
};
};
var validata=function () {
if(username.value===''){
alert('用户名不能为空!')
return false;
}
if(password.value===''){
alert('密码不能为空!')
return false;
}
}
var formSubmit=function () {
var param={
username=username.value;
password=password.value;
}
ajax('post','http://www.xxx.com',param);
}
formSubmit= formSubmit.before(validata);
submitBtn.onclick=function () {
formSubmit();
}
复杂一点的
// 装饰者模式 decorate(装饰)
var tree = {};
tree.decorate = function(){
console.log('别让树倒了')
}
// Decorator(装饰器) 定义方法,用于添加额外的装饰器,
// 装饰器被实现为构造器函数 都继承自tree对象
tree.getDecorator = function(deco){
tree[deco].prototype = this
return new tree[deco];
}
// 创建装饰器 redBalls() ,我们将他设为tree的一个属性(已保持
//全局命名空间的纯净) ,redBall 对象也提供了decorate()方法,
// 注意它先调用了父类的decorate()方法
tree.RedBalls = function(){
this.decorate = function (){
this.RedBalls.prototype.decorate();
console.log('放一些红球')
}
}
tree.BlueBalls = function(){
this.decorate = function (){
this.BlueBalls.prototype.decorate();
console.log('放一些蓝球1')
}
}
tree.caidai = function(){
this.decorate = function (){
this.caidai.prototype.decorate();
console.log('放一些彩带')
}
}
// 再把所有的装饰器都添加到基础对象中
tree = tree.getDecorator('BlueBalls');
tree = tree.getDecorator('RedBalls');
tree = tree.getDecorator('caidai');
tree.decorate();
// 返回的顺序是这个哦
/*别让树倒了
放一些蓝球1
放一些红球
放一些彩带*/
永远不要把希望寄托在任何人身上,爱情也好,友情也罢,你没有前途,爱情就没有未来,你不优秀,认识谁都没用,不要做一个讨好别人的人,而要做一个对别人有价值的人