const a = new Singleton(‘sven1’);
const b = new Singleton(‘sven2’);
console.log(a === b); // true
复制代码
为了把 instance 封装起来,我们使用了自执行的匿名函数和闭包,并且让这个匿名函数返回 真正的 Singleton 构造方法,这增加了一些程序的复杂度,阅读起来也不是很舒服。
使用代理实现单例模式
/**
- 使用代理实现单例模式
*/
const Singleton = (function (){
class SingletonOrigin {
constructor(name) {
this.name = name;
}
getName(){
console.log(this.name);
}
}
let instance;
return function(name) {
if (!instance){
instance = new SingletonOrigin(name);
}
return instance;
}
})();
const a = new Singleton(‘sven1’);
const b = new Singleton(‘sven2’);
console.log(a === b); // true
惰性单例模式
前面几种实现方式是基于面向对象思路的实现,现在使用js特殊的方式实现单例模式,名为惰性单例模式
。
惰性单例模式可以推迟创建对象的时机,并不在一开始就创建,所以叫惰性
。
/**
- 惰性单例模式
*/
const getInstance = (function () {
function createInstance(name) {
return {
name: name,
getName() {
console.log(this.name);
}
}
}
function getSingle (fn){
let result;
return function() {
if (!result) {
result = fn.apply(this, arguments)
}
return result;
}
};
return getSingle(createInstance);
})();
const a = getInstance(‘sven1’);
const b = getInstance(‘sven2’);
console.log(a === b); // true
发布订阅模式(又叫观察者模式Observer Pattern)
==========================================================================================
实现发布订阅模式
发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。在JavaScript开发中,一般用事件模型来替代传统的发布—订阅模式。
-
dom的
click
、focus
等事件 -
在Vue中的任意组件件通信事件总线
EventBus
-
含有订阅功能的系统中,如新闻app中订阅了报纸新闻;
简单版
/**
- 发布订阅模式
*/
class PublishSubscribePattern {
constructor() {
// 消息映射
this.msgMap = {};
}
// 发布
publish(name, param) {
const msg = this.msgMap[name];
if (msg) {
msg.subscribes.forEach(subscribe => {
subscribe.callback(param);
});
} else {
console.log(‘无人订阅此消息:’, name, param);
}
}
// 订阅
subscribe(name, callback) {
const msg = this.msgMap[name];
if (msg) {
msg.subscribes.push({callback});
} else {
this.msgMap[name] = {
name,
subscribes: [{callback}]
}
}
}
}
- 使用
const event = new PublishSubscribePattern();
event.publish(‘news’, ‘this is news 1’);
event.subscribe(‘news’, (param) => {
console.log(‘get news:’, param);
});
event.publish(‘news’, ‘this is news 2’);
带发布者版本
订阅者希望只订阅某一个发布者发布的消息
- 源码
/**
- 发布订阅模式
*/
class PublishSubscribePattern {
constructor() {
// 消息映射
this.msgMap = {};
}
// 发布
publish({name, param, publisher}) {
const msg = this.msgMap[name];
if (msg) {
if (!publisher) {
throw new Error(‘未注册发布人:’ + name);
} else if (publisher === ‘all’) {
msg.subscribes.forEach(e => e.callback(param));
} else {
let beAccept = false;
msg.subscribes.forEach(e => {
if (e.publisher === publisher) {
beAccept = true;
e.callback(param);
}
});
if (!beAccept) {
console.log(‘无人订阅你的消息:’, name, param);
}
}
} else {
console.log(‘无人订阅此消息:’, name, param);
}
}
// 订阅
subscribe({name, publisher}, callback) {
const msg = this.msgMap[name];
if (msg) {
msg.subscribes.push({
publisher,
callback
});
} else {
this.msgMap[name] = {
name,
subscribes: [{
publisher,
callback
}]
}
}
}
}
- 使用
const event = new PublishSubscribePattern();
event.publish({name: ‘news’, param: ‘this is news 1’, publisher: ‘weather’});
event.subscribe({name: ‘news’, publisher: ‘weather’}, (param) => {
console.log(get news from weather:
, param);
});
event.publish({name: ‘news’, param: ‘this is news 2’, publisher: ‘weather’});
event.publish({name: ‘news’, param: ‘this is news of newspaper’, publisher: ‘newspaper’});
/*
无人订阅此消息: news this is news 1
get news from weather: this is news 2
无人订阅你的消息: news this is news of newspaper
*/
复制代码
复杂版主要实现以下功能:
-
取消订阅功能
-
订阅者希望只订阅某一个发布者发布的消息
-
订阅者希望获取到在订阅之前发布的历史消息
-
订阅者希望查看自己订阅了哪些消息
-
发布者希望查看自己发布过哪些消息
-
代码
/**
- 发布订阅模式
*/
class Event {
constructor() {
// 发布者映射
this.publisherMap = {};
// 订阅者映射
this.subscriberMap = {};
}
// 注册
registration(username, type = ‘all’) {
if (type === ‘publisher’) {
this.publisherMap[username] = {
publisher: username,
msgMap: {}
};
return this.publisherMap[username];
} else if (type === ‘subscriber’) {
this.subscriberMap[username] = {
subscriber: username,
msgMap: {}
};
return this.subscriberMap[username];
} else if (type === ‘all’) {
this.publisherMap[username] = {
publisher: username,
msgMap: {}
};
this.subscriberMap[username] = {
subscriber: username,
msgMap: {}
};
}
}
// 发布
publish({name, param, publisher}) {
const publisherObj = this.publisherMap[publisher];
if (!publisherObj) {
throw new Error(‘未注册发布人:’ + name);
} else {
const historyRecord = {
name,
param,
publisher,
time: Date.now()
};
const msg = publisherObj.msgMap[name];
if (msg) {
let beAccept = false;
msg.subscribes.forEach(e => {
if (e.publisher === publisher) {
beAccept = true;
e.callback(param, {name, param, publisher});
console.log(e.subscriber, ‘收到了’, e.publisher, ‘发布的消息’, name, param);
}
});
if (!beAccept) {
console.log(‘无人订阅你的消息:’, name, param);
}
msg.history.push(historyRecord);
} else {
publisherObj.msgMap[name] = {
name,
publisher: publisher,
subscribes: [],
history: [historyRecord]
};
console.log(‘发布者’, publisher, ‘注册消息:’, name, param);
}
}
}
// 订阅
subscribe({name, publisher, subscriber, receiveHistoryMsg}, callback) {
const publisherObj = this.publisherMap[publisher];
if (subscriber) {
const subscriberObj = this.subscriberMap[subscriber];
if (subscriberObj) {
subscriberObj.msgMap[name] = {
name,
publisher,
subscriber: subscriber,
callback,
time: Date.now()
};
}
}
if (publisherObj) {
const msg = publisherObj.msgMap[name];
if (msg) {
msg.subscribes.push({
publisher,
subscriber,
callback
});
console.log(subscriber || ‘游客’, ‘订阅了’, publisher, ‘的消息:’, name);
if (receiveHistoryMsg === true) {
msg.history.forEach(e => callback(e.param, e));
}
} else {
console.log(‘发布者’, publisher, ‘未注册过此消息:’, name);
}
} else {
console.log(‘发布者未注册:’, publisher);
}
}
// 取消订阅
unsubscribe({name, publisher, subscriber}) {
const publisherObj = this.publisherMap[publisher];
if (subscriber) {
const subscriberObj = this.subscriberMap[subscriber];
if (subscriberObj) {
delete subscriberObj.msgMap[name];
}
}
if (publisherObj) {
const msg = publisherObj.msgMap[name];
if (msg) {
msg.subscribes = msg.subscribes.filter(e => !(e.publisher === publisher && msg.name === name));
} else {
console.log(‘发布者’, publisher, ‘未注册过此消息:’, name);
}
} else {
console.log(‘发布者未注册:’, publisher);
}
}
// 获取发布历史消息
getPublishHistory(publisher, name) {
return this.publisherMap[publisher].msgMap[name].history;
}
getSubscribeMsg(subscriber) {
return this.subscriberMap[subscriber].msgMap;
}
}
- 使用
// 直接使用Event实现发布订阅功能
const event = new Event();
const publisher = ‘A’;
const subscriber = ‘B’;
event.registration(publisher);
event.registration(subscriber);
const name = ‘news’;
const param = ‘一条消息a’;
event.publish({name, publisher, param});
event.subscribe({name, publisher, subscriber, receiveHistoryMsg: true}, (param, e) => {
console.log(---- 接收消息from:
, param, e);
});
event.publish({name, publisher, param: ‘一条消息b’});
console.log(‘订阅的消息’, event.getSubscribeMsg(subscriber));
event.unsubscribe({name, publisher, subscriber});
event.publish({name, publisher, param: ‘一条消息c’});
console.log(‘发布历史’, event.getPublishHistory(publisher, name));
/*
发布者 A 注册消息: news 一条消息a
B 订阅了 A 的消息: news
---- 接收消息from: 一条消息a { name: ‘news’,
param: ‘一条消息a’,
publisher: ‘A’,
time: 1603011782573 }
---- 接收消息from: 一条消息b { name: ‘news’, param: ‘一条消息b’, publisher: ‘A’ }
B 收到了 A 发布的消息 news 一条消息b
订阅的消息 { news:
{ name: ‘news’,
publisher: ‘A’,
subscriber: ‘B’,
callback: [Function],
time: 1603011782575 } }
无人订阅你的消息: news 一条消息c
发布历史 [ { name: ‘news’,
param: ‘一条消息a’,
publisher: ‘A’,
time: 1603011782573 },
{ name: ‘news’,
param: ‘一条消息b’,
publisher: ‘A’,
time: 1603011782577 },
{ name: ‘news’,
param: ‘一条消息c’,
publisher: ‘A’,
time: 1603011782578 } ]
*/
使用适配器类
- 代码
/**
- 代理类,屏蔽重复设置发布者、订阅者
*/
class Factory {
constructor(username, type) {
this.username = username;
this.type = type;
this._event = new Event();
this._event.registration(username, type || ‘all’);
}
// 发布
publish(param) {
return this._event.publish(Object.assign({}, param, {publisher: this.username}))
}
// 订阅
subscribe(param, callback) {
return this._event.subscribe(Object.assign({}, param, {subscriber: this.username}), callback);
}
// 取消订阅
unsubscribe(param) {
return this._event.unsubscribe(Object.assign({}, param, {subscriber: this.username}));
}
// 获取历史发布消息
getPublishHistory(name) {
return this._event.getPublishHistory(this.username, name);
}
// 获取订阅的消息列表
getSubscribeMsg() {
return this._event.getSubscribeMsg(this.username);
}
}
- 使用
// 使用适配器封装
const publisherA = ‘A’;
const subscriberB = ‘B’;
const publisher = new Factory(publisherA, ‘publisher’);
const subscriber = new Factory(subscriberB, ‘subscriber’);
const name = ‘新闻’;
publisher.publish({name, param: ‘this is news 1’});
subscriber.subscribe({name, publisher: publisherA, receiveHistoryMsg: true}, (param) => {
console.log(---- get news from ${publisherA}:
, param);
});
console.log(‘订阅的消息’, subscriber.getSubscribeMsg());
publisher.publish({name, param: ‘this is news 2’});
publisher.publish({name, param: ‘this is news of newspaper’});
console.log(‘发布历史’, publisher.getPublishHistory(name));
/*
发布者 A 注册消息: 新闻 this is news 1
发布者未注册: A
订阅的消息 { ‘新闻’:
{ name: ‘新闻’,
publisher: ‘A’,
subscriber: ‘B’,
callback: [Function],
time: 1603012329816 } }
无人订阅你的消息: 新闻 this is news 2
无人订阅你的消息: 新闻 this is news of newspaper
发布历史 [ { name: ‘新闻’,
param: ‘this is news 1’,
publisher: ‘A’,
time: 1603012329813 },
{ name: ‘新闻’,
param: ‘this is news 2’,
publisher: ‘A’,
time: 1603012329819 },
{ name: ‘新闻’,
param: ‘this is news of newspaper’,
publisher: ‘A’,
time: 1603012329819 } ]
*/
==================================================================================
中介者模式
的作用就是解除对象与对象之间的紧耦合关系。
增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知 中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系。
中介者模式是迎合最少知识原则
(迪米特法则)的一种实现。是指一个对象应 该尽可能少地了解另外的对象(类似不和陌生人说话)。
用一个小游戏说明中介者模式的用处。
**游戏规则:**两组选手进行对战,其中一个玩家死亡的时候游戏便结束, 同时通知它的对手胜利。
普通实现
var players = [];
//接着我们再来编写Hero这个函数;代码如下:
var players = []; // 定义一个数组 保存所有的玩家
function Hero(name,teamColor) {
this.friends = []; //保存队友列表
this.enemies = []; // 保存敌人列表
this.state = ‘live’; // 玩家状态
this.name = name; // 角色名字
this.teamColor = teamColor; // 队伍的颜色
}
Hero.prototype.win = function(){
console.log(“win:” + this.name);
};
Hero.prototype.lose = function(){
console.log(“lose:” + this.name);
};
Hero.prototype.die = function(){
// 所有队友死亡情况 默认都是活着的
var all_dead = true;
this.state = ‘dead’; // 设置玩家状态为死亡
for(var i = 0,ilen = this.friends.length; i < ilen; i+=1) {
// 遍历,如果还有一个队友没有死亡的话,则游戏还未结束
if(this.friends[i].state !== ‘dead’) {
all_dead = false;
break;
}
}
if(all_dead) {
this.lose(); // 队友全部死亡,游戏结束
// 循环 通知所有的玩家 游戏失败
for(var j = 0,jlen = this.friends.length; j < jlen; j+=1) {
this.friends[j].lose();
}
// 通知所有敌人游戏胜利
for(var j = 0,jlen = this.enemies.length; j < jlen; j+=1) {
this.enemies[j].win();
}
}
}
// 定义一个工厂类来创建玩家
var heroFactory = function(name,teamColor) {
var newPlayer = new Hero(name,teamColor);
for(var i = 0,ilen = players.length; i < ilen; i+=1) {
// 如果是同一队的玩家
if(players[i].teamColor === newPlayer.teamColor) {
// 相互添加队友列表
players[i].friends.push(newPlayer);
newPlayer.friends.push(players[i]);
}else {
// 相互添加到敌人列表
players[i].enemies.push(newPlayer);
newPlayer.enemies.push(players[i]);
}
}
players.push(newPlayer);
return newPlayer;
};
// 红队
var p1 = heroFactory(“aa”,‘red’),
p2 = heroFactory(“bb”,‘red’),
p3 = heroFactory(“cc”,‘red’),
p4 = heroFactory(“dd”,‘red’);
// 蓝队
var p5 = heroFactory(“ee”,‘blue’),
p6 = heroFactory(“ff”,‘blue’),
p7 = heroFactory(“gg”,‘blue’),
p8 = heroFactory(“hh”,‘blue’);
// 让红队玩家全部死亡
p1.die();
p2.die();
p3.die();
p4.die();
// lose:dd lose:aa lose:bb lose:cc
// win:ee win:ff win:gg win:hh
复制代码
中介者模式实现
玩家与玩家之间的耦合代码解除,把所有的逻辑操作放在中介者对象里面进去处理,某个玩家的任何操作不需要去遍历去通知其他玩家,而只是需要给中介者发送一个消息即可,中介者接受到该消息后进行处理,处理完消息之后会把处理结果反馈给其他的玩家对象。
var players = []; // 定义一个数组 保存所有的玩家
function Hero(name,teamColor) {
this.state = ‘live’; // 玩家状态
this.name = name; // 角色名字
this.teamColor = teamColor; // 队伍的颜色
}
Hero.prototype.win = function(){
// 赢了
console.log(“win:” + this.name);
};
Hero.prototype.lose = function(){
// 输了
console.log(“lose:” + this.name);
};
// 死亡
Hero.prototype.die = function(){
this.state = ‘dead’;
// 给中介者发送消息,玩家死亡
playerDirector.ReceiveMessage(‘playerDead’,this);
}
// 移除玩家
Hero.prototype.remove = function(){
// 给中介者发送一个消息,移除一个玩家
playerDirector.ReceiveMessage(‘removePlayer’,this);
};
// 玩家换队
Hero.prototype.changeTeam = function(color) {
// 给中介者发送一个消息,玩家换队
playerDirector.ReceiveMessage(‘changeTeam’,this,color);
};
// 定义一个工厂类来创建玩家
var heroFactory = function(name,teamColor) {
// 创建一个新的玩家对象
var newHero = new Hero(name,teamColor);
// 给中介者发送消息,新增玩家
playerDirector.ReceiveMessage(‘addPlayer’,newHero);
return newHero;
};
var playerDirector = (function(){
var players = {}, // 保存所有的玩家
operations = {}; // 中介者可以执行的操作
// 新增一个玩家操作
operations.addPlayer = function(player) {
// 获取玩家队友的颜色
var teamColor = player.teamColor;
// 如果该颜色的玩家还没有队伍的话,则新成立一个队伍
players[teamColor] = players[teamColor] || [];
// 添加玩家进队伍
players[teamColor].push(player);
};
// 移除一个玩家
operations.removePlayer = function(player){
// 获取队伍的颜色
var teamColor = player.teamColor,
// 获取该队伍的所有成员
teamPlayers = players[teamColor] || [];
// 遍历
for(var i = teamPlayers.length - 1; i>=0; i–) {
if(teamPlayers[i] === player) {
teamPlayers.splice(i,1);
}
}
};
// 玩家换队
operations.changeTeam = function(player,newTeamColor){
// 首先从原队伍中删除
operations.removePlayer(player);
// 然后改变队伍的颜色
player.teamColor = newTeamColor;
// 增加到队伍中
operations.addPlayer(player);
};
// 玩家死亡
operations.playerDead = function(player) {
var teamColor = player.teamColor,
// 玩家所在的队伍
teamPlayers = players[teamColor];
var all_dead = true;
//遍历
for(var i = 0,player; player = teamPlayers[i++]; ) {
if(player.state !== ‘dead’) {
all_dead = false;
break;
}
}
// 如果all_dead 为true的话 说明全部死亡
if(all_dead) {
for(var i = 0, player; player = teamPlayers[i++]; ) {
// 本队所有玩家lose
player.lose();
}
for(var color in players) {
if(color !== teamColor) {
// 说明这是另外一组队伍
// 获取该队伍的玩家
var teamPlayers = players[color];
for(var i = 0,player; player = teamPlayers[i++]; ) {
player.win(); // 遍历通知其他玩家win了
}
}
}
}
};
var ReceiveMessage = function(){
// arguments的第一个参数为消息名称 获取第一个参数
var message = Array.prototype.shift.call(arguments);
operations[message].apply(this,arguments);
};
return {
ReceiveMessage : ReceiveMessage
};
})();
// 红队
var p1 = heroFactory(“aa”,‘red’),
p2 = heroFactory(“bb”,‘red’),
p3 = heroFactory(“cc”,‘red’),
p4 = heroFactory(“dd”,‘red’);
// 蓝队
var p5 = heroFactory(“ee”,‘blue’),
p6 = heroFactory(“ff”,‘blue’),
p7 = heroFactory(“gg”,‘blue’),
p8 = heroFactory(“hh”,‘blue’);
// 让红队玩家全部死亡
p1.die();
p2.die();
p3.die();
p4.die();
// lose:aa lose:bb lose:cc lose:dd
// win:ee win:ff win:gg win:hh
复制代码
=================================================================================
要实现某一个功能有多种方案可以选择,定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换,这就是策略模式。
-
表单校验:
执行校验规则
和校验规则配置
分开; -
前端动画类:
将渲染动画
、动画配置
以及动画控制
分开
策略模式演示:表单校验
// 校验方法&规则配置
var strategies = {
isNonEmpty: function( value, errorMsg ){ // 不为空
if ( value === ‘’ ){
return errorMsg ;
}
},
minLength: function( value, length, errorMsg ){ // 限制最小长度
if ( value.length < length ){
return errorMsg;
}
},
isMobile: function( value, errorMsg ){ // 手机号码格式
if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){
return errorMsg;
}
}
};
// 校验执行器
var Validator = function(){
this.cache = []; // 保存校验规则
};
Validator.prototype.add = function( dom, rule, errorMsg ){
var ary = rule.split( ‘:’ ); // 把strategy 和参数分开
this.cache.push(function(){ // 把校验的步骤用空函数包装起来,并且放入cache
var strategy = ary.shift(); // 用户挑选的strategy
ary.unshift( dom.value ); // 把input 的value 添加进参数列表
ary.push( errorMsg ); // 把errorMsg 添加进参数列表
return strategies[ strategy ].apply( dom, ary );
});
};
Validator.prototype.start = function(){
for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){
var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息
if ( msg ){ // 如果有确切的返回值,说明校验没有通过
return msg;
}
}
};
// 控制器
var validataFunc = function(){
var validator = new Validator(); // 创建一个validator 对象
/添加一些校验规则*/
validator.add( registerForm.userName, ‘isNonEmpty’, ‘用户名不能为空’ );
validator.add( registerForm.password, ‘minLength:6’, ‘密码长度不能少于6 位’ );
validator.add( registerForm.phoneNumber, ‘isMobile’, ‘手机号码格式不正确’ );
var errorMsg = validator.start(); // 获得校验结果
return errorMsg; // 返回校验结果
}
// 程序入口
var registerForm = document.getElementById( ‘registerForm’ );
registerForm.onsubmit = function(){
var errorMsg = validataFunc(); // 如果errorMsg 有确切的返回值,说明未通过校验
if ( errorMsg ){
alert ( errorMsg );
return false; // 阻止表单提交
}
};
复制代码
策略模式是一种常用且有效的设计模式,总结一下策略模式的一些优点:
-
策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
-
策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于扩展。
-
策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
-
在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻便的替代方案。
策略模式也有一些缺点,但这些缺点并不严重:
-
使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的 逻辑堆砌在 Context 中要好。
-
要使用策略模式,必须了解所有的 strategy,必须了解各个 strategy 之间的不同点, 这样才能选择一个合适的 strategy。比如,我们要选择一种合适的旅游出行路线,必须先了解选 择飞机、火车、自行车等方案的细节。此时 strategy 要向客户暴露它的所有实现,这是违反最少 知识原则的。
不仅是算法,业务规则指向的目标一致,并且可以被替换使用,就也可以用策略模式来封装它们。
“在函数作为一等对象的语言中,策略模式是隐形的。 strategy 就是值为函数的变量。”
相对传统面向对象语言的方式实现策略模式,使用 JavaScript 语言的策略模式,策略类往往被函数所代替,这时策略模式就 成为一种“隐形”的模式。
==============================================================================
为其他对象提供一种代理以控制对这个对象的访问。
常用的代理模式变种有以下几种:
-
保护代理
-
虚拟代理
-
缓存代理
保护代理用于控制不同权限的对象对目标对象的访问
- 举例
class Car {
drive() {
return “driving”;
};
}
class CarProxy {
constructor(driver) {
this.driver = driver;
}
drive() {
// 保护代理,仅18岁才能开车
return (this.driver.age < 18) ? “too young to drive” : new Car().drive();
};
}
复制代码
虚拟代理可应用于:
图片懒加载
、惰性加载
、合并http请求
等
- 举例:图片懒加载
复制代码
缓存代理可应用于:
缓存ajax异步请求数据
、计算乘积
等
- 举例:缓存ajax请求数据
const getData = (function() {
const cache = {};
return function(url) {
if (cache[url]) {
return Promise.resolve(cache[url]);
}
return $.ajax.get(url).then((res) => {
cache[url] = res;
return res;
}).catch(err => console.error(err))
}
})();
getData(‘/getData’); // 发起http请求
getData(‘/getData’); // 返回缓存数据
复制代码
代理模式包括许多小分类,在 JavaScript 开发中最常用的是虚拟代理和缓存代理。虽然代理模式非常有用,但我们在编写业务代码的时候,往往不需要去预先猜测是否需要使用代理模式。当真正发现不方便直接访问某个对象的时候,再编写代理也不迟。
==================================================================================
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象 5 的内部表示。
迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
例如 Javascript中的forEach
、map
、some
等;
迭代器可分为一下两种:
-
内部迭代器
-
外部迭代器
内部已经定义好了迭代规则,它完全接手整个迭代过程,外部只需要一次初始调用。
- 例如:
function each(ary, callback) {
for (let i = 0; i < ary.length; i++){
callback.call(ary[i], i, ary[i]);
}
}
each([1, 2, 3], (i, n) => alert([i, n]));
复制代码
总结: 内部迭代器调用方式简答,但它的适用面相对较窄。
必须显式地请求迭代下一个元素。
外部迭代器增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,我们可以手工控制迭代的过程或者顺序。
var Iterator = function( obj ){
var current = 0;
var next = function(){
current += 1;
};
var isDone = function(){
return current >= obj.length;
};
var getCurrItem = function(){
return obj[ current ];
};
return {
next: next,
isDone: isDone,
getCurrItem: getCurrItem
}
};
复制代码
总结: 外部迭代器虽然调用方式相对复杂,但它的适用面更广,也能满足更多变的需求。内部迭代器和外部迭代器在实际生产中没有优劣之分,究竟使用哪个要根据需求场景而定。
迭代器模式不仅可以迭代数组,还可以迭代一些类数组的对象。
无论是内部迭代器还是外部迭代器,只要被迭代的聚合对象拥有 length 属性而且可以用下标访问,那它就可以被迭代。
例如:arguments
、 {'0': 'a', '1': 'b'}
// 迭代器支持类数组和对象的遍历
function each(obj, callback) {
var value, i = 0, length = obj.length, isArray = isArraylike( obj );
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
} else {
// 迭代object 对象
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
}
return obj;
};
复制代码
从尾到头
的遍历数组。
在遍历过程中,如果满足某种条件可以终止迭代。
function each(ary, callback){
for (let i = 0; i < ary.length; i++){
// callback 的执行结果返回false,提前终止迭代
if (callback(i, ary[i]) === false ){
break;
}
}
};
复制代码
-
nodejs的
express
框架的中间件思想,处理请求用到的next()
方法就是迭代器模式。 -
ECMAScript 6 的
Iterator
(遍历器)和Generator
异步编程中使用了next()
=================================================================================
适配器模式的作用是解决两个接口/方法间的接口不兼容的问题。
作为两个不兼容的接口之间的桥梁,就是新增一个包装类,对新的接口进行包装以适应旧代码的调用,避免修改接口和调用代码。
// 1. 方法适配 *******************************
const A = {
show() {
console.log(‘visible’);
}
}
const B = {
display() {
console.log(‘visible’);
}
}
// 不使用适配器
A.show();
B.display();
// 使用适配器
const C = {
show() {
B.display();
}
}
A.show();
C.show();
// 2. 接口适配 *******************************
const data1 = {name: ‘alan’};
const data2 = {username: ‘tom’};
function sayName(param) {
console.log(param.name);
}
function adapter(param) {
return {name: param.username}
}
sayName(data1);
sayName(adapter(data2));
复制代码
有一些模式跟适配器模式的 结构非常相似,比如装饰者模式、代理模式和外观模式。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
![](https://img-blog.csdnimg.cn/img_convert/57d92bb617068ecef482bfb5869afee5.jpeg)
结尾
学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。
在遍历过程中,如果满足某种条件可以终止迭代。
function each(ary, callback){
for (let i = 0; i < ary.length; i++){
// callback 的执行结果返回false,提前终止迭代
if (callback(i, ary[i]) === false ){
break;
}
}
};
复制代码
-
nodejs的
express
框架的中间件思想,处理请求用到的next()
方法就是迭代器模式。 -
ECMAScript 6 的
Iterator
(遍历器)和Generator
异步编程中使用了next()
=================================================================================
适配器模式的作用是解决两个接口/方法间的接口不兼容的问题。
作为两个不兼容的接口之间的桥梁,就是新增一个包装类,对新的接口进行包装以适应旧代码的调用,避免修改接口和调用代码。
// 1. 方法适配 *******************************
const A = {
show() {
console.log(‘visible’);
}
}
const B = {
display() {
console.log(‘visible’);
}
}
// 不使用适配器
A.show();
B.display();
// 使用适配器
const C = {
show() {
B.display();
}
}
A.show();
C.show();
// 2. 接口适配 *******************************
const data1 = {name: ‘alan’};
const data2 = {username: ‘tom’};
function sayName(param) {
console.log(param.name);
}
function adapter(param) {
return {name: param.username}
}
sayName(data1);
sayName(adapter(data2));
复制代码
有一些模式跟适配器模式的 结构非常相似,比如装饰者模式、代理模式和外观模式。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-aelUSFwU-1712427225687)]
[外链图片转存中…(img-QxFfA0aU-1712427225687)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-cEqfL6gT-1712427225689)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
![](https://img-blog.csdnimg.cn/img_convert/57d92bb617068ecef482bfb5869afee5.jpeg)
结尾
学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。