Шаблоны дизайна JavaScript для фронтенд-интервью

JavaScript

введение

Объектно-ориентированное программирование заключается в том, чтобы абстрагировать ваши требования в объект, а затем проанализировать объект и добавить к нему соответствующие функции (атрибуты) и поведения (методы).Мы называем этот объектДобрый. Очень важная особенность объектно-ориентированного пакета, хотяjavascriptЭтот интерпретируемый слабо типизированный язык не ведет себя как некоторые классические строго типизированные языки (например,C++,JAVAи т.д.) Существуют специальные способы реализации инкапсуляции классов, но мы можем использоватьjavascriptГибкие характеристики языка используются для имитации этих функций. И во многих крупныхwebПроект Китай,JavaScriptКода было очень много, нам нужны хорошие шаблоны проектирования, чтобы их изучить.JavaScriptсередина.

шаблон декоратора

Способ динамического добавления обязанностей к объекту называется шаблоном декоратора. В традиционных объектно-ориентированных языках наследование часто используется для добавления функций к объектам, но метод наследования не является гибким, по сравнению с этим шаблон декоратора является более гибким и "платит по мере использования".

Функция декоратора АОП:

Function.prototype.before = function(fn) {
  const self = this
  return function() {
    fn.apply(new(self), arguments)  // https://github.com/MuYunyun/blog/pull/30#event-1817065820
    return self.apply(new(self), arguments)
  }
}

Function.prototype.after = function(fn) {
  const self = this
  return function() {
    self.apply(new(self), arguments)
    return fn.apply(new(self), arguments)
  }
}

Сценарий: проверка формы подключаемого модуля

<html>
<body>
	用户名:<input id="username" type="text"/>

	密码: <input id="password" type="password"/>
	<input id="submitBtn" type="button" value="提交"></button>
</body>
<script>
	var username = document.getElementById( 'username' ),
	password = document.getElementById( 'password' ),
	submitBtn = document.getElementById( 'submitBtn' );
	var formSubmit = function(){
		if ( username.value === '' ){
			return alert ( '用户名不能为空' );
		}
		if ( password.value === '' ){
			return alert ( '密码不能为空' );
		}
		var param = {
			username: username.value,
			password: password.value
		}
		ajax( 'http:// xxx.com/login', param ); // ajax 具体实现略
	}

	submitBtn.onclick = function(){
		formSubmit();
	}


	var validata = function(){
		if ( username.value === '' ){
			alert ( '用户名不能为空' );
			return false;
		}
		if ( password.value === '' ){
			alert ( '密码不能为空' );
			return false;
		}
	}
</script>
</html>

В дополнение к отправке запроса ajax, функция formSubmit выше также проверяет правильность ввода пользователя, что приведет к раздутым функциям и запутанным обязанностям.Мы можем разделить код для проверки ввода и запроса ajax, и мы поместим логику проверки ввода в функции проверки данных. , и согласовано, что когда функция проверки данных возвращает false, это означает, что проверка не удалась

Function.prototype.before = function( beforefn ){
		var __self = this;
		return function(){
			if ( beforefn.apply( this, arguments ) === false ){
	// beforefn 返回false 的情况直接return,不再执行后面的原函数
				return;
			}
			return __self.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( 'http:// xxx.com/login', param );
	}

	formSubmit = formSubmit.before( validata );

	submitBtn.onclick = function(){
		formSubmit();
	}

модель публикации-подписки

Модель публикации-подписки, также известная как модель наблюдателя, определяет однозначное зависимость между объектами. Когда состояние изменений объекта, все объекты, которые зависят от него, будут уведомлены. В JavaScript мы обычно используем модель событий, чтобы заменить традиционную модель публикации-подписки.

Сцена: Уведомление о продаже

var salesOffices = {}; // 定义售楼处
	salesOffices.clientList = []; // 缓存列表,存放订阅者的回调函数

	salesOffices.listen = function( key, fn ){
		if ( !this.clientList[ key ] ){ // 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
			this.clientList[ key ] = [];
		}
		this.clientList[ key ].push( fn ); // 订阅的消息添加进消息缓存列表
	};

	salesOffices.trigger = function(){ // 发布消息
		var key = Array.prototype.shift.call( arguments ), // 取出消息类型
		fns = this.clientList[ key ]; // 取出该消息对应的回调函数集合
		if ( !fns || fns.length === 0 ){ // 如果没有订阅该消息,则返回
			return false;
		}
		for( var i = 0, fn; fn = fns[ i++ ]; ){
			fn.apply( this, arguments ); // (2) // arguments 是发布消息时附送的参数
		}
	};

	salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅88 平方米房子的消息
		console.log( '价格= ' + price ); // 输出: 2000000
	});

	salesOffices.listen( 'squareMeter110', function( price ){ // 小红订阅110 平方米房子的消息
		console.log( '价格= ' + price ); // 输出: 3000000
	});

	salesOffices.trigger( 'squareMeter88', 2000000 ); // 发布88 平方米房子的价格
	salesOffices.trigger( 'squareMeter110', 3000000 ); // 发布110 平方米房子的价格

