Fork me on GitHub

《Javascript设计模式与开发实践》--读书笔记

第2章 this call apply

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后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面

第3章 闭包和高阶函数

高阶函数的应用

函数柯里化

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);
});

第4章 单例模式

单例模式的核心是确保只有一个实例,并提供全局访问。
该模式可用于定义单一弹窗

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

第5章 策略模式

策略模式指的是定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换
该模式常用于表单校验

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

5.6.2 用策略模式重构表单校验

/***********************策略对象**************************/
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;
    }

};

第6章 代理模式

虚拟代理在惰性加载中的应用

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
我们在常常在项目中遇到分页的需求,同一页的数据理论上只需要去后台拉取一次,这些已经拉取到的数据在某个地方被缓存之后,下次再请求同一页的时候,便可以直接使用之前的数据。

第7章 迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各种元素,而又不需要暴露该对象的内部表示

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);

第8章 发布订阅模式

发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。在 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 ); // 售楼处发布消息

第9章 命令模式

在面向对象设计中,命令模式的接收者被当成 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 );

第10章 组合模式

10.7 组合模式的例子——扫描文件夹

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();

10.8 一些值得注意的地方

  1. 组合模式不是父子关系 组合对象把请求委托给它所包含的所有叶对象,它们能够合作的关键是拥有相同的接口。
  2. 对叶对象操作的一致性
  3. 双向映射关系

第11章 模板方法模式

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();

模板方法模式是基于继承的一种设计模式,父类封装了子类的算法框架和方法的执行顺序,
子类继承父类之后,父类通知子类执行这些方法

第12章 享元模式

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
}
]);

享元模式带来的好处很大程度上取决于如何使用以及何时使用,一般来说,以下情况发生时 便可以使用享元模式。

  1. 一个程序中使用了大量的相似对象。
  2. 由于使用了大量对象,造成很大的内存开销。
  3. 对象的大多数状态都可以变为外部状态。
  4. 剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象。

第13章 职责链模式

职责链模式的最大优点就是解耦了请求发送者和 N 个接收者之间的复杂关 系,由于不知道链中的哪个节点可以处理你发出的请求,所以你只需把请求传递给第一个节点即 可

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 ); // 输出:普通购买,无优惠券

第14章 中介者模式

中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。

    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 ); 
    };

第15章 装饰者模式

因为装饰者对象和它所装饰的对象拥有一致的接口,所以它们对使用该对象的客户来说是透 明的,被装饰的对象也并不需要了解它曾经被装饰过,这种透明性使得我们可以递归地嵌套任意 多个装饰者对象

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();
// 分别输出: 发射普通子弹、发射导弹、发射原子弹

用AOP装饰函数

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();

第16章 状态模式

状态模式的关键是把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部

javascript版的状态机

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();

第17章 适配器模式

适配器模式的作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。

	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 ) );
  1. 适配器模式主要用来解决两个已有接口之间不匹配的问题,它不考虑这些接口是怎样实现的,也不考虑它们将来可能会如何演化。适配器模式不需要改变已有的接口,就能够使它们协同作用。
  2. 装饰者模式和代理模式也不会改变原有对象的接口,但装饰者模式的作用是为了给对象增加功能。装饰者模式常常形成一条长的装饰链,而适配器模式通常只包装一次。代理模式是为了控制对对象的访问,通常也只包装一次。
  3. 外观模式的作用倒是和适配器比较相似,有人把外观模式看成一组对象的适配器,但外观模式最显著的特点是定义了一个新的接口。

第18章 单一职责原则

SRP 原则体现为:一个对象(方法)只做一件事情

第19章 最少知识原则

最少知识原则要求我们在设计程序时,应当尽量减少对象之间的交互。如果两个对象之间不必彼此直接通信,那么这两个对象就不要发生直接的相互联系。常见的做法是引入一个第三者对象,来承担这些对象之间的通信作用。如果一些对象需要向另一些对象发起请求,可以通过第三 者对象来转发这些请求。

第20章 开放封闭原则

当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码
通过封装变化的方式,可以把系统中稳定不变的部分和容易变化的部分隔离开来。在系统的 演变过程中,我们只需要替换那些容易变化的部分

第22章 代码重构

  1. 提炼函数
  2. 合并重复的条件判断
  3. 把条件分支语句提炼成函数
  4. 合理使用循环
  5. 提前让函数退出代替嵌套条件分支
  6. 传递对象参数代替过长的参数列表
  7. 尽量减少参数数量
  8. 少用三目运算符
  9. 合理使用链式调用
  10. 分解大型类
  11. 用return退出多重循环
posted @ 2019-09-16 20:59  Jesse131  阅读(834)  评论(0编辑  收藏  举报