本文介绍一个基于 Base2 框架思路的 JavaScript 通用 forEach 方法。该方法兼容数组、字符串、普通对象及类数组(如 arguments),并解决了原生 forEach 无法中途退出的问题。通过返回 false 即可实现类似 break 的功能,是构建轻量级工具库的实用基础。
通用 forEach 遍历方法介绍
鉴于之前的版本存在较多错误且排版混乱,此处进行了重新编写,恳请读者支持。
在开发过程中,循环遍历元素是最常见的需求之一。下面我们将探讨一个结合了 Base2 框架思想与 jQuery 风格的实现版本。
var forEach = (function(){
//数组与伪数组的遍历
var _Array_forEach = function (array, block, context) {
if (array == null) return;
//对String进行特殊处理
if(typeof array == 'string'){
array = array.split('');
}
var i = 0,length = array.length;
for (;i < length && block.call(context, array[i], (i+1), array)!==false; i++) {}
};
//对象的遍历
var _Function_forEach = function (object, block, context) {
for (var key in object) {
//只遍历本地属性
if (object.hasOwnProperty(key)&&block.call(context, object[key], key, object)===false){
break;
}
}
};
return function(object, block, context){
if (object == null) return;
if (typeof object.length == "number") {
_Array_forEach(object, block, context);
}else{
_Function_forEach(object, block, context);
}
};
})()
该函数逻辑并不复杂,但设计十分精巧。文中添加了简要注释,相信读者能够轻松理解其原理。
接下来通过具体示例进行演示。
//1:1 \n 2:2
forEach([1,2,3,4,5],function(el,index){
if(index>2){
return false;
}
alert(index+":"+el);
});
function print(el,index){
alert(index+":"+el);
}
//a:a \n b:b \n c:c
forEach({a:'a',b:'b',c:'c'},print);
//1:笨 \n 2:蛋 \n 3:的 \n 4:座 \n 5:右 \n 6:铭
forEach("笨蛋的座右铭",print);
function Person(name, age) {
this.name = name || "";
this.age = age || 0;
};
Person.prototype = new Person;
var fred = new Person("笨蛋的座右铭", 25);
fred.language = "chinese";//极晚绑定
//name:jxl \n age:22 \n language:chinese
forEach(fred,print);
注意:回调函数中的 index 参数下标从 1 开始计数。
为何不直接使用内置的 forEach
为何不直接使用内置的 forEach?
与 getElementsByClassName 类似,内置的 forEach 效率很高,但功能上存在局限:它无法在循环中途退出。而在本文介绍的 forEach 中,可以通过在处理函数内返回 false 来退出循环,从而具备更高的灵活性。
关于特殊的 length 属性
关于特殊的 length 属性。
length 属性是一个值得关注的特性。提到数组时,大家自然会联想到 length;那么当看到一个带有 length 属性的对象时,应联想到“伪数组”(或称类数组)。简单来说,伪数组是指那些拥有 length 属性,且能通过 Array.prototype.slice 转换为真正数组的对象。JavaScript 中最著名的伪数组是 arguments 对象。关于伪数组的讨论还有很多,未来可能会专门撰文详解。目前只需记住:除非明确打算将其作为伪数组使用,否则不要随意给对象赋予 length 属性。
我认为这个函数是简单 JavaScript 工具库中的必备组件。它是整个库的基石,在此基础上进行再封装,可以让你的库更加强大且结构优美。
Base2 框架中的 forEach 实现
以下是补充说明部分。
在 Base2 框架中,有一个名为 forEach 的函数,这是我见过的最佳实现之一。现将其提取并分析如下:它能对普通对象、字符串、数组以及类数组进行遍历。如果目标浏览器对象已实现此函数,则直接调用该原生函数。
function forEach(object, block, context, fn) {
if (object == null) return;
if (!fn) {
if (typeof object == "function" && object.call) {
//遍历普通对象
fn = Function;
} else if (typeof object.forEach == "function" && object.forEach != arguments.callee) {
//如果目标已经实现了forEach方法,则使用它自己的forEach方法(如标准游览器的Array对象)
object.forEach(block, context);
return;
} else if (typeof object.length == "number") {
// 如果是类数组对象或IE的数组对象
_Array_forEach(object, block, context);
return;
}
}
_Function_forEach(fn || Object, object, block, context);
};
function _Array_forEach(array, block, context) {
if (array == null) return;
var i = 0,length = array.length;
if (typeof array == "string") {
for (; i < length; i++) {
block.call(context, array.charAt(i), i, array);
}
}else{
for (;i < length; i++) {
block.call(context, array[i], i, array);
}
}
};
_Function_forEach = function(fn, object, block, context) {
// 这里的fn恒为Function
for (var key in object) {
//只遍历本地属性
if (object.hasOwnProperty(key)) {
//相当于 block(object[key], key)
block.call(context, object[key], key, object);
}
}
};
以下是原作者提供的一些示例(通过外部渠道获取):
function print(el,index){
alert(index+" : "+el)
}
forEach ([1, 2, 3], print);
forEach ({a: "aa", b: "bb",c: "cc"}, print);
forEach ("司徒正美", print);
forEach(document.styleSheets,function(el){
if(el.href) alert(el.href)
});
司徒正美的相关实现。
function forEach(object, block, context, fn) {
if (object == null) return;
if (!fn) {
if (typeof object == "function" && object.call) {
//遍历普通对象
fn = Function;
} else if (typeof object.forEach == "function" && object.forEach != arguments.callee) {
//如果目标已经实现了forEach方法,则使用它自己的forEach方法(如标准游览器的Array对象)
object.forEach(block, context);
return;
} else if (typeof object.length == "number") {
// 如果是类数组对象或IE的数组对象
_Array_forEach(object, block, context);
return;
}
}
_Function_forEach(fn || Object, object, block, context);
};
function _Array_forEach(array, block, context) {
if (array == null) return;
var i = 0,length = array.length;
if (typeof array == "string")
array = array.split("");
for (;i < length; i++) {
block.call(context, array[i], i, array);
}
};
_Function_forEach = function(fn, object, block, context) {
// 这里的fn恒为Function
for (var key in object) {
//只遍历本地属性
if (object.hasOwnProperty(key)) {
//相当于 block(object[key], key)
block.call(context, object[key], key, object);
}
}
};
function print(el,index){
alert(index+" : "+el)
}
forEach ([1, 2, 3], print);
forEach ({a: "aa", b: "bb",c: "cc"}, print);
forEach ("司徒正美", print);
forEach(document.styleSheets,function(el){
if(el.href) alert(el.href)
});
代码部分。
function Person(name, age) {
this.name = name || "";
this.age = age || 0;
};
var fred = new Person("Fred", 38);
fred.language = "English";//极晚绑定
fred.wife = "Wilma";//极晚绑定
forEach(fred,print)
完整代码展示。
function forEach(object, block, context, fn) {
if (object == null) return;
if (!fn) {
if (typeof object == "function" && object.call) {
//遍历普通对象
fn = Function;
} else if (typeof object.forEach == "function" && object.forEach != arguments.callee) {
//如果目标已经实现了forEach方法,则使用它自己的forEach方法(如标准游览器的Array对象)
object.forEach(block, context);
return;
} else if (typeof object.length == "number") {
// 如果是类数组对象或IE的数组对象
_Array_forEach(object, block, context);
return;
}
}
_Function_forEach(fn || Object, object, block, context);
};
function _Array_forEach(array, block, context) {
if (array == null) return;
var i = 0,length = array.length;
if (typeof array == "string")
array = array.split("");
for (;i < length; i++) {
block.call(context, array[i], i, array);
}
};
_Function_forEach = function(fn, object, block, context) {
// 这里的fn恒为Function
for (var key in object) {
//只遍历本地属性
if (object.hasOwnProperty(key)) {
//相当于 block(object[key], key)
block.call(context, object[key], key, object);
}
}
};
function print(el,index){
alert(index+" : "+el)
}
function Person(name, age) {
this.name = name || "";
this.age = age || 0;
};
var fred = new Person("Fred", 38);
fred.language = "English";//极晚绑定
fred.wife = "Wilma";//极晚绑定
forEach(fred,print)
在 Base2 中,还提供了一个名为 unbind 的方法,我们可以利用它将原生对象的 forEach 方法剥离出来供我们调用:
核心代码逻辑。
var _slice = Array.prototype.slice;
function unbind(fn) {
return function(context) {
return fn.apply(context, _slice.call(arguments, 1));
};
};
完整代码展示。
try{
var _slice = Array.prototype.slice;
function unbind(fn) {
return function(context) {
return fn.apply(context, _slice.call(arguments, 1));
};
};
function print(el,index){
alert(index+" : "+el)
}
var each = unbind(Array.prototype["forEach"])
var a = {cc : function(e){alert(e)}};
each(["11111","22222"],a.cc,a)//最后的a可有可无
}catch(e){alert("请在标准浏览器中使用!")}
本文介绍至此,希望大家今后能继续支持本站。