режим состояния

Ключом к модели состояния является различение внутреннего состояния вещи, а изменение внутреннего состояния вещи часто приводит к изменению поведения вещи.

  • Шаблон состояния определяет связь между состоянием и поведением и инкапсулирует их в класс.
  • Действие запроса в контексте и поведение, инкапсулированное в класс состояния, могут легко изменяться независимо друг от друга, не влияя друг на друга.

Сценарий: Программа освещения

Когда нажата кнопка выключателя света, первая пресса открывается слабый свет, открывается второй световой пресс, третья лампа выключена. Мы говорили о поведении пакета, обычно приоритетной цели пакета, а не состояния объекта. Но только наоборот в режиме состояния, модель критической состояния заключается в каждом состоянии вещей, упакованных в виде одного класса, при этом поведение, связанное с состоянием, связано с этим классом, поэтому кнопка нажата только в этом контексте, чтобы доверить Запрос текущего состояния объекта к государству объекта будет нести ответственность за оказание собственного поведения.

var OffLightState = function( light ){
		this.light = light;
	};

	OffLightState.prototype.buttonWasPressed = function(){
		console.log( '弱光' ); // offLightState 对应的行为
		this.light.setState( this.light.weakLightState ); // 切换状态到weakLightState
	};
// WeakLightState:
var WeakLightState = function( light ){
	this.light = light;
};

WeakLightState.prototype.buttonWasPressed = function(){
		console.log( '强光' ); // weakLightState 对应的行为
		this.light.setState( this.light.strongLightState ); // 切换状态到strongLightState
	};
	// StrongLightState:
	var StrongLightState = function( light ){
		this.light = light;
	};

	StrongLightState.prototype.buttonWasPressed = function(){
		console.log( '关灯' ); // strongLightState 对应的行为
		this.light.setState( this.light.offLightState ); // 切换状态到offLightState
	};

//这个light称为上下文(context)
	var Light = function(){
       //在light的构造函数中,我们要创建每一个状态类的实例对象,context持有这些状态对象的引用,以便将请求委托给状态对象。用户的请求,即点击button的动作也是实现在Context中的
		this.offLightState = new OffLightState( this );
		this.weakLightState = new WeakLightState( this );
		this.strongLightState = new StrongLightState( this );
		this.button = null;
	};


	Light.prototype.init = function(){
		var button = document.createElement( 'button' ),
		self = this;
		this.button = document.body.appendChild( button );
		this.button.innerHTML = '开关';
		this.currState = this.offLightState; // 设置当前状态
		this.button.onclick = function(){
			self.currState.buttonWasPressed();
		}	
	};

	Light.prototype.setState = function( newState ){
		this.currState = newState;
	};

	var light = new Light();
	light.init();

Конечный автомат JavaScript:

var delegate = function( client, delegation ){
		return {
			buttonWasPressed: function(){ // 将客户的操作委托给delegation 对象
				return delegation.buttonWasPressed.apply( client, arguments );
			}
		}
	};

	var FSM = {
		off: {
			buttonWasPressed: function(){
				console.log( '关灯' );
				this.button.innerHTML = '下一次按我是开灯';
				this.currState = this.onState;
			}
		},
		on: {
			buttonWasPressed: function(){
				console.log( '开灯' );
				this.button.innerHTML = '下一次按我是关灯';
				this.currState = this.offState;
			}
		}
	};

	var Light = function(){
		this.offState = delegate( this, FSM.off );
		this.onState = delegate( this, FSM.on );
		this.currState = this.offState; // 设置初始状态为关闭状态
		this.button = null;
	};

	Light.prototype.init = function(){
		var button = document.createElement( 'button' ),
		self = this;
		button.innerHTML = '已关灯';
		this.button = document.body.appendChild( button );
		this.button.onclick = function(){
			self.currState.buttonWasPressed();
		}
	};
	var light = new Light();
	light.init();

прокси-режим

