zoukankan      html  css  js  c++  java
  • 《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退出多重循环
  • 相关阅读:
    复利计算
    实验四 主存空间的分配和回收
    0526 Sprint1个人总结 & 《构建之法》第八、九、十章
    实验三 进程调度模拟程序
    0427 scrum & 读后感
    0415 评论
    0414 结对2.0
    汉堡包
    0406 结对编程总结
    读《构建之法》第四章有感
  • 原文地址:https://www.cnblogs.com/jesse131/p/11529964.html
Copyright © 2011-2022 走看看