百度了一下都是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的操作
一些个人见解可能不完全正确,姑且算抛砖引玉了