如何将网站提交给百度,定制小程序开发哪家公司好,南昌建设企业网站,深圳网站关键词排名查询MENU JavaScript之设计模式、单例、代理、装饰者、中介者、观察者、发布订阅、策略JavaScript之数组静态方法的实现、reduce、forEach、map、push、every JavaScript之设计模式、单例、代理、装饰者、中介者、观察者、发布订阅、策略
单例模式 概念 保证一个类仅有一个实例并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否如果存在则直接返回如果不存在就创建了再返回这就确保了一个类只有一个实例对象。 适用场景 一个单一对象。比如弹窗无论点击多少次弹窗只应该被创建一次。 代码实现
class CreateUser {constructor(name) {this.name name;this.getName();};getName() {console.log(this.name);};
};// 代理实现单例模式
let ProxyMode (function () {let instance null;return function (name) {if (!instance) instance new CreateUser(name);return instance;};
})();// 测试单体模式的实例
let a new ProxyMode(aaa);
console.log(a);
// CreateUser {name: aaa}let b new ProxyMode(bbb);
console.log(b);
// CreateUser {name: aaa}// 因为单体模式是只实例化一次
// 所以下面的实例是相等的
console.log(a b);
// true代理模式 概念 为一个对象提供一个代用品或占位符以便控制对它的访问。 常用的虚拟代理形式 某一个花销很大的操作可以通过虚拟代理的方式延迟到这种需要它的时候才去创建例使用虚拟代理实现图片懒加载。 图片懒加载的方式 先通过一张loading图占位然后通过异步的方式加载图片等图片加载好了再把完成的图片加载到img标签里面。 解释 使用代理模式实现图片懒加载的优点还有符合单一职责原则。减少一个类或方法的粒度和耦合度。 代码实现
let imgFunc (function() {// 创建一个img标签let imgNode document.createElement(img);// 把标签放到body上document.body.appendChild(imgNode);// 返回setSrc函数return {setSrc: function(src) {imgNode.src src;}};
})();let proxyImage (function() {// 创建一个img标签let img new Image();// 给img标签添加自执行函数img.onload function() {imgFunc.setSrc(this.src);};return {setSrc: function(src) {imgFunc.setSrc(/img/loading02.gif);setTimeout(() {img.src src;}, 1000);}};
})();console.log(proxyImage);
// {setSrc: ƒ}proxyImage.setSrc(/img/08.jpg);装饰者模式 概念 在不改变对象自身的基础上在程序运行期间给对象动态地添加方法。 例如 现有4种型号的自行车分别被定义成一个单独的类如果给每辆自行车都加上前灯、尾灯、铃铛这3个配件如果用类继承的方式需要创建4 * 3 12个子类。但如果通过装饰者模式只需要创建3个类。 适用的场景 原有方法维持不变在原有方法上再挂载其他方法来满足现有需求函数的解耦将函数拆分成多个可复用的函数再将拆分出来的函数挂载到某个函数上实现相同的效果但增强了复用性。 用AOP装饰函数实现装饰者模式
Function.prototype.before function(beforefn) {// 保存原函数引用let self this;// 返回包含了原函数和新函数的代理函数return function() {// 执行新函数修正thisbeforefn.apply(this, arguments);// 执行原函数return self.apply(this, arguments);};
};Function.prototype.after function(afterfn) {let self this;return function() {let ret self.apply(this, arguments);afterfn.apply(this, arguments);return ret;};
};let func function() {console.log(2);
};// func1和func3为挂载函数
let func1 function() {console.log(1);
};let func3 function() {console.log(3);
};func func.before(func1).after(func3);
func();
// 1 2 3中介者模式 概念 通过一个中介者对象其他所有的相关对象都通过该中介者对象来通信而不是相互引用当其中的一个对象发生改变时只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。 例如 现实生活中航线上的飞机只需要和机场的塔台通信就能确定航线和飞行状态而不需要和所有飞机通信。同时塔台作为中介者知道每架飞机的飞行状态所以可以安排所有飞机的起降和航线安排。 适用的场景 例如购物车需求存在商品选择表单、颜色选择表单、购买数量表单等都会触发change事件那么可以通过中介者来转发处理这些事件实现各个事件间的解耦仅仅维护中介者对象即可。 html
divdivspan选择颜色: /spanselect idcolorSelectoption value请选择/optionoption valuered红色/optionoption valueblue蓝色/option/select/divdivspan选择内存: /spanselect idmemorySelectoption value请选择/optionoption value32G32G/optionoption value16G16G/option/select/divdivspan输入购买数量: /spaninput typetext idnumberInput //divdivspan您选择了颜色: /spanspan idcolorInfo/span/divdivspan您选择了内存: /spanspan idmemoryInfo/span/divdivspan您输入了数量: /spanspan idnumberInfo/span/divdivbutton idnextBtn disabledtrue请选择手机颜色、内存和购买数量/button/div
/divscript src./index.js/scriptJavaScript
// 手机库存数据
let goods { red|32G: 3, red|16G: 0, blue|32G: 1, blue|16G: 6 };// 中介者对象
let mediator (function () {let colorSelect document.getElementById(colorSelect),memorySelect document.getElementById(memorySelect),numberInput document.getElementById(numberInput),colorInfo document.getElementById(colorInfo),memoryInfo document.getElementById(memoryInfo),numberInfo document.getElementById(numberInfo),nextBtn document.getElementById(nextBtn),regNumber /^[1-9]{1}[0-9]{0,2}$/;return {changed: function (obj) {// 颜色let color colorSelect.value,// 内存memory memorySelect.value,// 数量number numberInput.value,// 颜色和内存对应的手机库存数量stock goods[${color}|${memory}];// 如果改变的是选择颜色下拉框if (obj colorSelect) {colorInfo.innerHTML color;} else if (obj memorySelect) {memoryInfo.innerHTML memory;} else if (obj numberInput) {numberInfo.innerHTML number;}if (!color) return (nextBtn.disabled true, nextBtn.innerHTML 请选择手机颜色);if (!memory) return (nextBtn.disabled true, nextBtn.innerHTML 请选择内存大小);if (!regNumber.test(number)) return (nextBtn.disabled true, nextBtn.innerHTML 请输入正确的购买数量);if (number stock) return (nextBtn.disabled true, nextBtn.innerHTML 库存不足);nextBtn.disabled false;nextBtn.innerHTML 放入购物车;}}
})();// 事件函数
// id选择器可以直接绑定事件不需要特意获取。
colorSelect.onchange function () {mediator.changed(this);
};
memorySelect.onchange function () {mediator.changed(this);
};
numberInput.oninput function () {mediator.changed(this);
};相关链接 1、掘金-原文 html
divspan选择颜色:/spanselect idcolorSelectoption value请选择/optionoption valuered红色/optionoption valueblue蓝色/option/select
/div
divspan输入购买数量: /spaninput typetext idnumberInput /
/divdivspan您选择了颜色: /spanspan idcolorInfo/span
/div
divspan您输入了数量: /spanspan idnumberInfo/span
/div
divbutton idnextBtn disabledtrue请选择手机颜色和购买数量/button
/divscript src./index.js/scriptJavaScript
var colorSelect document.getElementById(colorSelect),numberInput document.getElementById(numberInput),colorInfo document.getElementById(colorInfo),numberInfo document.getElementById(numberInfo),nextBtn document.getElementById(nextBtn);// 手机库存数据
var goods {red: 3,blue: 6
};colorSelect.onchange function () {// 获取选中的颜色值var color this.value,// 数量number numberInput.value,// 该颜色手机对应的当前库存stock goods[color];console.log(color, number);// 赋值colorInfo.innerHTML color;if (!color) {nextBtn.disabled true;nextBtn.innerHTML 请选择手机颜色;return;}// 用户输入的购买数量是否为正整数if (((number - 0) | 0) ! number - 0) {nextBtn.disabled true;nextBtn.innerHTML 请输入正确的购买数量;return;}// 当前选择数量超过库存量if (number stock) {nextBtn.disabled true;nextBtn.innerHTML 库存不足;return;}nextBtn.disabled false;nextBtn.innerHTML 放入购物车;
};numberInput.oninput function () {// 颜色var color colorSelect.value,// 数量number this.value,// 该颜色手机对应的当前库存stock goods[color];numberInfo.innerHTML number;if (!color) {nextBtn.disabled true;nextBtn.innerHTML 请选择手机颜色;return;}// 输入购买数量是否为正整数if (((number - 0) | 0) ! number - 0) {nextBtn.disabled true;nextBtn.innerHTML 请输入正确的购买数量;return;}// 当前选择数量没有超过库存量if (number stock) {nextBtn.disabled true;nextBtn.innerHTML 库存不足;return;}nextBtn.disabled false;nextBtn.innerHTML 放入购物车;
};观察者模式
// 有一家猎人工会
// 其中每个猎人都具有发布任务publish
// 订阅任务subscribe的功能
// 他们都有一个订阅列表来记录谁订阅了自己
// 定义一个猎人类
// 包括姓名级别订阅列表function Hunter(name, level) {this.name name;this.level level;this.list [];
};// 在Hunter原型上添加publish方法
Hunter.prototype.publish function(money) {console.log(this.level 猎人 this.name 寻求帮助);this.list.forEach(function(item, index) {item(money);});
};// 在Hunter原型上添加subscribe方法
Hunter.prototype.subscribe function(targrt, fn) {console.log(this.level 猎人 this.name 订阅了 targrt.name);targrt.list.push(fn);
};// 猎人工会走来了几个猎人
let hunterMing new Hunter(小明, 黄金);
let hunterJin new Hunter(小金, 白银);
let hunterZhang new Hunter(小张, 黄金);
let hunterPeter new Hunter( Peter , 青铜);// Peter等级较低
// 可能需要帮助
// 所以小明小金小张都订阅了Peter
hunterMing.subscribe(hunterPeter, function(money) {console.log(小明表示 (money 200 ? : 暂时很忙不能给予帮助));
});hunterJin.subscribe(hunterPeter, function() {console.log(小金表示给予帮助);
});hunterZhang.subscribe(hunterPeter, function() {console.log(小金表示给予帮助);
});// Peter遇到困难赏金198寻求帮助
hunterPeter.publish(198);发布订阅模式 版本一
// 定义一家猎人工会
// 主要功能包括任务发布大厅topics
// 以及订阅任务subscribe
// 发布任务publish
let HunterUnion {// 任务发布大厅topics: Object.create(null),// 发布任务publishpublish: function (topic, money) {if (!this.topics[topic]) return false;for (let fn of this.topics[topic]) fn(money);},// 订阅任务subscribesubscribe: function (topic, fn) {if (!this.topics[topic]) this.topics[topic] [];this.topics[topic].push(fn);},
};// 定义一个猎人类
// 包括姓名级别
function Hunter(name, level) {this.name name;this.level level;
}// 订阅
Hunter.prototype.subscribe function (topic, fn) {console.log(this.level 猎人 this.name 订阅了狩猎 topic 的任务。);HunterUnion.subscribe(topic, fn);
};// 发布
Hunter.prototype.publish function (topic, money) {console.log(this.level 猎人 this.name 发布了狩猎 topic 的任务。);HunterUnion.publish(topic, money);
};// 猎人工会走来了几个猎人
let hunterMing new Hunter(小明, 黄金);
let hunterJin new Hunter(小金, 白银);
let hunterZhang new Hunter(小张, 黄金);
let hunterPeter new Hunter(Peter, 青铜);// Peter发布了狩猎tiger的任务
hunterPeter.publish(tiger, 198);// 小明小金小张分别订阅了狩猎tiger的任务
hunterMing.subscribe(tiger, function (money) {console.log(小明表示 (money 200 ? : 不) 接取任务。, money);
});hunterJin.subscribe(tiger, function (money) {console.log(小金表示接取任务。, money);
});hunterZhang.subscribe(tiger, function (money) {console.log(小张表示接取任务。, money);
});// 猎人们发布 (发布者) 或订阅 (观察者/订阅者)
// 任务都是通过猎人工会 (调度中心) 关联起来的
// 他们没有直接的交流。版本二
class PublishAndSubscribe {constructor() {// 收集订阅信息调度中心this.eventList {};}// 收集打包传入的数据// attributeName属性名// datas数据// fn 事件collects(attributeName, datas, fn) {if (!(this.eventList[attributeName] instanceof Array)) this.eventList[attributeName] [];this.eventList[attributeName].push({ datas, fn });}// 通过属性名触发对应的事件// attributeName属性名release(attributeName) {if (!this.eventList[attributeName].length) throw 出错啦;this.eventList[attributeName].forEach((item) item.fn(item.datas));}// 通过属性名找到对应的属性下的数组// 通过id移出对应的数组项off(attributeName, id) {this.eventList[attributeName].forEach((item, index) {if (item.datas.id id) this.eventList[attributeName].splice(index, 1);});}
}let publishAndSubscribe new PublishAndSubscribe();// 收集属性
// attributeName1attributeName2attributeName3
// 并且给它们绑定值和事件
publishAndSubscribe.collects(attributeName1,{ id: 1, content: A4 },function (datas) {console.log(接收发布的数据:, datas);}
);
publishAndSubscribe.collects(attributeName2,{ id: 2, content: { a: 1, b: 6, c: 4 } },function (datas) {console.log(接收发布的数据:, datas);}
);
publishAndSubscribe.collects(attributeName3,{id: 3,content: [1, 9, 6, 3],},function (datas) {console.log(接收发布的数据:, datas);}
);
publishAndSubscribe.collects(attributeName4,{id: 4,content: array,},function (datas) {console.log(接收发布的数据:, datas);}
);// 通过属性触发对应的事件
publishAndSubscribe.release(attributeName1);
publishAndSubscribe.release(attributeName2);
publishAndSubscribe.release(attributeName3);
console.log(publishAndSubscribe:, publishAndSubscribe.eventList);// 移除
publishAndSubscribe.off(attributeName4, 4);
console.log(publishAndSubscribe:, publishAndSubscribe.eventList);
// publishAndSubscribe.release(attributeName4);
// Error: Failed to resolve async component default: 出错啦版本三
let publishAndSubscribe {eventList: {},// 订阅add(propertyName, datas, listener) {if (!this.eventList[propertyName]) this.eventList[propertyName] [];this.eventList[propertyName].push({ datas, listener });},// 发布triggle(propertyName) {this.eventList[propertyName] this.eventList[propertyName].forEach((item) item.listener(item.datas));},// 移除removes(propertyName, fn) {if (!this.eventList[propertyName]) return false;let index this.eventList[propertyName].findIndex((listener) listener fn);this.eventList[propertyName].splice(index, 1);},
};let event1 (data) {console.log(数据:, data);
};
let event2 (data) {console.log(数据:, data);
};
let event3 (data) {console.log(数据:, data);
};let datas [1, 2, 3, 4, 5];// 订阅
publishAndSubscribe.add(property1, datas, event1);
publishAndSubscribe.add(property2, [9, 8, 6, 7, 3], event2);
publishAndSubscribe.add(property3, [], event3);// 移除
publishAndSubscribe.removes(property2, event2);// 发布
publishAndSubscribe.triggle(property1);
publishAndSubscribe.triggle(property2);
publishAndSubscribe.triggle(property3);策略模式 概念 定义一系列的算法把他们一个个封装起来并且使他们可以相互替换。 目的 策略模式的目的就是将算法的使用和算法的实现分离开。 解释 一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类可变策略类封装了具体的算法并负责具体的计算过程。第二个部分是环境类Context不变Context接受客户的请求随后将请求委托给某一个策略类。要做到这一点说明Context中要维持对某个策略对象的引用。 代码实现
// 策略类
let levelOBJ {funA: function(money) {return money * 5;},funB: function(money) {return money * 3;},funC: function(money) {return money * 2;}
};// 环境类
let calculateBouns function(level, money) {return levelOBJ[level](money);
};console.log(calculateBouns(funA, 10));
// 50
console.log(calculateBouns(funB, 20));
// 60
console.log(calculateBouns(funC, 30));
// 60JavaScript之数组静态方法的实现、reduce、forEach、map、push、every
Array.prototype.myjoin 概念 join()方法将一个数组或一个类数组对象的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目那么将返回该项目而不使用分隔符。 MDN链接地址 MDN - join 示例代码
let arrayData [1, null, [2], string, [], { sname: 3 }, null, {}];Array.prototype.myjoin function(separator) {// 如果 separator 是字符串类型// 赋值为 separator // 否则赋值为 , 。separator typeof separator string ? separator : ,;// 获取 this 的长度。let len this.length;// 初始化一个字符串let str ;// 如果 len 等于 0 // 返回空字符串if (!len) return str;// 初始化 while 循环条件let i 1;// 如果 this 的长度等于 1 // 直接返回且不加 , 。str this[0] ? this[0].toString() : ;while (i len) {str separator (this[i] ? this[i].toString() : );i;};return str;
};console.log(arrayData.myjoin());
// 1,,2,string,,[object Object],,[object Object]console.log(arrayData.myjoin(,));
// 1,,2,string,,[object Object],,[object Object]console.log(arrayData.myjoin(_));
// 1__2_string__[object Object]__[object Object]console.log(arrayData.myjoin(:));
// 1::2:string::[object Object]::[object Object]Array.prototype.myfindIndex 概念 findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引下标。若没有找到对应元素则返回-1。 MDN链接地址 MDN - findIndex 示例代码
let arrayData [4, 6, 8, 12];Array.prototype.myfindIndex function(callback, context) {// 获取第二个参数// 即this指向。// 如果有直接使用// 否则指向windowcontext context || window;// 获取this的长度。let len this.length;// 初始化while的值。let i 0;while (i len) {// 调用函数if (callback.call(context, this[i], i, this)) return i;i;};return -1;
};let fun function(item) { return item this.svalue 10;
};console.log(arrayData.myfindIndex(fun, { svalue: 5 }));
// 1Array.prototype.myreduce 概念 reduce()方法对数组中的每个元素执行一个由您提供的reducer函数升序执行将其结果汇总为单个返回值。 MDN链接地址 MDN - reduce 功能函数
Array.prototype.myreduce function(callback, initialValue) {const len this.length;let k 0;// 如果this的长度等于0// 抛出错误if (len 0) {throw new TypeError(this is null or not defined);};// 如果callback的类型不是function函数类型// 抛出错误if (typeof callback ! function) {throw new TypeError(callback is not a function);};// 如果initialValue的值为undefined// 拿出this里面的第一个值作为累加值// 同时把k // 因为this[k]需要返回两个值// 第一个值是initialValue// 第二个值是this[k]// 如果initialValue的值不为undefined// 直接返回initialValue和this[k]// 此时this[k] k 0;if (initialValue undefined) {initialValue this[k];};// 如果initialValue的值不是数字或者字符串类型的数字// 抛出错误if (!/^(-?\d)(\.\d)?$/.test(initialValue)) {throw new TypeError(initialValue is not number);};while (k len) {// 如果this中的值不是数字或者字符串类型的数字// 抛出错误if (!/^(-?\d)(\.\d)?$/.test(this[k])) {throw new TypeError(this[k] is not number);};// 如果k的值在this中有对应的下标// 就继续执行// 否则退出if (k in this) {// Number(initialValue)把字符串类型的数字转为纯数字// Number(this[k])把字符串类型的数字转为纯数字// 回调函数的作用是将循环出来的数据返回到外面initialValue callback.call(undefined, Number(initialValue), Number(this[k]), k, this);};k;};return initialValue;
};函数调用
// 纯数组求和
let sumNumF function(item, num) {return item num;
},dataNum [2, 3.1, 2.2];console.log(dataNum.myreduce(sumNumF, 3.1));
// 10.399999999999999// 数组对象求和
function sumObjF(item, num) {// Math.round(num)四舍五入return item Math.round(num);
};let sumObj [{id: 1,value: 2
}, {id: 2,value: 1
}, {id: 3,value: 1.4
}, {id: 4,value: 2.6
}];console.log(sumObj.map((item) {return item.value;
}).myreduce(sumObjF)); // 7Array.prototype.myforEach 概念 forEach()方法对数组的每个元素执行一次给定的函数。 MDN链接地址 MDN - forEach 功能函数
Array.prototype.myforEach function() {const len this.length;// 获取传入的第一参数// 回调函数let callback arguments[0] || this;// 获取传入的第二个参数// 需要指向的this值let thisArg arguments[1] || this;// 如果this的长度为0抛出错误if (len 0) {throw new TypeError(this is null or not defined);};// 如果传入的callback不是函数抛出错误if (typeof callback ! function) {throw new TypeError(callback is not a function);};let k 0;while (k len) {// if in this对象中是否含有k属性// if(age in data) data对象中是否含有age属性if (k in this) {// this[k] --- item// k --- i// this --- data// 循环调用传进来的函数// call改变this的指向// 回调函数的作用是将循环出来的值返回到外面callback.call(thisArg, this[k], k, this);};k;};
};函数调用
[10, 50, 90].myforEach((item, i, data) {console.log(item);console.log(i);console.log(data);// 注意使用箭头函数时// this.a的值为undefinedconsole.log(this.a); }, {a: 1
});[10, 50, 90].myforEach(function(item, i, data) {console.log(item);console.log(i);console.log(data);// 注意 : 不使用箭头函数时// this.a的值为传入的值console.log(this.a);}, {a: 1
});Array.prototype.mymap 概念 map()方法创建一个新数组其结果是该数组中的每个元素是调用一次提供的函数后的返回值。 MDN链接地址 MDN - map 功能函数
Array.prototype.mymap function(callback, thisPointTo) {const len this.length;if (len 0) {throw new TypeError(this is null or not defined);};if (typeof callback ! function) {throw new TypeError(callback is not a function);};// 定义返回数组let result [],i 0;// 使用for循环遍历数据for (; i len; i) {if (i in this) {// 调用回调函数并传入新数组result[i] callback.call(thisPointTo, this[i], i, this);};};// 返回新数组return result;
};函数调用
let returnMap [10, 50, 90].mymap((item, i, data) {console.log(item);console.log(i);// 原始值不变console.log(data); // [10, 50, 90]// 注意 : 使用箭头函数时// this.a 的值为 undefinedconsole.log(this.thisPointTo);item 100;return item;
}, {thisPointTo: 1
});
console.log(returnMap, returnMap); // returnMap (3) [100, 100, 100]let returnMap [10, 50, 90].mymap(function(item, i, data) {console.log(item);console.log(i);// 原始值不变console.log(data); // [10, 50, 90]// 注意 : 不使用箭头函数时// this.a的值为传入的值console.log(this.thisPointTo);// 返回值item 100;return item;
}, {thisPointTo: 1
});
console.log(returnMap, returnMap); // returnMap (3) [100, 100, 100]Array.prototype.mypush 概念 push()方法将一个或多个元素添加到数组的末尾并返回该数组的新长度。 MDN链接地址 MDN - push 功能函数
Array.prototype.mypush function() {// 初始化被push的数组长度// 如果长度不是0// 给len赋值为this.length;// 否则赋值为0let len this.length ? this.length : (this.length 0) 0;// 作用逐一获取传进来的值let i 0;while (i arguments.length) {// 通过长度赋值给this数组。// 也就是向数组末尾添加元素。// 自带返回值this[len] arguments[i];i;// 同时this数组的长度也要;len;}// 给this数组的length属性重新赋值this.length len;// 返回长度return this.length;
};函数执行
let arrayData [3, string];
console.log(arrayData);
// [3, string]arrayData.mypush(1, 字符串, { sname: object 对象 }, [array 数组]);console.log(arrayData);
// [3, string, 1, 字符串, {…}, Array(1)]Array.prototype.myevery 概念 every()方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。 注意若收到一个空数组此方法在一切情况下都会返回true。 MDN链接地址 MDN - every 功能函数
Array.prototype.myevery function(callback) {// 初始值为truelet isEvery true;// 获取this的长度let len this.length;// 初始化index let i 0;// 获取第二个参数// this是防止报错。// 第二个参数是一个对象// 作用改变this指向。let context arguments[1] || this;while (i len) {if (!callback.call(context, this[i], i, this)) {isEvery false;break;};i;}return isEvery;
};传入函数
let fun function(item, i) {console.log(item this.svaleu);// 输出3次truereturn item this.svaleu;
};执行
let arrayData [2, 3, 5];
console.log(arrayData.myevery(fun, { svaleu: 1 }));
// true