Существует много типов агентов, таких какагент защитыЗапрос будет отфильтрован, и прокси Б может отказаться пропускать инициированный А запрос к С; виртуальный прокси отдаст часть операций прокси на выполнение, а прокси Б может выполнять операции при выполнении А определенных условий, то есть виртуальный прокси создаст некоторые накладные расходы. Большие объекты откладываются до тех пор, пока они действительно не понадобятся. Прокси-серверы защиты используются для управления доступом объектов с различными разрешениями к целевому объекту, но реализовать прокси-серверы защиты в JavaScript непросто, поскольку мы не можем сказать, кто получил доступ к объекту.

играет рольа такжеонтологияИнтерфейс должен быть непротиворечивым.Для пользователей они не знают разницы между прокси и онтологией.Любое место, где используется онтология, может быть заменено использованием прокси.

Сценарий: предварительная загрузка изображения

//只有本体
var myImage=(function(){
    var imgNode=document.createElement('img');
    document.body.appendChild(imgNode);
    
    return{
        setSrc:function(src){
            imgNode.src=src;
        }
    }
})
myImg.setSrc('./logo.lpg');

//有本体和代理的情况;引入代理对象proxyImage,通过这个代理对象,在图片被真正加载好之前,页面将出现一张占位的菊花图来提示用户图片正在被加载
var myImage=(function(){
    var imgNode=document.createElement('img');
    document.body.appendChild(imgNode);
    return{
        setSrc:function(src){
            imgNode.src=src;
        }
    }
})()

var proxyImage=(function(){
    var img=new Image;
    img.onload=function(){
        myImage.setSrc(this.src);
    }
    return{
        setSrc:function(src){
            myImage.setSrc('./loading.gif');
            img.src=src;
        }
    }
})()
proxyImage.setSrc('./logo.jpg')

режим стратегии

Цель шаблона стратегии состоит в том, чтобы отделить использование алгоритма от реализации алгоритма.Программа шаблона стратегии состоит как минимум из двух частей. Первая часть — это класс стратегии, который инкапсулирует конкретный алгоритм и отвечает за конкретный процесс расчета. Вторая часть — это класс среды Context, который принимает запрос клиента, а затем делегирует запрос определенному классу политики.

Реализация традиционного объектно-ориентированного языка

/*strategy类*/
var performanceS=function(){};

performanceS.prototype.caculate=function(salary){
    return salary*4;
}

var performanceA=function(){};

performanceA.prototype.caculate=function(salary){
    return salary*3;
}

var performanceB=function(){};

performanceB.proptype.caculate=function(salary){
    return salary*2;
}

/*Context类*/
var Bonus=function(){
    this.salary=null;
    this.strategy=null;
}

Bonus.prototype.setSalary=function(salary){
    this.salary=salary;
}
Bonus.prototype.setStrategy=function(strategy){
    this.strategy=strategy;
}
Bonus.prototype.getBonus=function(){
    return this.strategy.caculate(this.salary);
}

var bonus=new Bonus();
bonus.setSalary(1000);
bonus.setStrategy(new performanceS());
console.log(bonus.getBonus());

Реализация в JavaScript

В JavaScript Strategy мы можем определить как объект функции, Context отвечает за получение пользовательских запросов и делегирование их объекту стратегии.

var strategies={
    "S":function(salary){
        return salary*4;
    },
    "A":function(salary){
        return salary*3;
    },
    "B":function(salary){
        return salary*2;
    }
};

var calculateBonus=function(level,salary){
    return strategies[level](salary);
};

console.log(calculateBonus('S',2000));

Сцена: Тест Форма

/*Validator类的实现*/
var Validator = function(){
    this.cache = [];
}
Validator.prototype.add =function(dom,rule,errorMsg){
    var ary = rule.split(':');
    this.cache.push(function(){
        var strategy =ary.shift();
        ary.unshift(dom.value);
        ary.push(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;
        }
    }
}

/*定义strategy*/
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]$/.test(value)){
            return errorMsg;
        }
    }
}

