持续输出js第6篇-(惰性函数和单例设计模式&&compose函数)

437 阅读4分钟

什么是单例设计模式

它是一种最初始的模块化思想

单例设计模式作用: 把描述同一件事务的属性和特征进行“分组、归类”(存储在同一 个堆内存空间中),因此避免了全局变量之间的冲突和污染

例子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); 

明天继续更