1. 前言
在上一篇博客中,我介绍了JavaScript中闭包,模块和回调的概念,这些概念理解起来相对复杂。
在本篇博客中,我将通过例子,进一步解释这些概念。
2. 示例
2.1 模块模式的封装
首先让我们封装一个HouseInspector的模块,用于检查house对象。
在这个HouseInspector模块中,定义私有变量lastCheckResult用于存储上一次的检查结果。同时,返回对象中包含两个暴露的方法,方法CheckLight用于检查house对象中light的情况,方法getLastCheckResult用于返回检查结果。
在外部调用时,仅可访问HouseInspector模块所暴露的方法,HouseInspector模块内部的变量对外是不可见的。
var HouseInspector = (function () {
var lastCheckResult = "No check yet";
return {
checkLight: function (house) {
if (house.windows >= 4) {
lastCheckResult = 'Enough light';
} else {
lastCheckResult = 'Too dark!';
}
},
getLastCheckResult: function () {
return lastCheckResult;
}
}
})();
var house = {
window: 3,
people: [],
enter: function (name) {
this.people.push(name);
},
whoIsIn: function () {
for (var index = 0; index < this.people.length; index++) {
console.log(this.people[index]);
}
}
};
// test 1
HouseInspector.checkLight(house);
let checkResult = HouseInspector.getLastCheckResult();
console.log(checkResult); // 运行结果:Too dark!
通过调用HouseInspector模块提供的公共方法,我们可以完成对house对象的检查,并拿到检查结果。
2.2 闭包特性
接下来,让我们测试一下闭包的特性。
在HouseInspector中定义一个私有变量inspectionTimes, 用于记录执行检查的次数。然后连续使用HouseInspector来检查两个house对象。
var HouseInspector = (function () {
var lastCheckResult = "No check yet";
var inspectionTimes = 0;
return {
checkLight: function (house) {
if (house.windows >= 4) {
lastCheckResult = 'Enough light';
} else {
lastCheckResult = 'Too dark!';
}
inspectionTimes++;
},
getLastCheckResult: function () {
return lastCheckResult;
},
getInspectionTimes: function (){
return inspectionTimes;
}
}
})();
var house = {
window: 3,
people: [],
enter: function (name) {
this.people.push(name);
},
whoIsIn: function () {
for (var index = 0; index < this.people.length; index++) {
console.log(this.people[index]);
}
}
};
// test
HouseInspector.checkLight(house);
HouseInspector.checkLight(house);
let count = HouseInspector.getInspectionTimes( );
console.log(count); // 运行结果:2
可以看到,运行结果为2。这也就意味着,模块内的私有变量,仍会会一直保持,即使外部调用结束后。
类比:JavaScript中闭包的这种特性,有点类似于ABAP中单例模式的效果,也即在一个线程内,单例内部的属性会一致保持。(当然,也可以类比成,一个类的静态属性Static Attribute)
2.3 回调函数的使用
结合上例,让我们为HouseInspector模块的checkLight方法定义出一个回调函数的参数。
在使用时,我们创建出所需要回调函数,并将这个函数作为参数传递给HouseInspector模块的checkLight方法,这样,调用方就可以通过回调函数来影响函数的执行行为了。
在下例中,我们创建了两个不同功能的回调函数printResult和showResult,并在调用时传递给服务方。
var HouseInspector = (function () {
//Stored last result with initial value
var lastCheckResult = "No check yet";
return {
//Function to check the light
checkLight: function (house, fnResultHandler) {
if (house.windows >= 4) {
lastCheckResult = 'Enough light';
} else {
lastCheckResult = 'Too dark!';
}
//if a callback function was given, call it with the result
if (fnResultHandler && typeof (fnResultHandler) === 'function') {
fnResultHandler(lastCheckResult);
}
},
//Function to get the last stored result
getLastCheckResult: function () {
return lastCheckResult;
}
}
})();
var house = {
window: 3,
people: [],
enter: function (name) {
this.people.push(name);
},
whoIsIn: function () {
for (var index = 0; index < this.people.length; index++) {
console.log(this.people[index]);
}
}
};
//callback function 1: print the result to the console
function printResult(result) {
console.log(result);
}
//callback function 2: show a popup with the result
function showResult(result) {
//alert(result);
console.log('<< window >>');
console.log(result);
console.log('<< window >>');
}
//test
HouseInspector.checkLight(house);
HouseInspector.getLastCheckResult();
HouseInspector.checkLight(house, printResult); //运行结果:Too dark!
HouseInspector.checkLight(house, showResult); //运行结果:<< window >> Too dark! << window >>
可以看到,通过传入回调函数的不同,我们可以得到不同的运行结果。
类比:JavaScript中回调函数的概念,与ABAP编程中回调的思想是类似的,都是通过提前“注册”一个预期执行的行为给服务方。
只不过在JavaScript中,这种“注册”的方式是通过函数实现的,而在ABAP中,更多的是通过接口实现的。
(当然,在ABAP中,这种回调的思想,也可以通过Function实现,但并非将function作为输入参数,而是通过预先设计的配置表,将回调函数配置到配置表中)
3. 小结
本文通过示例,进一步解释了JavaScript中闭包,模块和回调的概念,并类比了ABAP中相关概念的实现方式。希望本文对你有帮助!