/*定义Context*/
var validataFunc = function(){
    var validator = new Validator();
    validator.add(registerForm.userName,'isNonEmpty','用户名不能为空’);
    validator.add(registerForm.password,'inLength:6',密码长度不能少于6位'')
    validator.add(registerForm.phoneNumber,'isMobile','手机号码格式不正确’);

    var errorMsg = validator.start();
    return errorMsg;
}

var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
    varerrorMsg = validataFunc(); 
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

Схема цепочки ответственности

Режим цепочки ответственности: аналогично домино, при запросе первого условия последующие условия будут продолжать выполняться до тех пор, пока не будет возвращен результат.

img

Важность: 4 звезды, можно оптимизировать оператор if-else в проекте.

Сцена: Продажа мобильных телефонов

Сцена: поставщику электроэнергии за депозит было выплачено. Пользователи имеют льготную политику, после официальной покупки вы должны заплатить более 500 юаней. Пользователи депозита получат купоны на 100 юаней, пользователи на депозит в размере 200 долларов могут получить купоны на 50 юаней, пользователь не платит депозит можно купить только через обычный.

// orderType: 表示订单类型, 1: 500 元定金用户;2: 200 元定金用户;3: 普通购买用户
// pay: 表示用户是否已经支付定金, true: 已支付;false: 未支付
// stock: 表示当前用于普通购买的手机库存数量, 已支付过定金的用户不受此限制

const order = function( orderType, pay, stock ) {
  if ( orderType === 1 ) {
    if ( pay === true ) {
      console.log('500 元定金预购, 得到 100 元优惠券')
    } else {
      if (stock > 0) {
        console.log('普通购买, 无优惠券')
      } else {
        console.log('库存不够, 无法购买')
      }
    }
  } else if ( orderType === 2 ) {
    if ( pay === true ) {
      console.log('200 元定金预购, 得到 50 元优惠券')
    } else {
      if (stock > 0) {
        console.log('普通购买, 无优惠券')
      } else {
        console.log('库存不够, 无法购买')
      }
    }
  } else if ( orderType === 3 ) {
    if (stock > 0) {
        console.log('普通购买, 无优惠券')
    } else {
      console.log('库存不够, 无法购买')
    }
  }
}

order( 3, true, 500 ) // 普通购买, 无优惠券

Давайте изменим код с помощью шаблона цепочки ответственности:

const order500 = function(orderType, pay, stock) {
  if ( orderType === 1 && pay === true ) {
    console.log('500 元定金预购, 得到 100 元优惠券')
  } else {
    order200(orderType, pay, stock)
  }
}

const order200 = function(orderType, pay, stock) {
  if ( orderType === 2 && pay === true ) {
    console.log('200 元定金预购, 得到 50 元优惠券')
  } else {
    orderCommon(orderType, pay, stock)
  }
}

const orderCommon = function(orderType, pay, stock) {
  if (orderType === 3 && stock > 0) {
    console.log('普通购买, 无优惠券')
  } else {
    console.log('库存不够, 无法购买')
  }
}

order500( 3, true, 500 ) // 普通购买, 无优惠券

После преобразования можно обнаружить, что код относительно ясен, но код ссылки и бизнес-код все еще связаны вместе для дальнейшей оптимизации:

// 业务代码
const order500 = function(orderType, pay, stock) {
  if ( orderType === 1 && pay === true ) {
    console.log('500 元定金预购, 得到 100 元优惠券')
  } else {
    return 'nextSuccess'
  }
}

const order200 = function(orderType, pay, stock) {
  if ( orderType === 2 && pay === true ) {
    console.log('200 元定金预购, 得到 50 元优惠券')
  } else {
    return 'nextSuccess'
  }
}

const orderCommon = function(orderType, pay, stock) {
  if (orderType === 3 && stock > 0) {
    console.log('普通购买, 无优惠券')
  } else {
    console.log('库存不够, 无法购买')
  }
}

// 链路代码
const chain = function(fn) {
  this.fn = fn
  this.sucessor = null
}

chain.prototype.setNext = function(sucessor) {
  this.sucessor = sucessor
}

chain.prototype.init = function() {
  const result = this.fn.apply(this, arguments)
  if (result === 'nextSuccess') {
    this.sucessor.init.apply(this.sucessor, arguments)
  }
}

const order500New = new chain(order500)
const order200New = new chain(order200)
const orderCommonNew = new chain(orderCommon)

order500New.setNext(order200New)
order200New.setNext(orderCommonNew)

order500New.init( 3, true, 500 ) // 普通购买, 无优惠券

После рефакторинга код ссылок и бизнес-код полностью разделены. Если вам нужно добавить order300 в будущем, вам нужно только добавить функции, связанные с ним, без изменения исходного бизнес-кода.

Кроме того, в сочетании с АОП приведенный выше код ссылки можно упростить:

// 业务代码
const order500 = function(orderType, pay, stock) {
  if ( orderType === 1 && pay === true ) {
    console.log('500 元定金预购, 得到 100 元优惠券')
  } else {
    return 'nextSuccess'
  }
}

const order200 = function(orderType, pay, stock) {
  if ( orderType === 2 && pay === true ) {
    console.log('200 元定金预购, 得到 50 元优惠券')
  } else {
    return 'nextSuccess'
  }
}

const orderCommon = function(orderType, pay, stock) {
  if (orderType === 3 && stock > 0) {
    console.log('普通购买, 无优惠券')
  } else {
    console.log('库存不够, 无法购买')
  }
}

// 链路代码
Function.prototype.after = function(fn) {
  const self = this
  return function() {
    const result = self.apply(self, arguments)
    if (result === 'nextSuccess') {
      return fn.apply(self, arguments) // 这里 return 别忘记了~
    }
  }
}

const order = order500.after(order200).after(orderCommon)

order( 3, true, 500 ) // 普通购买, 无优惠券

Шаблон цепочки ответственности более важен. Его можно использовать во многих местах проекта. Он может отделить связь между 1 объектом запроса и n целевыми объектами.

одноэлементный шаблон

Определение шаблона singleton: гарантирует, что класс имеет только один экземпляр и предоставляет к нему глобальную точку доступа.

Приложение: пул потоков, глобальный кеш, объект окна в браузере и т. д.

Реализовано на традиционных объектно-ориентированных языках.

var SingleTon =function(name){
    this.name=name;
    this.instance=null;
}

SingleTon.proptype.getName=function(){
    alert(this.name);

}

SingleTon.getInstance=function(name){
    if(!this.instance){
        this.instance=new SingleTon(name);
    }
    return this.instance;
}

var a=SingleTon.getInstance('sven1');
var b=SingleTon.getInstance('sven2');

Или измените this.intance на форму локальной переменной

var SingleTon =function(name){
    this.name=name;
}

SingleTon.proptype.getName=function(){
    alert(this.name);

}

SingleTon.getInstance=function(name){
    var instance=null;
    return function(name) {
        if (!instance) {
            this.instance = new SingleTon(name);
        }
        return this.instance;
    }
}

var a=SingleTon.getInstance('sven1');
var b=SingleTon.getInstance('sven2');

Более прозрачный одноэлементный класс, когда пользователи создают объекты, они могут использовать любой другой обычный класс.

//这段代码既创建了对象,又返回了单例,跟其他创建对象方式无异
var CreateDiv=(function(){
    var instance;
    var CreateDiv=function(html){
        if(instance){
            return instance;
        }
        this.htm=html;
        this.init();
        return instance=this;
    };


    CreateDiv.proptype.init=function(){
        var div=document.createElement('div');
        div.innerHTML=this.html;
        document.body.appendChild(div);
    };
    return CreateDiv;
})()

var a=new CreateDiv('sven1');
var b=new CreateDiv('sven2');

Нам также необходимо отделить логику создания объектов, выполнения методов инициализации инициализации от управления синглтонами и введения прокси-классов для оптимизации кода.

var CreateDiv=function(html) {
    this.html = html;
    this.init();
}
CreateDiv.proptype.init=function(){
    var div=document.createElement('div');
    div.innerHTML=this.html;
    document.body.appendChild(div);
};


var ProxySingleTonCreateDiv=(function() {
    var instance;
    return function (html) {
        if (!instance) {
            instance = new CreateDiv(html);
        }

        return instance;
    };
})()


var a=new CreateDiv('sven1');
var b=new CreateDiv('sven2');

Реализовано на Javascript

JavaScript — это бесклассовый язык. В JavaScript мы реализуем одноэлементный шаблон через глобальные переменные, но глобальные переменные часто вызывают проблемы с загрязнением имен. Решения следующие:

1. Используйте пространства имен

var namespace1={
    a:function(){
        alert(1);
    },
    b:function(){
        laert(2);
    }
}

2. Используйте замыкания для инкапсуляции приватных переменных

var user=(function(){
    var _name='sven',_age=29;
    return{
        getUserInfo:function(){
            return _name+'-'+'_age';
        }
    }
})

модель посредника

Режим посредника относится к связи между объектами и объектами с помощью стороннего посредника. Режим посредничества позволяет отделить различные объекты и заменяет связь «сетка-многие» между объектами связью «один ко многим» между посредниками и объекты.Отношения «многие ко многим». Каждому объекту нужно сосредоточиться только на собственной реализации, а взаимодействие между объектами передается посреднику для реализации и поддержки.

После теста объявляются результаты: тому, кто ответил на вопрос, сообщается, что вызов успешен, в противном случае вызов провален.

const player = function(name) {
  this.name = name
  playerMiddle.add(name)
}

player.prototype.win = function() {
  playerMiddle.win(this.name)
}

player.prototype.lose = function() {
  playerMiddle.lose(this.name)
}

//在这段代码中 A、B、C 之间没有直接发生关系, 而是通过另外的 playerMiddle 对象建立链接, 姑且将之当成是中介者模式了
const playerMiddle = (function() { 
  const players = []
  const winArr = []
  const loseArr = []
  return {
    add: function(name) {
      players.push(name)
    },
    win: function(name) {
      winArr.push(name)
      if (winArr.length + loseArr.length === players.length) {
        this.show()
      }
    },
    lose: function(name) {
      loseArr.push(name)
      if (winArr.length + loseArr.length === players.length) {
        this.show()
      }
    },
    show: function() {
      for (let winner of winArr) {
        console.log(winner + '挑战成功;')
      }
      for (let loser of loseArr) {
        console.log(loser + '挑战失败;')
      }
    },
  }
}())

const a = new player('A 选手')
const b = new player('B 选手')
const c = new player('C 选手')

a.win()
b.win()
c.lose()

режим адаптера

Режим адаптера используется для решения проблемы несовместимости интерфейсов двух программных объектов.

var googleMap = {
		show: function(){
			console.log( '开始渲染谷歌地图' );
		}
	};
	var baiduMap = {
		display: function(){
			console.log( '开始渲染百度地图' );
		}
	};

    var renderMap = function( map ){
		if ( map.show instanceof Function ){
			map.show();
		}
	}; 



  //解决百度地图中展示地图的方法名与谷歌地图中展示地图的方法名不一样的问题
	var baiduMapAdapter = {
		show: function(){
			return baiduMap.display();

		}
	};

	renderMap( googleMap ); // 输出:开始渲染谷歌地图
	renderMap( baiduMapAdapter ); // 输出:开始渲染百度地图

шаблон итератора

Итераторы предназначены для предоставления способа последовательного доступа к элементам агрегатного объекта без раскрытия внутреннего представления объекта. Шаблон итератора может отделить процесс итерации от бизнес-логики.После использования шаблона итератора вы можете получить доступ к каждому элементу по порядку, даже если вас не волнует внутренняя структура объекта.

Реализация итератора:

var each=function(arry,callback){
    for(var i=0,l=ary.length;i<l;i++) {
        callback.call(ary[i], i, ary[i]);
    }
}

each([1,2,3],function(i,n){
    alert([i,n]);
})

внутренний итератор

Даже если вышеприведенная каждая функция является внутренним итератором, поскольку правила итерации внутреннего итератора были указаны заранее, вышеприведенная каждая функция не может одновременно выполнять итерацию двух массивов.Сейчас лучше, если мы хотим понять, является ли значение элементов в двух массивах оценивается. Полностью равны, если вы не можете изменить каждую функцию, вы можете изменить только функцию обратного вызова каждого

var compare=function(ary1,ary2){
    if(ary1.length!=ary2.length){
        throw new Error("ary1和ary2不相等")
    }
    each(ary1,funtion(i,n){
        if(n!==ary2[i]){
            thorw new Error('ary1和ary2不相等')
        }
    })
    alert('ary1和ary2相等')
}

compare([1,2,3],[1,2,4]);

внешний итератор

Внешняя итерация должна явно запрашивать итерацию элемента, внешняя итерация добавляет вызов сложности, но также повышает гибкость относительного итератора, мы можем вручную управлять итеративным процессом или порядком

var Interator=function(obj){
    var current=0;
    var next=funtion(){
        current+=1;
    }
    var isDone=function(){
        return current>=obj.length;
    }
    var getCurrItem=function(){
        return obj[current];
    }
    return {
        next:next,
        isDone:isDone,
    getCurrItem:getCurrItem,
        length:obj.length
    }
}

Комбинированный режим

  • Образцы композиции формируются между объектами树形结构;

  • В составном режиме базовые объекты и составные объекты一致对待;

  • Неважно, сколько слоев у объекта, просто вызывайте в корне при вызове;

    При сканировании папки под папкой может быть другая папка или файл.Мы хотим обрабатывать эти папки и файлы единообразно, что подходит для использования комбинированного режима.

const Folder = function(folder) {
  this.folder = folder
  this.lists = []
}

Folder.prototype.add = function(resource) {
  this.lists.push(resource)
}

Folder.prototype.scan = function() {
  console.log('开始扫描文件夹: ', this.folder)
  for (let i = 0, folder; folder = this.lists[i++];) {
    folder.scan()
  }
}

const File = function(file) {
  this.file = file
}

File.prototype.add = function() {
  throw Error('文件下不能添加其它文件夹或文件')
}

File.prototype.scan = function() {
  console.log('开始扫描文件: ', this.file)
}

const folder = new Folder('根文件夹')
const folder1 = new Folder('JS')
const folder2 = new Folder('life')

const file1 = new File('深入React技术栈.pdf')
const file2 = new File('JavaScript权威指南.pdf')
const file3 = new File('小王子.pdf')

folder1.add(file1)
folder1.add(file2)

folder2.add(file3)

folder.add(folder1)
folder.add(folder2)

folder.scan()

// 开始扫描文件夹:  根文件夹
// 开始扫描文件夹:  JS
// 开始扫描文件:  深入React技术栈.pdf
// 开始扫描文件:  JavaScript权威指南.pdf
// 开始扫描文件夹:  life
// 开始扫描文件:  小王子.pdf

наилегчайший образец

Шаблон Flyweight — это шаблон для оптимизации производительности программы, существенно уменьшающий количество создаваемых объектов.

Режим наилегчайшего веса можно использовать в следующих ситуациях:

  1. Имеется большое количество однотипных объектов, занимающих много памяти
  2. Большую часть состояния объекта можно извлечь как внешнее состояние.

Предприятие имеет 50 видов мужского нижнего белья и 50 видов женского нижнего белья и хочет их выставить.

Вариант 1: Сделайте 50 пластиковых мужских моделей и 50 пластиковых женских моделей, пусть они носят и демонстрируют, код выглядит следующим образом:

const Model = function(gender, underwear) {
  this.gender = gender
  this.underwear = underwear
}

Model.prototype.takephoto = function() {
  console.log(`${this.gender}穿着${this.underwear}`)
}

for (let i = 1; i < 51; i++) {
  const maleModel = new Model('male', `第${i}款衣服`)
  maleModel.takephoto()
}

for (let i = 1; i < 51; i++) {
  const female = new Model('female', `第${i}款衣服`)
  female.takephoto()
}

Вариант 2: Сделать 1 пластиковую мужскую модель и 1 пластиковую женскую модель и примерить 50 видов нижнего белья соответственно.

const Model = function(gender) {
  this.gender = gender
}

Model.prototype.takephoto = function() {
  console.log(`${this.sex}穿着${this.underwear}`)
}

const maleModel = new Model('male')
const femaleModel = new Model('female')

for (let i = 1; i < 51; i++) {
  maleModel.underwear = `第${i}款衣服`
  maleModel.takephoto()
}

for (let i = 1; i < 51; i++) {
  femaleModel.underwear = `第${i}款衣服`
  femaleModel.takephoto()
}

Сравнение показало, что: Схема 1 создала 100 объектов, а Схема 2 создала только объекты 2. В этой демонстрации пол (гендер) — это внутренний объект, а нижнее белье (одежда) — внешний объект.

Конечно, в демо-версии Схемы 2 ее можно еще улучшить:

  1. Экземпляры создаются явно через конструктор в самого начала, которые могут быть обновлены до контролируемого поколения с использованием заводской структуры
  2. Вручную добавлять нижнее белье на инстанс не очень элегантно, можно написать функцию менеджера отдельно снаружи
const Model = function(gender) {
  this.gender = gender
}

Model.prototype.takephoto = function() {
  console.log(`${this.gender}穿着${this.underwear}`)
}

const modelFactory = (function() { // 优化第一点
  const modelGender = {}
  return {
    createModel: function(gender) {
      if (modelGender[gender]) {
        return modelGender[gender]
      }
      return modelGender[gender] = new Model(gender)
    }
  }
}())

const modelManager = (function() {
  const modelObj = {}
  return {
    add: function(gender, i) {
      modelObj[i] = {
        underwear: `第${i}款衣服`
      }
      return modelFactory.createModel(gender)
    },
    copy: function(model, i) { // 优化第二点
      model.underwear = modelObj[i].underwear
    }
  }
}())

for (let i = 1; i < 51; i++) {
  const maleModel = modelManager.add('male', i)
  modelManager.copy(maleModel, i)
  maleModel.takephoto()
}

for (let i = 1; i < 51; i++) {
  const femaleModel = modelManager.add('female', i)
  modelManager.copy(femaleModel, i)
  femaleModel.takephoto()
}

Шаблон метода шаблона

Шаблонный метод — это шаблон проектирования, основанный на наследовании.Шаблон метода шаблона состоит из двух частей: первая — это абстрактный родительский класс, а вторая — конкретный подкласс реализации. Но в JavaScript нам часто не нужно рисовать совок для реализации шаблона метода шаблона, и функции более высокого порядка — лучший выбор.

var Coffee = function(){};
	Coffee.prototype.boilWater = function(){
		console.log( '把水煮沸' );
	};
	Coffee.prototype.brewCoffeeGriends = function(){
		console.log( '用沸水冲泡咖啡' );
	};
	Coffee.prototype.pourInCup = function(){
		console.log( '把咖啡倒进杯子' );
	};
	Coffee.prototype.addSugarAndMilk = function(){
		console.log( '加糖和牛奶' );
	};
	Coffee.prototype.init = function(){
		this.boilWater();
		this.brewCoffeeGriends();
		this.pourInCup();
		this.addSugarAndMilk();
	};
	var coffee = new Coffee();
	coffee.init();

	var Tea = function(){};
	Tea.prototype.boilWater = function(){
		console.log( '把水煮沸' );
	};
	Tea.prototype.steepTeaBag = function(){
		console.log( '用沸水浸泡茶叶' );
	};
	Tea.prototype.pourInCup = function(){
		console.log( '把茶水倒进杯子' );
	};
	Tea.prototype.addLemon = function(){
		console.log( '加柠檬' );
	};
	Tea.prototype.init = function(){
		this.boilWater();
		this.steepTeaBag();
		this.pourInCup();
		this.addLemon();
	};
	var tea = new Tea();
	tea.init();

Некоторые процессы заваривания чая и кофе одинаковы.Мы можем определить класс Beverage, чтобы определить общие действия при заваривании чая и кофе, а уникальные действия при заваривании чая и кофе могут напрямую переопределить метод класса Beverage. в

var Beverage = function( param ){
		var boilWater = function(){
			console.log( '把水煮沸' );
		};
		var brew = param.brew || function(){
			throw new Error( '必须传递brew 方法' );
		};
		var pourInCup = param.pourInCup || function(){
			throw new Error( '必须传递pourInCup 方法' );
		};
		var addCondiments = param.addCondiments || function(){
			throw new Error( '必须传递addCondiments 方法' );
		};
		var F = function(){};
		F.prototype.init = function(){
			boilWater();
			brew();
			pourInCup();
			addCondiments();
		};
		return F;
	};
	var Coffee = Beverage({
		brew: function(){
			console.log( '用沸水冲泡咖啡' );
		},
		pourInCup: function(){
			console.log( '把咖啡倒进杯子' );
		},
		addCondiments: function(){
			console.log( '加糖和牛奶' );
		}
	});

	var Tea = Beverage({
		brew: function(){
			console.log( '用沸水浸泡茶叶' );
		},
		pourInCup: function(){
			console.log( '把茶倒进杯子' );
		},
		addCondiments: function(){
			console.log( '加柠檬' );
		}
	});
	var coffee = new Coffee();
	coffee.init();
	var tea = new Tea();
	tea.init();

командный режим

Мы разделяем некую задачу на две части, одна часть программиста реализует отрисовку кнопки, они не знают, для чего кнопка используется, а часть программиста отвечает за то, чтобы прописать конкретное поведение после нажатия кнопки.

<body>

	<button id="button1">点击按钮1</button>
	<button id="button2">点击按钮2</button>
	<button id="button3">点击按钮3</button>

	<script>
		var button1 = document.getElementById( 'button1' ),
		var button2 = document.getElementById( 'button2' ),
		var button3 = document.getElementById( 'button3' );

		var setCommand = function( button, command ){
			button.onclick = function(){
				command.execute();
			}
		};

		var MenuBar = {
			refresh: function(){
				console.log( '刷新菜单目录' );
			}
		};
		var SubMenu = {
			add: function(){
				console.log( '增加子菜单' );
			},
			del: function(){
				console.log( '删除子菜单' );
			}
		};
		在让button 变得有用起来之前,我们要先把这些行为都封装在命令类中:
		var RefreshMenuBarCommand = function( receiver ){
			this.receiver = receiver;
		};
		RefreshMenuBarCommand.prototype.execute = function(){
			this.receiver.refresh();
		};
		var AddSubMenuCommand = function( receiver ){
			this.receiver = receiver;
		};

		AddSubMenuCommand.prototype.execute = function(){
			this.receiver.add();
		};
		var DelSubMenuCommand = function( receiver ){
			this.receiver = receiver;
		};
		DelSubMenuCommand.prototype.execute = function(){
			console.log( '删除子菜单' );
		};

		var refreshMenuBarCommand = new RefreshMenuBarCommand( MenuBar );
		var addSubMenuCommand = new AddSubMenuCommand( SubMenu );
		var delSubMenuCommand = new DelSubMenuCommand( SubMenu );
		setCommand( button1, refreshMenuBarCommand );
		setCommand( button2, addSubMenuCommand );
		setCommand( button3, delSubMenuCommand );
	</script>
</body>

Использованная литература:

«Шаблоны проектирования JavaScript и методы разработки»