本文详细解析JavaScript中数组方法的扩展与兼容性修复方案。针对IE7及以下版本,实现了indexOf、lastIndexOf、shuffle、flatten、unique、unshift及splice等方法的兼容重写。重点探讨了数组去重中处理字符串与数字类型差异及对象引用问题的逻辑,并提供了针对旧版浏览器返回值不一致的修复代码,帮助开发者构建更健壮的跨浏览器数组操作库。
indexOf与lastIndexOf的兼容性实现
本节首先介绍数组的indexOf和lastIndexOf方法。
由于在IE7浏览器中直接对数组对象调用indexOf方法会引发错误,因此需要编写一个兼容性的实现。
具体代码实现如下:
Array.prototype.lastIndexOf(item,index){
var n = this.length,i = (index==null||index>n-1)?n-1:index;
if(i < 0) i = n+i;
for(;i>=0;i--)
if(this[i] === item) //全等判断,indexOf,lastIndexOf
return i;
return -1;
}
数组洗牌与平坦化处理
接下来是shuffle方法,用于对数组进行随机洗牌操作。
相关代码如下:
function shuffle(target){
var i = target.length, j ,temp;
for(;i>0;j=parseInt(Math.random() * i), x = target[--i],target[i] = target[j],target[j]=x){}
假设数组长度为10,Math.random()*10的结果范围是[0,10),经过parseInt处理后变为[0,9]。算法随机选择一个索引与数组最后一项交换。第二次循环时,范围变为[0,8],再与倒数第二项交换,依此类推。
return target;
}
第三部分是数组的平坦化处理,即flatten方法,旨在将多维数组转换为一维数组。
实现代码如下:
function flatten(arr){
var result = [];
arr.forEach(function(item){
if(Array.isArray(item)) result.concat(flatten(item));
else result.push(item);
});
return result;
}
数组去重的类型判断与对象处理
第四部分介绍数组的unique方法,用于执行数组去重操作。
这是面试中常见的问题,因为存在多种实现方式。最基础的方法是使用两层for循环。通常大家熟悉的做法是创建一个对象a和一个for循环遍历数组arr,每次检查if(a[arr[i]])是否存在,若不存在则push到新定义的数组result中;若存在则视为重复,不执行push。然而,这种方案对于"123"和123(字符串与数字)会判定为相同,但实际上它们类型不同,不应视为重复。
因此,针对[1,"1","1"]这类情况,需要更严谨的处理。
改进后的代码如下:
if ((typeof obj[array[i]]) != (typeof array[i]) || obj[array[i]] != array[i]) {
a.push(array[i]);
obj[array[i]] = array[i];
}
该逻辑首先判断类型是否相同,如果类型相同,再判断值是否相等。若不相等则存入数组,若相等则证明之前已存在该值。
当类型不相同时,存在两种具体情况:
第一种情况是obj之前已经存储了此数据,例如obj[123] = 123,而当前array[i] = "123"。此时typeof obj[array[i]])结果为数字,而typeof array[i]为字符串,因此将其存入数组。
第二种情况是obj尚未存储此数据,例如array[i] = "123",而obj["123"] = undefined。此时typeof obj[array[i]])即为typeof undefined,结果为undefined,不等于typeof array[i],因此存入数组。
这种方法可以解决字符串和数字值相同但类型不同的情况,但无法解决对象引用相同的问题。例如a = {1:2}和b = {2:1},虽然内容不同,但作为对象键时可能产生冲突。
在第一次循环时,typeof obj[a] = undefined,typeof a = Object。存入obj[a] = a,实际上等同于obj[Object] = a。
在第二次循环时,typeof obj[b] 等于typeof obj[Object],即typeof a = object,且typeof b = object。因此进入判断obj[array[i]] != array[i],即obj[b]->obj[Object]->a != b,结果存入。
随后执行obj[b] = b,即obj[Object] = b,这覆盖了之前的obj[Object] = a。
在这种情况下,所有对象都只会保留最后一个对象的值。
当需要考虑对象去重时,可以采用以下方法:
具体代码实现如下:
for(var i = 0; i < temp.length; i++){
for(var j = i + 1; j < temp.length; j++){
if(temp[i] === temp[j]){
temp.splice( j, 1 );
j--;
}
}
}
return temp;
排序、极值与IE浏览器兼容修复
第五部分是数组排序,即sort方法。如果排序对象是复杂对象,可以自定义compare(a,b)函数,例如if(a.age>b.age) return 1;else return -1;,然后调用A.sort(compare)。
第六部分是获取数组最小值,即min方法,实现为return Math.min.apply(0,array)。
第七部分指出unshift方法在IE6和IE7下不返回数组长度。
相关代码示例如下:
if([].unshift(1)!==1) //往空数组中从前面添加一项,其他浏览器会返回1,而IE6,7不会返回数组长度。这时就执行if语句
{
var _unshift = Array.prototype.unshift; //函数劫持。
Array.prototype.unshift = function(){
_unshift.apply(this,arguments);
return this.length;
}
}
第八部分说明splice方法在仅传入一个参数时,IE8及更低版本默认第二个参数为0,而其他浏览器默认为数组长度。
相关代码示例如下:
if([1,2,3].splice(1).length == 0) //IE8以及以下版本会等于0,其他版本会等于3,进入if里面
{
var _splice = Array.prototype.splice;
Array.prototype.splice = function(a){
if(arguments.length == 1) //如果只有一个参数时
{
return _splice.call(this,a,this.length);
}else{
return _splice.apply(this,arguments);
}
}
}
此方法会改变原数组,因此数组的push、pop、shift、unshift(这几个方法也会修改原数组)都会调用此修复后的splice方法来实现。
这里有一个需要注意的细节:
示例代码如下:
var color = new Array('red','blue','yellow','black');
var color2 = color.splice(2,0,'brown','pink');
alert(color); // red,blue,brown,pink,yellow,black,在yellow选项上,开始操作,如果删除为0,则添加的选项是在yellow之前插入。切记。建议大家查阅splice和slice的区别,关注它们的返回值以及对原数组的影响。
以上就是本节内容的精简版,虽然经过精简,但重点均已涵盖,希望对大家阅读本节时能有所帮助。