什么是单例设计模式
它是一种最初始的模块化思想
单例设计模式作用: 把描述同一件事务的属性和特征进行“分组、归类”(存储在同一 个堆内存空间中),因此避免了全局变量之间的冲突和污染
例子1
var name ="小a";
var age = 18;
var sex ="girl";
var name ="小b";
var age=19;
var sex ="boy";
name、age,sex上下相互冲突
我们看看下面这样就是单例设计模式
var person1= {
name: "小a"
age: 18
};
var person2= {
name : "小b",
age: 19
};
图解:

我们再来看下下面的例子,也是单例设计模式
let weatherModule = function () {
let city='beijing'
};
let newsModule = function () {
let city='shanghai'
};
如果这样搞的话,两个函数里面的city会相互冲突,let下不允许重名
例子2
/* 单例设计模式(最原始的模块化思想) */
let weatherModule = (function () {
let _default = 'beijing';
let queryWeather = function () {
//...
};
let setCity = function () {
//...
};
//假如另外一个人写的代码也想用setcity函数,怎么办呢?那就把这个函数暴露出去,return!!
return {
//ES6 setCity:setCity
setCity
};
})();
看看下面的函数,里面就可以调用上面函数的setcity方法
let newsModule = (function () {
let _default = 'beijing';
let queryNews = function () {
//...
weatherModule.setCity();//调用上面函数的setcity方法
};
return {};//不需要暴露的时候也先把这个结构搭建出来
})();
我们把这种模式也叫做单例设计模式!
至于为什么说是惰性函数,因为第一个函数执行完之后,就保存了栈和堆,不销毁
惰性函数案例:DOM2事件绑定
- 元素.addEventListener() es5
- 元素.attachEvent() es678
普通版
function emit(element, type, func) {
if (element.addEventListener) {
element.addEventListener(type, func, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, func);
} else {
element['on' + type] = func;
}
}
emit(box, 'click', fn1);
emit(box, 'click', fn2);
这样有个坏处,就是每次在全局下执行emit的时候,都要进一次循环体
升级版(封装思想,闭包)
function emit(element, type, func) {
if (element.addEventListener) {
emit = function (element, type, func) {
element.addEventListener(type, func, false);
};//这里和上面那个情况不一样,直接封装成小函数赋给emit
} else if (element.attachEvent) {
emit = function (element, type, func) {
element.attachEvent('on' + type, func);
};
} else {
emit = function (element, type, func) {
element['on' + type] = func;
};
}
emit(element, type, func);
}
emit(box, 'click', fn1);
emit(box, 'click', fn2);
可以看到我们直接让emit在大函数里直接等于小函数,这样那些小函数就被全局变量emit占用,小函数的堆销毁不了,大函数的栈销毁不了。
所以无论我们调用几次emit函数,除了第一次会进循环,后面的都不用进循环,直接调用小函数,因为它们都指向同一个地址,且这个地址不会销毁(小函数堆),
但是它们执行的时候形成的私有作用域不一样哦!!!
所以这也叫惰性函数,做判断这件事,我只做一次。。
compose函数实现函数调用扁平化
let fn1 = function (x) {
return x + 10;
};
let fn2 = function (x) {
return x * 10;
};
let fn3 = function (x) {
return x / 10;
};
普通版
console.log(fn3(fn1(fn2(fn1(5))))); //=> ((5+10)*10+10)/10 => 16
升级版:这就叫函数调用扁平化,没有那么多层级嵌套了
console.log(compose(fn1, fn2, fn1, fn3)(5)); //=>16
它的执行规律:从左边第一个函数开始执行
compose()(5); //=>5
(compose(fn1)(5); //=>5+10 = 15
compose(fn1, fn2)(5); //=>fn1(5)=15 fn2(15)=150
compose函数原理,其实也运用到了柯理化函数的思想
function compose(...funcs) {
//=>funcs:传递的函数集合
return function proxy(...args) {
//=>args:第一次调用函数传递的参数集合
let len = funcs.length;
if (len === 0) {
//=>一个函数都不需要执行,直接返回ARGS
return args;
}
if (len === 1) {
//=>只需要执行一个函数,把函数执行,把其结果返回即可
return funcs[0](...args);
}
return funcs.reduce((x, y) => {
return typeof x === "function" ? y(x(...args)) : y(x);
});
};
}
我们来了解一下里面的reduce函数的使用原理
let arr = [12, 23, 34, 45];
let total = arr.reduce((x, y) => {
//=>x:上一次处理的结果
//=>y:数组中某一项
//reduce不传第二个参数:x初始值是第一项的值,然后每一次返回的结果作为下一次x的值
//reduce传第二个参数:x初始值就是第二个参数值
console.log(x, y);
return x + y;
}, 0);
console.log(total);
明天继续更