闭包生成的三个必要条件:
(1)在函数a内部直接或间接返回一个函数b。
(2)a函数内部使用着b函数的私有变量。
(3)a函数外部有一个变量接收着函数b。
这个不会销毁的a函数的执行空间叫做闭包空间。
把函数a里面返回的函数b叫做函数A的闭包函数。
function a() {
// 这个 num 变量就是函数 a 的私有变量
// var num = 100
return function b() {
console.log(num+1)
}
}
闭包的特点:
(1)延长了变量的生命周期。
(2)利用闭包函数可以访问函数内部的私有变量。
(3)保护私有变量不被外界访问。
闭包的缺点:
当一段内存空间中有一个不会被销毁的东西一直存在,会出现内存占用,如果过多会导致内存溢出。
闭包的作用:
(1)当需要延长变量的声明周期的时候用闭包函数来解决。
(2)需要访问某一个函数内部的私有数据的时候用闭包函数解决。
二、js设计模式
设计模式是针对特定问题给出的简洁而优化的处理方案
常用的设计模式如下:
(1)单例模式
让一个构造函数一辈子只有一个实例对象。
单例模式的核心代码如下:
function Person() {
this.name = 'Jack'
}
var instance = null
function singleton() {
if (instance === null) {
instance = new Person()
}
return instance
}
var p1 = singleton()
(2)组合模式
把若干个启动方式一样的构造函数放在一起,通过总开关启动,这些构造函数都启动。
实现组合模式的三个条件:
需要一个承载所有构造函数实例的数组;
需要一个方法,向数组里面添加内容;
需要一个方法,能把数组里面的所有内容启动了。
应用如下:
// 一个构造函数的启动方式
class Play {
constructor () {}
// 这个构造函数的启动方法
init () {
console.log('开始玩游戏')
}
}
class Eat {
constructor () {}
init () {
console.log('去吃饭')
}
}
class Sleep {
constructor () {}
init () {
console.log('去睡觉')
}
}
// 组合模式的代码
class Compose {
constructor () {
// 用来承载每一个实例的数组
this.composeArr = []
}
// 向数组里面添加内容
add (instance) {
this.composeArr.push(instance)
}
// 把数组里面的每一个内容调用了
init () {
console.log('总开关启动了')
this.composeArr.forEach(item => item.init())
}
}
// c 就是一个总开关
var c = new Compose()
// 每一次执行 add 方法就是向总开关上添加内容
c.add(new Play())
c.add(new Eat())
c.add(new Sleep())
// 只要我的总开关一启动
// 里面的每一个构造函数就同时都启动了
c.init()
console.log(c)
(3)观察者模式
又称发布/订阅模式
让观察者看着被观察者,只要数据改变了,就让观察者做些事情。
从观察者角度:
1. 被观察者
class Student {
constructor () {
// 一个学生本身的状态, 就应该是好好学习
this.state = '好好学习'
// 准备一个数组,观察者放数组里
this.observers = []
}
// 可以改变状态的方法
setState (value) {
this.state = value
// 状态一旦改变, 就要通知观察者 this.notify()
}
// 获取自己的状态
getState () {
return this.state
}
// 添加观察者
attach (observer) {
this.observers.push(observer)
}
// 通知 this.observers 数组里面的每一个人, 状态改变了
notify () {
this.observers.forEach(item => item.qingjiazhang( this.state ))
}
}
// 2. 观察着
class Observer {
// constructor 就相当于 es5 的构造函数体 => 构造器
constructor (name) {
// 用它来标明一下我是班主任还是年级主任还是教务主任
this.name = name
}
// 方法, 就是观察者能触发的技能
qingjiazhang (state) {
console.log(`我是 ${ this.name }, 因为你 ${ state } 了, 我要请你家长来!`)
}
}
var xiaoming = new Student() // 被观察者
var banzhuren = new Observer('班主任') // 观察者
var jiaowuzhuren = new Observer('教务主任') // 观察者
// 让班主任看着小明
xiaoming.attach(banzhuren)
// 让教务主任看着小明
xiaoming.attach(jiaowuzhuren)
// 当小明状态改变的时候, xiaoming.observers 这个数里里面的每一个对象的调用技能
xiaoming.setState('玩手机')
从发布/订阅角度:
class Observer {
constructor () {
// 准备的消息盒子
this.message = {}
}
// 订阅的方法
// type 事件类型, 你要订阅的类型
// fn 事件处理函数, 你要给这个类型订阅一个什么行为
on (type, fn) {
// 向消息盒子里面添加成员
// 判断消息盒子里面有没有成员
if (!this.message[type]) {
// 表示 this.message 里面没有这个成员
this.message[type] = []
}
//没有成员直接把成员push进去
this.message[type].push(fn)
}
// 取消订阅的方法
off (type, fn) {
// 删除消息盒子里面的某一个成员
// 先看看有没有这个事件类型, 如果没有, 不需要取消
// 如果由才需要取消
if (!this.message[type]) return
// 取消这个数组里面和 fn 一样的那个事件处理函数
// 使用一个 filter 方法
this.message[type] = this.message[type].filter(item => item !== fn)
}
// 发布的方法
emit (type) {
// 执行消息盒子里面的某一个对应的处理函数
// 判断你有没有订阅过, 如果没有订阅过, 直接return
if (!this.message[type]) return
// 如果你订阅过, 那么就执行一下
this.message[type].forEach(item => item())
}
}
// 将来使用的时候
var o = new Observer()
// 订阅事件
o.on('click', handlerA)
o.on('click', handlerB)
// 取消订阅事件
// o.off('click', handlerA)
// 发布事件
// 把你订阅好的事件处理函数都给你触发了
console.log(o)
// 准备事件处理函数
function handlerA() { console.log('我是事件处理函数 handlerA') }
function handlerB() { console.log('我是事件处理函数 handlerB') }
“家中逆战,无畏疫情!”