单例模式
单例模式有两个特点: 全局性和唯一性.全局唯一性是指在全局环境中,只创建对象一次且保证对象在创建过后不被再次创建.下面我们就来演示一下Javascript语言的普通类单例模式和透明单例模式
普通类单例模式
function GetSingleton(name) {
this.name = name;
this.instance = null;
}
GetSingleton.prototype.getName = function() {
return this.name;
};
GetSingleton.prototype.Singleton = function(name) {
return this.instance || (this.instance = new GetSingleton(name));
};
let gary_one = GetSingleton.prototype.Singleton("gary_one");
let gary_two = GetSingleton.prototype.Singleton("gary_two");
//在这里打印:
//true
console.log(gary_one === gary_two);
普通类单例模式(闭包版本)
function GetSingleton(name) {
this.name = name;
}
GetSingleton.prototype.getName = function() {
return this.name;
};
let Singleton = (function() {
let instance;
return function(name) {
return instance || (instance = new GetSingleton(name));
}
})();
let gary_one = Singleton("gary_one");
let gary_two = Singleton("gary_two");
//在这里打印:
//true
console.log(gary_one === gary_two);
透明单例模式
let GetSingleton = (function() {
let instance;
return function(html) {
if(instance) {
return instance;
}
this.html = html;
this.init();
return instance = this;
}
})();
GetSingleton.prototype.init = function() {
let div = document.createElement("div");
div.innerHTML = this.html;
document.body.appendChild(div);
return div;
};
let gary_one = new GetSingleton("gary_one");
let gary_two = new GetSingleton("gary_two");
//页面上只有创造了一个DIV DOM节点,内容是"gary_one"
//在这里打印:
//true
console.log(gary_one === gary_two);
透明单例模式(代理模式版本)
function GetSingleton(html) {
this.html = html;
this.init();
}
GetSingleton.prototype.init = function() {
let div = document.createElement("div");
div.innerHTML = this.html;
document.body.appendChild(div);
}
let Singleton = (function() {
let instance;
return function(html) {
return instance || (instance = new GetSingleton(html));
}
})();
let gary_one = new Singleton("gary_one");
let gary_two = new Singleton("gary_two");
//页面上只有创造了一个DIV DOM节点,内容是"gary_one"
//在这里打印:
//true
console.log(gary_one === gary_two);
Javascript单例模式 ——— 单一对象
let obj_only = {
a() {
console.log("a");
},
b() {
console.log("b");
}
};
//在这里打印:
//a
obj_only.a();
//在这里打印:
//b
obj_only.b();
//动态空间对象
let MyApp = {};
MyApp.namespace = function(model) {
let parts = model.split("."),
prototype = MyApp;
for(let [key, value] of parts.entries()) {
if(!prototype[value]) {
prototype[value] = {};
}
prototype = prototype[value];
}
};
MyApp.namespace("model");
MyApp.namespace("div.style");
//在这里打印:
//{model: {}, div: {style: {}}}
console.log(MyApp);
//闭包
function Person() {
let _name = "Gary",
_age = 26;
return function() {
return `name: ${_name}, age: ${_age}`;
}
}
let Gary = Person();
//在这里打印:
//name: Gary, age: 26
console.log(Gary());
惰性单例模式
//首先演示的不是惰性单例模式,而是对于创造单个对象有哪几种方式
let GetSingleton = (function (){
let div = document.createElement("div");
div.innerHTML = "Gary";
div.style.display = "none";
document.body.appendChild(div);
return div;
})();
function Singleton() {
document.getElementById("open").onclick = function(e) {
let singletonDiv = GetSingleton;
singletonDiv.style.display = "block";
//取消冒泡事件
e.stopImmediatePropagation();
};
}
//这种情况下,无论调不调用Singleton方法,界面在最开始加载时,就已经进行了DOM操作,添加了一个DIV DOM节点,并把这个节点的内容设置为Gary,显示方式设置为none,这种方式的确时创造了单个对象且不可再次创造,但是缺点还是比较明显的,创造不创造单个对象跟绑定事件并没有关系,假如我在页面上并没有触发绑定的事件,页面上在一开始还是具有已经创造好的DIV DOM节点的
Singleton();
function GetSingleton() {
let div = document.createElement("div");
div.innerHTML = "Gary";
div.style.display = "none";
document.body.appendChild(div);
return div;
}
function Singleton() {
document.getElementById("open").onclick = function(e) {
let singletonDiv = GetSingleton();
singletonDiv.style.display = "block";
//取消冒泡
e.stopImmediatePropagation();
};
}
//这种情况下,满足了调用时再创造单个对象的条件,但是并不满足创造了单个对象并不可再次创造的条件,在这种情况下,每点击一次id为open的按钮一次,就会创造一个对象,这显然是不符合单例模式的特点的
Singleton();
let GetSingleton = (function() {
let res;
return function() {
if(!res) {
res = document.createElement("div");
res.innerHTML = "Gary";
res.style.display = "none";
document.body.appendChild(res);
}
return res;
}
})();
function Singleton() {
document.getElementById("open").onclick = function(e) {
let singletonDiv = GetSingleton();
singletonDiv.style.display = "block";
//取消冒泡
e.stopImmediatePropagation();
};
}
//这种才是真正的惰性单例模式,在调用绑定事件时才创造单个对象,且也满足创造了单个对象并不可再次创造的条件,但是还是有优化的空间的
Singleton();
惰性单例模式(优化版本)
function GetSingleton(func) {
let res;
return function() {
return res || (res = func.apply(this, arguments));
}
}
let singleton = GetSingleton(function() {
let div = document.createElement("div");
div.innerHTML = "Gary";
div.style.display = "none";
document.body.appendChild(div);
return div;
});
function Singleton() {
document.getElementById("open").onclick = function(e) {
let singletonDiv = singleton();
singletonDiv.style.display = "block";
//取消冒泡事件
e.stopImmediatePropagation();
};
}
//这是惰性单例模式的最终优化版本,最显著的特点时它的复用性,无论是创造任何的DOM节点,只用GetSingleton函数方法,外部传入一个func函数即可,立即满足创造单一对象之后不可再次创建的特点
Singleton();
惰性单例模式的应用
还记得jQuery工具库的.one函数方法嘛,它就是最经典的单例模式例子,我们今天来模拟一下
function GetSingleton(func) {
let res;
return function() {
return res || (res = func.apply(this, arguments));
}
}
let bindEvent = GetSingleton(function () {
document.getElementById("open").onclick = function(e) {
alert("Gary");
//取消冒泡
e.stopImmediatePropagation();
};
console.log("bind~");
return true;
});
function render() {
console.log("开始渲染~");
bindEvent();
}
//切实的实现了单例模式"一朝绑定,终生收益"的特性
//在这里打印:
//开始渲染~
//bind~
//开始渲染~
//开始渲染~
render();
render();
render();
策略模式
策略模式的主要条件有三个: 可扩展、可复用和可替换,就让我们先演示一个非策略模式下的例子,我们要计算年终奖,首先要知晓月薪基数,然后再知道奖励倍数,就可以计算出最后的年终奖,一般我们会这么写
function getPerformance(performance, salary) {
if(performance === "S") {
return salary * 4;
}
if(performance === "A") {
return salary * 3;
}
if(performance === "B") {
return salary * 2;
}
}
//在这里打印:
//80000
console.log(getPerformance("S", 20000));
//在这里打印:
//120000
console.log(getPerformance("A", 40000));
非策略模式的例子,存在很多缺点,首先,策略对象不可复用,策略对象逻辑全都写在一个函数对象里面,并不能拿出进行复用,其次,扩展很不方便,每一次扩展都必须去改变策略对象所处的逻辑函数对象,最后新的策略对象不可替换新的策略对象,接下来我们用传统的策略模式进行解决,首先将策略对象抽取到外面,形成一个抽象的对象
function PerformanceS() {
}
PerformanceS.prototype.getSalary = function(salary) {
return salary * 4;
};
function PerformanceA() {
}
PerformanceA.prototype.getSalary = function(salary) {
return salary * 3;
};
function PerformanceB() {
}
PerformanceB.prototype.getSalary = function(salary) {
return salary * 2;
};
function GetPerformanceSalary() {
this.performance = null;
this.salary = null;
}
GetPerformanceSalary.prototype.setPerformance = function(performance) {
this.performance = performance;
};
GetPerformanceSalary.prototype.setSalary = function(salary) {
this.salary = salary;
};
GetPerformanceSalary.prototype.getBonus = function() {
return this.performance.getSalary(this.salary);
};
let getPerformanceSalary = new GetPerformanceSalary();
getPerformanceSalary.setSalary(20000);
getPerformanceSalary.setPerformance(new PerformanceS());
//在这里打印:
//80000
console.log(getPerformanceSalary.getBonus());
getPerformanceSalary.setSalary(40000);
getPerformanceSalary.setPerformance(new PerformanceA());
//在这里打印:
//120000
console.log(getPerformanceSalary.getBonus());
策略模式下的策略对象,复用性、扩展性和替换性都很强,策略对象现在是以抽象函数的形式进行的定义,可以直接拿原有的抽象函数对象进行复用,也可以定义新的抽象函数对象进行扩展,在替换性方面,薪酬基数和策略对象模式都可根据set…方法的方式进行替换,并且得到不同的结果
Javascript策略模式
let categories = {
S(salary) {
return salary * 4;
},
A(salary) {
return salary * 3;
},
B(salary) {
return salary * 2;
}
};
function getPerformance(performance, salary) {
return categories[performance](salary);
}
//在这里打印:
//80000
console.log(getPerformance("S", 20000));
//在这里打印:
//120000
console.log(getPerformance("A", 40000));
策略模式 —— 表单验证
使用策略模式来进行表单验证,也是一种很好的做法,首先我们先不使用策略模式来编写表单验证
function validate() {
let userAgent = document.getElementById("user-agent");
if(userAgent.username.value === "") {
return "用户名不可为空";
}
if(userAgent.password.value.length !== 6) {
return "密码长度必须为6位";
}
if(!/^1(3|5|8)[\d]{9}$/.test(userAgent.phone.value)) {
return "手机号码不符合规范";
}
}
let userAgent = document.getElementById("user-agent");
userAgent.onsubmit = function(e) {
let errMsg = validate();
if(errMsg) {
console.log(errMsg);
}
//取消冒泡事件
e.stopImmediatePropagation();
//取消默认事件
e.preventDefault();
};
策略对象不可复用,全都处在validate函数作用域下,且不可扩展,扩展必须去更改validate源代码,不符合开放封闭原则,更不能替换在任何的表单校验上面,只能针对特定的表单(name为username,password,phone的表单)进行校验,所以不使用策略模式,效果极差,下面我们就使用策略模式来进行表单校验
let categories = {
isNonUsername(value, errMsg) {
if(value === "") {
return errMsg;
}
},
minLength(value, length, errMsg) {
if(value.length !== parseInt(length)) {
return errMsg;
}
},
isMobile(value, errMsg) {
if(!/^1(3|5|8)[\d]{9}$/.test(value)) {
return errMsg;
}
}
};
function Validate() {
this.cache = [];
}
Validate.prototype.add = function(dom, condition, errMsg) {
this.cache.push(function() {
let parts = condition.split(":");
let categories_condition = parts.shift();
parts.unshift(dom.value);
parts = [...parts, errMsg];
return categories[categories_condition].apply(dom, parts);
});
};
Validate.prototype.start = function() {
for(let [key, value] of this.cache.entries()) {
let validateFunc = value;
let errMsg = validateFunc();
if(errMsg) {
return errMsg;
}
}
};
function validate() {
let userAgent = document.getElementById("user-agent"),
new_validate = new Validate();
new_validate.add(userAgent.username, "isNonUsername", "用户名不可为空");
new_validate.add(userAgent.password, "minLength:6", "密码长度必须为6位");
new_validate.add(userAgent.phone, "isMobile", "手机号码不符合规范");
let errMsg = new_validate.start();
if(errMsg) {
return errMsg;
}
}
let userAgent = document.getElementById("user-agent");
userAgent.onsubmit = function(e) {
let errMsg = validate();
if(errMsg) {
console.log(errMsg);
}
//取消冒泡事件
e.stopImmediatePropagation();
//取消默认事件
e.preventDefault();
};
这样就利用策略模式的复用性、扩展性以及替换性完美的解决了表单验证,你这时可能会有疑问,假如表单里面一个表单项有多个验证条件怎么办,这套策略模式的方式不就不好使了嘛,别急,只要修改一下Validate.prototype.add函数方法,就可以既兼容一个还兼容多个验证条件
let categories = {
isNonUsername(value, errMsg) {
if(value === "") {
return errMsg;
}
},
minLength(value, length, errMsg) {
if(value.length !== parseInt(length)) {
return errMsg;
}
},
isMobile(value, errMsg) {
if(!/^1(3|5|8)[\d]{9}$/.test(value)) {
return errMsg;
}
}
};
function Validate() {
this.cache = [];
}
Validate.prototype.add = function(dom, rules) {
for(let [key, value] of rules.entries()) {
this.cache.push((function() {
let condition = value["condition"],
errMsg = value["errMsg"];
return function() {
let parts = condition.split(":");
let categories_condition = parts.shift();
parts.unshift(dom.value);
parts = [...parts, errMsg];
return categories[categories_condition].apply(dom, parts);
}
})());
}
};
Validate.prototype.start = function() {
for(let [key, value] of this.cache.entries()) {
let validateFunc = value;
let errMsg = validateFunc();
if(errMsg) {
return errMsg;
}
}
};
function validate() {
let userAgent = document.getElementById("user-agent"),
new_validate = new Validate();
new_validate.add(userAgent.username, [{
condition: "isNonUsername",
errMsg: "用户名不可为空"
}, {
condition: "minLength:10",
errMsg: "用户名长度必须为10位"
}]);
new_validate.add(userAgent.password, [{
condition: "minLength:6",
errMsg: "密码长度必须为6位"
}]);
new_validate.add(userAgent.phone, [{
condition: "isMobile",
errMsg: "手机号码不和规范"
}]);
let errMsg = new_validate.start();
if(errMsg) {
return errMsg;
}
}
let userAgent = document.getElementById("user-agent");
userAgent.onsubmit = function(e) {
let errMsg = validate();
if(errMsg) {
console.log(errMsg);
}
//取消冒泡事件
e.stopImmediatePropagation();
//取消默认事件
e.preventDefault();
};
这样就完美解决了多个验证条件的问题,这种策略模式可复用,可扩展,可替换,十分完美
代理模式
下面我们要介绍的是代理模式,顾名思义,代理模式就是通过代理层去替代实体对象处理一些信息,其实就是在保护实体对象,减少实体对象的职责,在过滤、处理、整合信息之后再交给实体对象,这样会使得实体对象很轻便,对于实体对象的扩展性、维护性以及替换性有很大的帮助
function Flower() {
}
let xiaoming = {
sendFlower(target) {
let flower = new Flower();
target.receiveFlower(flower);
}
};
let A = {
receiveFlower(flower) {
console.log("已收到花朵: " + flower);
}
};
let B = {
receiveFlower(flower) {
A.receiveFlower(flower);
}
};
//在这里打印:
//已收到花朵: [object Object]
xiaoming.sendFlower(B);
这样就形成了一个小明送给女神A花朵表达想和她恋爱的心意,又不太好意思亲自送,只好让他跟A共同的好友B来进行代理送,这就是一个典型的代理模式的例子,你可能会问,这样做有什么意义呢,只不过多了一层罢了,的确这样只是多添加了一层而已,但是假如我们再改编一下剧情,你就会感觉很有意义
function Flower() {
}
let xiaoming = {
sendFlower(target) {
let flower = new Flower();
target.receiveFlower(flower);
}
};
let A = {
receiveFlower(flower) {
console.log("已收到花朵: " + flower);
},
listenerBeHappy(func) {
let self = this;
setTimeout(function() {
func.apply(self);
}, 1000);
}
};
let B = {
receiveFlower(flower) {
A.listenerBeHappy(function () {
this.receiveFlower(flower);
});
}
};
//在这里打印:
//已收到花朵: [object Object]
xiaoming.sendFlower(B);
故事的剧情转变成了这样,小明送给女神A花朵表达想和她恋爱的心意,又不太好意思亲自送,只好让他跟A共同的好友B来进行代理送,B清楚A什么时间心情最好,什么时间可以接受别人对她的表白,所以B在A心情很好的时候将小明的花朵转交给女神A,虽然也不见得小明的追求会成功,但是B的代理工作充分表明了代理模式
虚拟代理
我们使用虚拟代理来写一个懒加载图片的例子,通常我们使用Js写加载图片时,一般会这么做
let loadImage = (function () {
let Image = document.createElement("img");
document.body.appendChild(Image);
return {
setSrc(src) {
Image.src = src;
}
}
})();
loadImage.setSrc("https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png");
但是这样做有一个用户体验的交互问题,图片还没有完全渲染的时候,图片区域是空白的,这时候我们需要使用代理来虚拟在图片还没有完全渲染完毕的时候,先在所在区域展示loading加载图,当图片完全渲染完毕之后,再将所在区域替换成图片
let loadImage = (function () {
let Image = document.createElement("img");
document.body.appendChild(Image);
return {
setSrc(src) {
Image.src = src;
}
}
})();
let proxyLoadImage = (function () {
let img = new Image();
img.onload = function() {
loadImage.setSrc(this.src);
};
return {
setSrc(src) {
loadImage.setSrc("../img/zy.jpeg");
img.src = src;
}
}
})();
proxyLoadImage.setSrc("https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png");
虚拟代理模式对于用户是无感的,用户不会知道你使用的是实体对象接口还是虚拟代理对象接口,且虚拟代理对象的复用性、扩展性以及可替换性都是很完美的,完全不会破坏原有的实体对象的代码结构,下面我们来写一个使用虚拟代理模式实现批量提交文件,节省浏览器和后台服务器资源的例子
function syncReadFile(id) {
console.log("开始上传文件: " + id);
}
let checkbox = document.getElementsByName("checkbox");
for(let i = 0; i < checkbox.length; i ++) {
checkbox[i].onclick = function(e) {
if(this.checked) {
syncReadFile(this.id);
}
//取消冒泡事件
e.stopImmediatePropagation();
};
}
这个例子,我每点一次复选框,且复选框选中,就会上传文件,在短时间内选中多个复选框,会造成大量的上传请求发出,频繁的调用上传接口,会大量浪费浏览器和后台服务器资源,所以我们设置一个函数节流函数来模拟syncReadFile函数,相当于是一个虚拟代理模式的函数,节省浏览器和后台服务器资源
function syncReadFile(id) {
console.log("开始上传文件: " + id);
}
let proxySyncReadFile = (function() {
let cache = [],
timer = null;
return function (id) {
let self = this;
cache = [...cache, id];
if(timer) {
return false;
}
timer = setTimeout(function () {
let id_transform = cache.join(",");
syncReadFile.call(self, id_transform);
clearTimeout(timer);
timer = null;
cache.length = 0;
}, 2000);
}
})();
let checkbox = document.getElementsByName("checkbox");
for(let i = 0; i < checkbox.length; i ++) {
checkbox[i].onclick = function (e) {
if(this.checked) {
proxySyncReadFile(this.id);
}
//取消冒泡事件
e.stopImmediatePropagation();
};
}
缓存代理
我们先来写一个缓存代理计算乘积的例子,当传入的参数与上一次传入的参数相同时,就使用缓存中的乘积结果,不需要再次进行计算
function getMultiple() {
let cache = {};
function multiple() {
let a = 1;
for(let i = 0; i < arguments.length; i ++) {
a *= arguments[i];
}
return a;
}
return function() {
let cache_prototype = Array.prototype.join.call(arguments, "_");
if(cache[cache_prototype]) {
return cache[cache_prototype];
}
console.log("again");
return cache[cache_prototype] = multiple.apply(this, arguments);
}
}
let multipleForReal = getMultiple();
//在这里打印:
//again
//168
console.log(multipleForReal(4, 6, 7));
//在这里打印:
//168
console.log(multipleForReal(4, 6, 7));
让我们将事情变得更有趣一些
function plus() {
let a = 0;
for(let i = 0; i < arguments.length; i ++) {
a += arguments[i];
}
return a;
}
function multiple() {
let a = 1;
for(let i = 0; i < arguments.length; i ++) {
a *= arguments[i];
}
return a;
}
function getMethod(func) {
let cache = {};
return function () {
let cache_prototype = Array.prototype.join.call(arguments, "_");
if(cache[cache_prototype]) {
return cache[cache_prototype];
}
console.log("again");
return cache[cache_prototype] = func.apply(this, arguments);
}
}
let getMethodResult = getMethod(plus);
//在这里打印:
//again
//20
console.log(getMethodResult(4, 8, 8));
//在这里打印:
//20
console.log(getMethodResult(4, 8, 8));
let getMethodResult_multiple = getMethod(multiple);
//在这里打印:
//again
//256
console.log(getMethodResult_multiple(4, 8, 8));
//在这里打印:
//256
console.log(getMethodResult_multiple(4, 8, 8));
迭代器模式
除了某一些古代的语言,现代的程序语言都实现了内置迭代器,迭代器模式或许从来都没有被人所听说过,我们今天就用JavaScript来模拟迭代器模式
function each(arr, func) {
for(let [key, value] of arr.entries()) {
func.call(this, key, value);
}
}
//在这里打印:
//0 2
//1 6
//2 8
each([2, 6, 8], function (index, item) {
console.log(index, item);
});
这只是内部迭代器模式,将迭代逻辑放在内部实现,但是这样做很不灵活,比如我要实现一个俩数组是否相等的函数compare,你会发现compare的实现方式并不美观,且很不灵活
function each(arr, func) {
for(let [key, value] of arr.entries()) {
func.call(this, key, value);
}
}
function compare(arr, arrAno) {
if(arr.length !== arrAno.length) {
throw new Error("两个数组不相同");
}
each(arr, function (index, item) {
if(item !== arrAno[index]) {
throw new Error("两个数组不相同");
}
});
console.log("两个数组是相同的");
}
//在这里打印:
//两个数组是相同的
compare([1, 2, 5], [1, 2, 5]);
//在这里报错:
//两个数组不相同
compare([1, 2, 8], [1, 3, 6]);
外部迭代模式
后来出现了外部迭代模式,代码如下,这种迭代模式,灵活的解决了外部传入迭代器时,一些比较复杂的需求,维护性、扩展性、灵活性都很高,且针对对象和数组,只要拥有length属性的对象都可以进行迭代
function Iterator(obj) {
let index = 0,
length = obj.length;
function next() {
index++;
}
function isDone() {
return index < length;
}
function getCurrentItem() {
return obj[index];
}
return {
length,
next,
isDone,
getCurrentItem
}
}
let iterator_arr = Iterator([55, 66, 99, 10, 1, 11, 28, 32]);
//在这里打印:
//55
//66
//99
//10
//1
//11
//28
//32
while(iterator_arr.isDone()) {
console.log(iterator_arr.getCurrentItem());
iterator_arr.next();
}
这样的话,我们来编写compare函数进行两个函数比对时,就会灵活,更可维护的多
function Iterator(obj) {
let index = 0,
length = obj.length;
function next() {
index++;
}
function getCurrentItem() {
return obj[index];
}
function isDone() {
return index >= length;
}
return {
length,
isDone,
getCurrentItem,
next
};
}
function compare(arr, arrAno) {
if(arr.length !== arrAno.length) {
throw new Error("两个数组不相同");
}
while(!arr.isDone() && !arrAno.isDone()) {
if(arr.getCurrentItem() !== arrAno.getCurrentItem()) {
throw new Error("两个数组不相同");
}
arr.next();
arrAno.next();
}
console.log("两个数组是相同的");
}
let iterator = Iterator([1, 6, 8, 10, 55, 25, 36, 96]);
let iterator_ano = Iterator([1, 6, 8, 10, 55, 25, 36, 96]);
let iterator_diff = Iterator([1, 6, 8, 10, 55, 25, 36, 96]);
let iterator_diff_ano = Iterator([1, 6, 8, 10, 55, 28, 36, 96]);
//在这里打印:
//两个数组是相同的
compare(iterator, iterator_ano);
//在这里报错:
//两个数组不相同
compare(iterator_diff, iterator_diff_ano);
jQuery中的each方法,就提供了迭代的功能,但是each方法不仅可以迭代数组,还可以迭代对象,我们来看一下each方法的源代码实现方式
$.each = function (obj, func) {
let value,
i = 0,
length = obj.length;
if(Object.prototype.toString.call(obj) === "[object Array]") {
for(; i < length; i++) {
value = func.call(this, i, obj[i]);
if(value === false) {
break;
}
}
} else {
for(i in obj) {
value = func.call(this, i, obj[i]);
if(value === false) {
break;
}
}
}
}
//在这里打印:
//0 5
//1 8
//2 10
$.each([5, 8, 10, 3, 66], function (index, item) {
if(item === 3) {
return false;
}
console.log(index, item);
});
这期间还使用了中断迭代器模式的判断语句 if(value === false) break;当函数返回false的时候,就将迭代器终止掉,直接break出for循环,下面我们来介绍反向迭代函数,我们分分钟搞定
function reverseEach(obj, func) {
let value;
for(let l = obj.length - 1; l >=0; l--) {
value = func.call(this, l, obj[l]);
if(value === false) {
break;
}
}
}
//在这里打印:
//4 101
//3 99
//2 66
reverseEach([10, 88, 66, 99, 101], function (index, item) {
if(item === 88) {
return false;
}
console.log(index, item);
});