Revealing Module(揭示模块)模式

百度了一下都是JS设计模式复制粘贴,可能我比较愚钝,对那寥寥几行介绍并不能理解这个模式相比Module模式的优势,故记录下自己的一点想法。

1.为什么要使用Revealing Module?

先上代码,代码来自一位国外博主

window.onload = function () {
    eqCtl = document.getElementById('eq');
    currNumberCtl = document.getElementById('currNumber');
};

var eqCtl,
    currNumberCtl,
    operator,
    operatorSet = false,
    equalsPressed = false,
    lastNumber = null;

function add(x,y) {
    return x + y;
}

function subtract(x, y) {
    return x - y;
}

function multiply(x, y) {
    return x * y;
}

function divide(x, y) {
    if (y == 0) {
        alert("Can't divide by 0");
        return 0;
    }
    return x / y;
}
     
function setVal(val) {
    currNumberCtl.innerHTML = val;
}
        
function setEquation(val) {
    eqCtl.innerHTML = val;
}
        
function clearNumbers() {
    lastNumber = null;
    equalsPressed = operatorSet = false;
    setVal('0');
    setEquation('');
}

function setOperator(newOperator) {
    if (newOperator == '=') {
        equalsPressed = true;
        calculate();
        setEquation('');
        return;
    }
            
    //Handle case where = was pressed
    //followed by an operator (+, -, *, /)
    if (!equalsPressed) calculate();
    equalsPressed = false;
    operator = newOperator;
    operatorSet = true;
    lastNumber = parseFloat(currNumberCtl.innerHTML);
    var eqText = (eqCtl.innerHTML == '') ? 
        lastNumber + ' ' + operator + ' ' : 
        eqCtl.innerHTML + ' ' + operator + ' ';
    setEquation(eqText);
}

function numberClick(e) {
    var button = (e.target) ? e.target : e.srcElement;
    if (operatorSet == true || currNumberCtl.innerHTML == '0') {
        setVal('');
        operatorSet = false;            
    }
    setVal(currNumberCtl.innerHTML + button.innerHTML);
    setEquation(eqCtl.innerHTML + button.innerHTML);
}

function calculate() {
    if (!operator || lastNumber == null) return;
    var currNumber = parseFloat(currNumberCtl.innerHTML),
        newVal = 0;
    switch (operator) {
        case '+':
            newVal = add(lastNumber, currNumber);
            break;
        case '-':
            newVal = subtract(lastNumber, currNumber);
            break;
        case '*':
            newVal = multiply(lastNumber, currNumber);
            break;
        case '/':
            newVal = divide(lastNumber, currNumber);
            break;
    }
    setVal(newVal);
    lastNumber = newVal;
}

相信大家对这种意大利面式的代码不陌生,包括我自己的代码在一些地方也会写成这样(这也是学习的原因啊自己都看不下去),各个函数之间高耦合,代码可读性差


以下是Revealing Module(揭示模块模式的写法)

var Calculator = function () {
		    var eqCtl,
		    currNumberCtl,
		    operator,
		    operatorSet = false,
		    equalsPressed = false,
		    lastNumber = null,

		    init = function (equals, currNumber) {
		        eqCtl = equals;
		        currNumberCtl = currNumber;
		    },

		    add = function (x, y) {
		        return x + y;
		    },

		    subtract = function (x, y) {
		        return x - y;
		    },

		    multiply = function (x, y) {
		        return x * y;
		    },

		    divide = function (x, y) {
		        if (y == 0) {
		            alert("Can't divide by 0");
		            return 0;
		        }
		        return x / y;
		    },

		    setVal = function (val) {
		        currNumberCtl.innerHTML = val;
		    },

		    setEquation = function(val) {
		        eqCtl.innerHTML = val;
		    },

		    clearNumbers = function() {
		        lastNumber = null;
		        equalsPressed = operatorSet = false;
		        setVal('0');
		        setEquation('');
		    },

		    setOperator = function(newOperator) {
		        if (newOperator == '=') {
		            equalsPressed = true;
		            calculate();
		            setEquation('');
		            return;
		        }

		        //Handle case where = was pressed
		        //followed by an operator (+, -, *, /)
		        if (!equalsPressed) calculate();
		        equalsPressed = false;
		        operator = newOperator;
		        operatorSet = true;
		        lastNumber = parseFloat(currNumberCtl.innerHTML);
		        var eqText = (eqCtl.innerHTML == '') ?
		            lastNumber + ' ' + operator + ' ' :
		            eqCtl.innerHTML + ' ' + operator + ' ';
		        setEquation(eqText);
		    },

		    numberClick = function(e) {
		        var button = (e.target) ? e.target : e.srcElement;
		        if (operatorSet == true || currNumberCtl.innerHTML == '0') {
		            setVal('');
		            operatorSet = false;
		        }
		        setVal(currNumberCtl.innerHTML + button.innerHTML);
		        setEquation(eqCtl.innerHTML + button.innerHTML);
		    },

		    calculate = function() {
		        if (!operator || lastNumber == null) return;
		        var currNumber = parseFloat(currNumberCtl.innerHTML),
		            newVal = 0;
		        //eval() would've made this a whole lot simpler
		        //but didn't want to use it in favor of a more
		        //"robust" set of methods to demo patterns
		        switch (operator) {
		        case '+':
		            newVal = add(lastNumber, currNumber);
		            break;
		        case '-':
		            newVal = subtract(lastNumber, currNumber);
		            break;
		        case '*':
		            newVal = multiply(lastNumber, currNumber);
		            break;
		        case '/':
		            newVal = divide(lastNumber, currNumber);
		            break;
		        }
		        setVal(newVal);
		        lastNumber = newVal;
		    };

		    return {
		        init: init,
		        numberClick: numberClick,
		        setOperator: setOperator,
		        clearNumbers: clearNumbers
		    };
		} ();

		window.onload = function () {
		    var eqCtl = document.getElementById('eq');            
		    var currNumberCtl = document.getElementById('currNumber');
		    Calculator.init(eqCtl, currNumberCtl);
		};
