前言
在平时的开发中,经常会遇到一些特殊情况,就需要一些招式来应对。这里总结一下自己常用到的一些小招式,有不足欢迎大家指出。
模块
var fuc1 = (function() {
var a = 'hello ';
return function(str) {
return a + str;
}
})()
fuc1('world'); // hello world
模块模式的一般形式是:一个定义了私有变量和函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把它们保存到一个可访问的地方。
该招式有以下应用场景
协调异步
代码需要在两个异步fuc1
与fuc2
都执行结束后再执行,
var manager = (function() {
var count = 0;
return function() {
if(++count === 2){
// logic 3
}
}
})()
function fuc1() {
//...
}
function fuc2() {
//...
}
setTimeout(function() {
fuc1();
manager();
}, 10)
setTimeout(function() {
fuc2();
manager();
}, 10)
兼容测试
var createXHR = (function () {
if(typeof XMLHttpRequest != "undefined") {
return function () {
return new XMLHttpRequest();
}
}else if(typeof ActiveXObject != "undefined") {
return function () {
if(typeof arguments.callee.activeXString != "string") {
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
i,len;
for(1 = 0, len = versions.length; i < len; i++){
try{
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
}catch(ex){
// ...
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
}else{
return function() {
throw new Error("NO XHR object available.");
}
}
})();
利用私有变量
var parse = (function() {
var config = ['I', 'II', 'III', 'IV'];
return function(num) {
return config[num - 1]
}
})()
parse(1); // I
parse(4); // IV
柯里化
柯里化听起来很高大上,然而也就是Currying的音译,千万别被名称吓跑了。
它是把接受多个参数的函数中的参数拆分成两次调用来传递并返回结果的新函数。
举一个简单的例子理解一下:
var add = function(a, b) {
return a + b
}
add(1, 2) // 3
var currying = function(a) {
return function(b) {
return a + b
}
}
currying(1)(2) // 3
add
为 接受多个参数的函数,currying
是将add
中的参数分两次调用传入并返回结果的新函数。
仔细想想,它其实是模块的衍生招式。模块的私有变量是预先定义好的,而柯里化是保存第一次调用时传入的参数为私有变量。因此它俩的区别是私有变量定义时机不同。
下面是一个利用柯里化实现的累加方法
var currying = function (fn) {
var args = [];
return function () {
var innerArgs = Array.prototype.slice.call(arguments);
if(innerArgs.length === 0){
return fn.apply(null, args)
}
args = args.concat(innerArgs);
return arguments.callee;
}
}
var mutil = function () {
var num = 0, i = 0;
while(arguments[i]){
num += arguments[i++]
}
return num
}
var add = currying(mutil);
add(1,2)(3);
add(4);
add(); // 10
函数节流
某些代码不可以在没有间断的情况下连续重复执行,所有就需要节流。比如:拖动某DOM元素时,需要动态改变元素的 x y ,就需要用到节流函数了。
function throttle(method, delay) {
var timer = null;
return function () {
var that = this, args = arguments;
clearTimeout(method.tId);
timer = setTimeout(function () {
method.apply(that, args);
clearTimeout(timer);
}, delay);
}
}
window.onresize = throttle(resizeHandle, 50)
使用该函数时你会发现一个问题:不断拖动元素的过程中元素是不动的,只有停顿一下元素才跟着动。
这样的体验很糟糕,为了提高性能、改善体验,需要升级一下节流函数。设置一个最大时间间隔来确保在拖动的过程中,元素可以跟随移动。
function throttle(method, context, delay, maxInterval) {
var _start,_diff,timer;
return function () {
var that = this,
args = arguments,
_now = +new Date();
clearTimeout(timer);
_start || (_start = _now);
if(_now - _start >= maxInterval){
method.apply(that, args)
}else{
timer = setTimeout(function () {
method.apply(that, args)
}, delay)
}
}
}
window.onresize = throttle(resizeHandle, 50, 100)
延迟加载处理
在业务中经常会遇到一些不需要同步处理的,又比较耗时的逻辑。对于这些逻辑可以采用数据分块(array chunking)的技术,小块小块地处理数组,通常每次一小块。可以创建一个“待办事项”队列,然后使用定时器逐个取出“事项”,接着设置另一个定时器。
function chunk(array, process, context) {
setTimeout(function() {
var item = array.shift();
process.call(context, item);
if(array.length > 0){
setTimeout(arguments.callee, 100);
}
}, 100)
}
记忆函数
函数可以将先前操作的结果记录在某个对象里,从而避免重复运算,比如计算阶乘函数。
function memoizer(memory, handle) {
return function (n) {
var result = memory[n],
recur = arguments.callee;
// 已运算过则直接返回
if(!result){
result = handle(recur ,n);
memory[n] = result;
}
return result;
}
}
var memory = [1, 1],
factorial = memoizer(memory, function (recur, n) {
return n * recur(n - 1)
});
factorial(4);
console.log(memory); // [1, 1, 2, 6, 24]