bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。
bind()方法底层实现
Function.prototype.bind = Function.prototype.bind || function () {
var self = this
var rest1 = Array.prototype.slice.call(arguments)
var context = rest1.shift() // 获取第一个参数,this的指向
return function () {
var rest2 = Array.prototype.slice.call(arguments) // 获取其余参数
return self.apply(context, rest1.concat(rest2)) // 将预设参数和其余参数一起传参
}
}
var food = {
name: '汉堡',
price: '5块钱',
getPrice: function (place,name) {
console.log(place + this.price + name)
}
}
var getPrice1 = food.getPrice.bind({ name: '鸡腿', price: '7块钱' }, '肯打鸡 ')
getPrice1('jesse')
bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为bind()的参数写在this后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面
函数柯里化
var curring = function(fn){
var args = [];
return function(){
if(arguments.length === 0){
return fn.apply(this,args);
}else{
[].push.apply(args,arguments);
return arguments.callee;
}
}
}
var cost = (function(){
var money = 0;
return function(){
for(var i = 0;,l = arguments.length;i < l;i++){
money += aruments[i];
}
return money;
}
})()
var cost = curring(cost);
cost(100);
console.log(cost());
uncurring()
Function.prototype.uncurring = fucntion(){
var self = this;
return function(){
var obj = Array.prototype.shift.call(arguments);
return self.apply(obj,arguments);
}
}
var push = Array.prototype.push.uncurring();
(function(){
push(arguments,4);
console.log(arguments); //[1,2,3,4]
})(1,2,3);
函数节流,用于解决函数频繁被调用而造成的性能问题
var throttle = function(fn,interval){
var _self = fn,timer,firstTime = true;
return function(){
var args = arguments,_me = this;
if(firstTime){
_self.apply(_me,args);
return firstTime = false;
}
if(timer){//500毫秒之内再次触发的缩放事件不处理
return false;
}
timer = setTimeout(function(){
clearTimeout(timer);
timer = null;
_self.apply(_me,args);
},interval||500);
};
};
window.onresize = throttle(function(){
console.log(1)
},500);
惰性加载函数
var addEvent = function( elem, type, handler ){
if ( window.addEventListener ){
addEvent = function( elem, type, handler ){//重写函数,避免频繁调用嗅探函数
elem.addEventListener( type, handler, false );
}
}else if ( window.attachEvent ){
addEvent = function( elem, type, handler ){
elem.attachEvent( 'on' + type, handler );
}
}
addEvent( elem, type, handler );
};
var div = document.getElementById( 'div1' );
addEvent( div, 'click', function(){
alert (1);
});
addEvent( div, 'click', function(){
alert (2);
});
单例模式的核心是确保只有一个实例,并提供全局访问。
该模式可用于定义单一弹窗
var CreateDiv = function( html ){
this.html = html;
this.init();
};
CreateDiv.prototype.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 ProxySingletonCreateDiv( 'sven1' );
var b = new ProxySingletonCreateDiv( 'sven2' );
alert ( a === b );
ES6写法
let instance
class CreateDiv{
constructor(html){
if(instance){
return instance
}
this.html = html;
this.init();
return instance = this
}
init(){
var div = document.createElement('div')
div.innerHTML = this.html
document.body.appendChild(div)
}
}
var a = new CreateDiv( 'sven1' );
var b = new CreateDiv( 'sven2' );
console.log(a===b) // true
策略模式指的是定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换
该模式常用于表单校验
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', 20000 ) ); // 输出:80000
console.log( calculateBonus( 'A', 10000 ) ); // 输出:30000
/***********************策略对象**************************/
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;
}
}
};
/***********************Validator 类**************************/
var Validator = function(){
this.cache = [];
};
Validator.prototype.add = function( dom, rules ){
var self = this;
for ( var i = 0, rule; rule = rules[ i++ ]; ){
(function( rule ){
var strategyAry = rule.strategy.split( ':' );
var errorMsg = rule.errorMsg;
self.cache.push(function(){
var strategy = strategyAry.shift();
strategyAry.unshift( dom.value );
strategyAry.push( errorMsg );
return strategies[ strategy ].apply( dom, strategyAry );
});
})( rule )
}
};
Validator.prototype.start = function(){
for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){
var errorMsg = validatorFunc();
if ( errorMsg ){
return errorMsg;
}
}
};
/***********************客户调用代码**************************/
var registerForm = document.getElementById( 'registerForm' );
var validataFunc = function(){
var validator = new Validator();
validator.add( registerForm.userName, [{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空'
}, {
strategy: 'minLength:6',
errorMsg: '用户名长度不能小于10 位'
}]);
validator.add( registerForm.password, [{
strategy: 'minLength:6',
errorMsg: '密码长度不能小于6 位'
}]);
var errorMsg = validator.start();
return errorMsg;
}
registerForm.onsubmit = function(){
var errorMsg = validataFunc();
if ( errorMsg ){
alert ( errorMsg );
return false;
}
};
var miniConsole = (function(){
var cache = [];
var handler = function( ev ){
if ( ev.keyCode === 113 ){
var script = document.createElement( 'script' );
script.onload = function(){
for ( var i = 0, fn; fn = cache[ i++ ]; ){
fn();
}
};
script.src = 'miniConsole.js';
document.getElementsByTagName( 'head' )[0].appendChild( script );
document.body.removeEventListener( 'keydown', handler );// 只加载一次miniConsole.js
}
};
document.body.addEventListener( 'keydown', handler, false );
return {
log: function(){
var args = arguments;
cache.push( function(){
return miniConsole.log.apply( miniConsole, args );
});
}
}
})();
miniConsole.log( 11 ); // 开始打印log
// miniConsole.js 代码
miniConsole = {
log: function(){
// 真正代码略
console.log( Array.prototype.join.call( arguments ) );
}
var mult = function(){
console.log( '开始计算乘积' );
var a = 1;
for ( var i = 0, l = arguments.length; i < l; i++ ){
a = a * arguments[i];
}
return a;
};
mult( 2, 3 ); // 输出:6
mult( 2, 3, 4 ); // 输出:24
//现在加入缓存代理函数:
var proxyMult = (function(){
var cache = {};
return function(){
var args = Array.prototype.join.call( arguments, ',' );
if ( args in cache ){
return cache[ args ];
}
return cache[ args ] = mult.apply( this, arguments );
}
})();
proxyMult( 1, 2, 3, 4 ); // 输出:24
proxyMult( 1, 2, 3, 4 ); // 输出:24
我们在常常在项目中遇到分页的需求,同一页的数据理论上只需要去后台拉取一次,这些已经拉取到的数据在某个地方被缓存之后,下次再请求同一页的时候,便可以直接使用之前的数据。
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各种元素,而又不需要暴露该对象的内部表示
var getActiveUploadObj = function(){
//...
};
var getFlashUploadObj = function(){
//...
};
var getFormUploadObj = function(){
//...
};
var iteratorUploadObj = function(){
for(var i=0,fn;fn=arguments[i++];){
var uploadObj = fn();
if(uploadObj !== false){
return uploadObj;
}
}
};
var uploadObj = iteratorUploadObj(getActiveUploadObj,getFlashUploadObj,getFormUploadObj);
发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。在 JavaScript 开发中,我们一般用事件模型 来替代传统的发布—订阅模式。
var Event = (function(){
var clientList = {},
listen,
trigger,
remove;
listen = function( key, fn ){
if ( !clientList[ key ] ){
clientList[ key ] = [];
}
clientList[ key ].push( fn );
};
trigger = function(){
var key = Array.prototype.shift.call( arguments ),
fns = clientList[ key ];
if ( !fns || fns.length === 0 ){
return false;
}
for( var i = 0, fn; fn = fns[ i++ ]; ){
fn.apply( this, arguments );
}
};
remove = function( key, fn ){
var fns = clientList[ key ];
if ( !fns ){
return false;
}
if ( !fn ){
fns && ( fns.length = 0 );
}else{
for ( var l = fns.length - 1; l >=0; l-- ){
var _fn = fns[ l ];
if ( _fn === fn ){
fns.splice( l, 1 );
}
}
}
};
return {
listen: listen,
trigger: trigger,
remove: remove
}
})();
Event.listen( 'squareMeter88', function( price ){ // 小红订阅消息
console.log( '价格= ' + price ); // 输出:'价格=2000000'
});
Event.trigger( 'squareMeter88', 2000000 ); // 售楼处发布消息
在面向对象设计中,命令模式的接收者被当成 command 对象的属性保存起来,同时约定执行命令的操作调用 command.execute 方法。在使用闭包的命令模式实现中,接收者被封闭在闭包产生的环境中,执行命令的操作可以更加简单,仅仅执行回调函数即可。无论接收者被保存为对象的属性,还是被封闭在闭包产生的环境中,在将来执行命令的时候,接收者都能被顺利访问。用闭包实现的命令模式如下代码所示:
var RefreshMenuBarCommand = function( receiver ){
return {
execute: function(){
receiver.refresh();
}
}
};
var setCommand = function( button, command ){
button.onclick = function(){
command.execute();
}
};
var refreshMenuBarCommand = RefreshMenuBarCommand( MenuBar );
setCommand( button1, refreshMenuBarCommand );
var Folder = function( name ){
this.name = name;
this.files = [];
};
Folder.prototype.add = function( file ){
this.files.push( file );
};
Folder.prototype.scan = function(){
console.log( '开始扫描文件夹: ' + this.name );
for ( var i = 0, file, files = this.files; file = files[ i++ ]; ){
file.scan();
}
};
/******************************* File ******************************/
var File = function( name ){
this.name = name;
};
File.prototype.add = function(){
throw new Error( '文件下面不能再添加文件' );
};
File.prototype.scan = function(){
console.log( '开始扫描文件: ' + this.name );
};
var folder = new Folder( '学习资料' );
var folder1 = new Folder( 'JavaScript' );
var folder2 = new Folder ( 'jQuery' );
var file1 = new File( 'JavaScript 设计模式与开发实践' );
var file2 = new File( '精通jQuery' );
var file3 = new File( '重构与模式' )
folder1.add( file1 );
folder2.add( file2 );
folder.add( folder1 );
folder.add( folder2 );
folder.add( file3 );
var folder3 = new Folder( 'Nodejs' );
var file4 = new File( '深入浅出Node.js' );
folder3.add( file4 );
var file5 = new File( 'JavaScript 语言精髓与编程实践' );
folder.add( folder3 );
folder.add( file5 );
folder.scan();
双向映射关系
var Beverage = function(){};
Beverage.prototype.boilWater = function(){
console.log( '把水煮沸' );
};
Beverage.prototype.brew = function(){
throw new Error( '子类必须重写brew 方法' );
};
Beverage.prototype.pourInCup = function(){
throw new Error( '子类必须重写pourInCup 方法' );
};
Beverage.prototype.addCondiments = function(){
throw new Error( '子类必须重写addCondiments 方法' );
};
Beverage.prototype.customerWantsCondiments = function(){
return true; // 默认需要调料
};
Beverage.prototype.init = function(){//封装了子类的算法框架
this.boilWater();
this.brew();
this.pourInCup();
if ( this.customerWantsCondiments() ){ // 如果挂钩返回true,则需要调料
this.addCondiments();
}
};
var CoffeeWithHook = function(){};
CoffeeWithHook.prototype = new Beverage();
CoffeeWithHook.prototype.brew = function(){
console.log( '用沸水冲泡咖啡' );
};
CoffeeWithHook.prototype.pourInCup = function(){
console.log( '把咖啡倒进杯子' );
};
CoffeeWithHook.prototype.addCondiments = function(){
console.log( '加糖和牛奶' );
};
CoffeeWithHook.prototype.customerWantsCondiments = function(){
return window.confirm( '请问需要调料吗?' );
};
var coffeeWithHook = new CoffeeWithHook();
coffeeWithHook.init();
模板方法模式是基于继承的一种设计模式,父类封装了子类的算法框架和方法的执行顺序,
子类继承父类之后,父类通知子类执行这些方法
var Upload = function( uploadType){
this.uploadType = uploadType;
};
Upload.prototype.delFile = function( id ){
uploadManager.setExternalState( id, this ); // (1)
if ( this.fileSize < 3000 ){
return this.dom.parentNode.removeChild( this.dom );
}
if ( window.confirm( '确定要删除该文件吗? ' + this.fileName ) ){
return this.dom.parentNode.removeChild( this.dom );
}
}
var UploadFactory = (function(){
var createdFlyWeightObjs = {};
return {
create: function( uploadType){
if ( createdFlyWeightObjs [ uploadType] ){
return createdFlyWeightObjs [ uploadType];
}
return createdFlyWeightObjs [ uploadType] = new Upload( uploadType);
}
}
})();
var uploadManager = (function(){
var uploadDatabase = {};
return {
add: function( id, uploadType, fileName, fileSize ){
var flyWeightObj = UploadFactory.create( uploadType );
var dom = document.createElement( 'div' );
dom.innerHTML =
'<span>文件名称:'+ fileName +', 文件大小: '+ fileSize +'</span>' +
'<button class="delFile">删除</button>';
dom.querySelector( '.delFile' ).onclick = function(){
flyWeightObj.delFile( id );
}
document.body.appendChild( dom );
uploadDatabase[ id ] = {
fileName: fileName,
fileSize: fileSize,
dom: dom
};
return flyWeightObj ;
},
setExternalState: function( id, flyWeightObj ){
var uploadData = uploadDatabase[ id ];
for ( var i in uploadData ){
flyWeightObj[ i ] = uploadData[ i ];
}
}
}
})();
var id = 0;
window.startUpload = function( uploadType, files ){
for ( var i = 0, file; file = files[ i++ ]; ){
var uploadObj = uploadManager.add( ++id, uploadType, file.fileName, file.fileSize );
}
};
startUpload( 'plugin', [
{
fileName: '1.txt',
fileSize: 1000
},
{
fileName: '2.html',
fileSize: 3000
},
{
fileName: '3.txt',
fileSize: 5000
}
]);
startUpload( 'flash', [
{
fileName: '4.txt',
fileSize: 1000
},
{
fileName: '5.html',
fileSize: 3000
},
{
fileName: '6.txt',
fileSize: 5000
}
]);
享元模式带来的好处很大程度上取决于如何使用以及何时使用,一般来说,以下情况发生时 便可以使用享元模式。
剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象。
var order500 = function( orderType, pay, stock ){
if ( orderType === 1 && pay === true ){
console.log( '500 元定金预购,得到100 优惠券' );
}else{
return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
}
};
var order200 = function( orderType, pay, stock ){
if ( orderType === 2 && pay === true ){
console.log( '200 元定金预购,得到50 优惠券' );
}else{
return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
}
};
var orderNormal = function( orderType, pay, stock ){
if ( stock > 0 ){
console.log( '普通购买,无优惠券' );
}else{
console.log( '手机库存不足' );
}
};
Chain.prototype.setNextSuccessor 指定在链中的下一个节点
Chain.prototype.passRequest 传递请求给某个节点
var Chain = function( fn ){
this.fn = fn;
this.successor = null;
};
Chain.prototype.setNextSuccessor = function( successor ){
return this.successor = successor;
};
Chain.prototype.passRequest = function(){
var ret = this.fn.apply( this, arguments );
if ( ret === 'nextSuccessor' ){
return this.successor && this.successor.passRequest.apply( this.successor, arguments );
}
return ret;
};
var chainOrder500 = new Chain( order500 );
var chainOrder200 = new Chain( order200 );
var chainOrderNormal = new Chain( orderNormal );
chainOrder500.setNextSuccessor( chainOrder200 );
chainOrder200.setNextSuccessor( chainOrderNormal );
chainOrder500.passRequest( 1, true, 500 ); // 输出:500 元定金预购,得到100 优惠券
chainOrder500.passRequest( 2, true, 500 ); // 输出:200 元定金预购,得到50 优惠券
chainOrder500.passRequest( 3, true, 500 ); // 输出:普通购买,无优惠券
chainOrder500.passRequest( 1, false, 0 ); // 输出:手机库存不足
Function.prototype.after = function( fn ){
var self = this;
return function(){
var ret = self.apply( this, arguments );
if ( ret === 'nextSuccessor' ){
return fn.apply( this, arguments );
}
return ret;
}
};
var order = order500yuan.after( order200yuan ).after( orderNormal );
order( 1, true, 500 ); // 输出:500 元定金预购,得到100 优惠券
order( 2, true, 500 ); // 输出:200 元定金预购,得到50 优惠券
order( 1, false, 500 ); // 输出:普通购买,无优惠券
中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。
var goods = { // 手机库存
"red|32G": 3,
"red|16G": 0,
"blue|32G": 1,
"blue|16G": 6
}
var colorSelect = document.getElementById( 'colorSelect' ), memorySelect = document.getElementById( 'memorySelect' ), numberInput = document.getElementById( 'numberInput' ), colorInfo = document.getElementById( 'colorInfo' ),
memoryInfo = document.getElementById( 'memoryInfo' ),
numberInfo = document.getElementById( 'numberInfo' ),
nextBtn = document.getElementById( 'nextBtn' );
var mediator = (function(){
return {
changed: function( obj ){
var color = colorSelect.value, // 颜色
memory = memorySelect.value,// 内存
number = numberInput.value, // 数量
stock = goods[ color + '|' + memory ];// 颜色和内存对应的手机库存数量
if ( obj === colorSelect ){ // 如果改变的是选择颜色下拉框
colorInfo.innerHTML = color;
}else if ( obj === memorySelect ){
memoryInfo.innerHTML = memory;
}else if ( obj === numberInput ){
numberInfo.innerHTML = number;
}
if ( !color ){
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择手机颜色';
return;
}
if ( !memory ){
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择内存大小';
return;
}
if ( ( ( number - 0 ) | 0 ) !== number - 0 ){ // 输入购买数量是否为正整数
nextBtn.disabled = true;
nextBtn.innerHTML = '请输入正确的购买数量';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车';
}
}
})();
// 事件函数:
colorSelect.onchange = function(){
mediator.changed( this );
};
memorySelect.onchange = function(){
mediator.changed( this );
};
numberInput.oninput = function(){
mediator.changed( this );
};
因为装饰者对象和它所装饰的对象拥有一致的接口,所以它们对使用该对象的客户来说是透 明的,被装饰的对象也并不需要了解它曾经被装饰过,这种透明性使得我们可以递归地嵌套任意 多个装饰者对象
var plane = {
fire: function(){
console.log( '发射普通子弹' );
}
}
var missileDecorator = function(){
console.log( '发射导弹' );
}
var atomDecorator = function(){
console.log( '发射原子弹' );
}
var fire1 = plane.fire;
plane.fire = function(){
fire1();
missileDecorator();
}
var fire2 = plane.fire;
plane.fire = function(){
fire2();
atomDecorator();
}
plane.fire();
// 分别输出: 发射普通子弹、发射导弹、发射原子弹
Function.prototype.before = function( beforefn ){
var __self = this; // 保存原函数的引用
return function(){ // 返回包含了原函数和新函数的"代理"函数
beforefn.apply( this, arguments ); // 执行新函数,且保证 this 不被劫持,新函数接受的参数 // 也会被原封不动地传入原函数,新函数在原函数之前执行
return __self.apply( this, arguments ); // 执行原函数并返回原函数的执行结果, 2 // 并且保证 this 不被劫持
}
}
Function.prototype.after = function( afterfn ){
var __self = this;
return function(){
var ret = __self.apply( this, arguments );
afterfn.apply( this, arguments );
return ret;
}
};
document.getElementById = document.getElementById.before(function(){
alert (1);
});
var button = document.getElementById( 'button' );
不喜欢这种污染原型的方式,那么我们可以做一些变通
var before = function( fn, beforefn ){
return function(){
beforefn.apply( this, arguments );
return fn.apply( this, arguments );
}
}
var a = before(
function(){alert (3)},
function(){alert (2)}
);
a =before( a, function(){alert (1);} );
a();
状态模式的关键是把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部
var Light = function(){
this.currState = FSM.off; // 设置当前状态
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.call( self ); // 把请求委托给FSM 状态机
}
};
var FSM = {
off: {
buttonWasPressed: function(){
console.log( '关灯' );
this.button.innerHTML = '下一次按我是开灯';
this.currState = FSM.on;
}
},
on: {
buttonWasPressed: function(){
console.log( '开灯' );
this.button.innerHTML = '下一次按我是关灯';
this.currState = FSM.off;
}
}
};
var light = new Light();
light.init();
适配器模式的作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。
var guangdongCity = {
shenzhen: 11,
guangzhou: 12,
zhuhai: 13
};
var getGuangdongCity = function(){
var guangdongCity = [
{
name: 'shenzhen',
id: 11,
}, {
name: 'guangzhou',
id: 12,
}
];
return guangdongCity;
};
var render = function( fn ){
console.log( '开始渲染广东省地图' );
document.write( JSON.stringify( fn() ) );
};
var addressAdapter = function( oldAddressfn ){
var address = {},
oldAddress = oldAddressfn();
for ( var i = 0, c; c = oldAddress[ i++ ]; ){
address[ c.name ] = c.id;
}
return function(){
return address;
}
};
render( addressAdapter( getGuangdongCity ) );
SRP 原则体现为:一个对象(方法)只做一件事情
最少知识原则要求我们在设计程序时,应当尽量减少对象之间的交互。如果两个对象之间不必彼此直接通信,那么这两个对象就不要发生直接的相互联系。常见的做法是引入一个第三者对象,来承担这些对象之间的通信作用。如果一些对象需要向另一些对象发起请求,可以通过第三 者对象来转发这些请求。
当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码
通过封装变化的方式,可以把系统中稳定不变的部分和容易变化的部分隔离开来。在系统的 演变过程中,我们只需要替换那些容易变化的部分
原文:https://www.cnblogs.com/jesse131/p/11529964.html