Revealing Module模式是先定义一个立即执行函数表达式并把它赋值给一个变量,然后将函数定义在这个闭包匿名函数中来实现功能的复用

var Calculator = function () { /* Code goes here */ }();

因为js没有权限访问修饰符,所以通过Revealing Module可以实现良好的“公私分明”(即直观的给出可访问的,隐藏私有)


回到代码例子,第二个例子中引入的init函数是为了初始化传入与其交互的两个HTML元素

那么你可能会问,这种写法只有一个实例对象啊?我想在多个地方创建这种模式的属性怎么办呢?

直接上代码

var Calculator = function () {
    var eqCtl,
    currNumberCtl,
    operator,
    operatorSet = false,
    equalsPressed = false,
    lastNumber = null,

    init = function (equals, currNumber) {
        eqCtl = equals;
        currNumberCtl = currNumber;
    },

    add = function (x, y) {
        return x + y;
    },

    subtract = function (x, y) {
        return x - y;
    },

    multiply = function (x, y) {
        return x * y;
    },

    divide = function (x, y) {
        if (y == 0) {
            alert("Can't divide by 0");
            return 0;
        }
        return x / y;
    },

    setVal = function (val) {
        currNumberCtl.innerHTML = val;
    },

    setEquation = function(val) {
        eqCtl.innerHTML = val;
    },

    clearNumbers = function() {
        lastNumber = null;
        equalsPressed = operatorSet = false;
        setVal('0');
        setEquation('');
    },

    setOperator = function(newOperator) {
        if (newOperator == '=') {
            equalsPressed = true;
            calculate();
            setEquation('');
            return;
        }

        //Handle case where = was pressed
        //followed by an operator (+, -, *, /)
        if (!equalsPressed) calculate();
        equalsPressed = false;
        operator = newOperator;
        operatorSet = true;
        lastNumber = parseFloat(currNumberCtl.innerHTML);
        var eqText = (eqCtl.innerHTML == '') ?
            lastNumber + ' ' + operator + ' ' :
            eqCtl.innerHTML + ' ' + operator + ' ';
        setEquation(eqText);
    },

    numberClick = function(e) {
        var button = (e.target) ? e.target : e.srcElement;
        if (operatorSet == true || currNumberCtl.innerHTML == '0') {
            setVal('');
            operatorSet = false;
        }
        setVal(currNumberCtl.innerHTML + button.innerHTML);
        setEquation(eqCtl.innerHTML + button.innerHTML);
    },

    calculate = function() {
        if (!operator || lastNumber == null) return;
        var currNumber = parseFloat(currNumberCtl.innerHTML),
            newVal = 0;
        //eval() would've made this a whole lot simpler
        //but didn't want to use it in favor of a more
        //"robust" set of methods to demo patterns
        switch (operator) {
        case '+':
            newVal = add(lastNumber, currNumber);
            break;
        case '-':
            newVal = subtract(lastNumber, currNumber);
            break;
        case '*':
            newVal = multiply(lastNumber, currNumber);
            break;
        case '/':
            newVal = divide(lastNumber, currNumber);
            break;
        }
        setVal(newVal);
        lastNumber = newVal;
    };

    return {
        init: init,
        numberClick: numberClick,
        setOperator: setOperator,
        clearNumbers: clearNumbers
    };
} ;

var myCalc;
window.onload = function () {
    var eqCtl = document.getElementById('eq');
    var currNumberCtl = document.getElementById('currNumber');
    myCalc = Calculator(); //调用对象(分配给对象的函数)并分配给myCalc
    myCalc.init(eqCtl, currNumberCtl);
};

如果在页面或脚本中需要多个对象,则可以将不同的变量分配给遵循相同模式的Calculator()的调用。注意,每次对Calculator()的调用都会将每个函数的 新副本放在内存中。

将myCalc定义在window.onload外是为了便于其他模块后续对myCalc的操作


一些个人见解可能不完全正确,姑且算抛砖引玉了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值