详解Javascript中的Array对象

基础介绍

创建数组

和Object对象一样,创建Array也有2种方式:构造函数、字面量法。

构造函数创建

使用构造函数的方式可以通过new关键字来声明,如下所示:

12
var arr = new Array();console.log(arr);//[]

当然也可以不通过new关键字来声明:

12
var arr = Array();console.log(arr);  //[]

如果知道数组元素的个数,也可以直接传入数字表示元素个数:

12
var arr2 = new Array(5);console.log(arr2.length);  //5

也可以直接传入数组元素:

12
var arr3 = new Array(5, "aa", "bb");console.log(arr3);  //[5, "aa", "bb"]
字面量法创建

通过字面量创建数组非常简单,推荐这种方式去创建数组:

12
var arr = [];console.log(arr);//[]

如果数组有一些初始值,可以直接写上去:

12
var arr2 = [5, "aa", "bb"];console.log(arr2);  //[5, "aa", "bb"]

数组长度

我们都知道要访问数组元素的个数很简单,直接通过length属性来获取到。

12
var arr = [2, 3, 4, 5];console.log(arr.length);  //4

但是和其他语言不同的是,在JS中,length属性是可以被设置的。
假设我们要清空一个数组,我们可以直接将length设置成0:

12
arr.length = 0;console.log(arr); //[]

可以看到,arr数组的元素被清空的

如果我们设置成负数会怎么样呢:

12
arr.length = -1;//Uncaught RangeError: Invalid array lengthconsole.log(arr);

很明显,length是不允许被设置成负数的。

通过设置length属性,我们可以删除元素,比如:

1234
var arr = [2, 3, 4, 5];console.log(arr);//[2, 3, 4, 5]arr.length = 2;console.log(arr);[2, 3]

将length属性设置成比原来小的数值,后面的元素就会被移除掉。

如果将length设置成比原来大的数值会怎么样呢?

1234
arr.length = 5;console.log(arr);//[2, 3, 4, 5]console.log(arr[4]);//undefinedconsole.log(arr.length);//5

可以看到,设置成比较大的数值后,多出来的元素值是undefined

数组检测

简单地介绍了Array之后,我们来说说如果检测一个变量是否是一个Array对象。这个也是一个比较常见的话题。
在ES5中,Array新增了一个检测数组对象的方法:Array.isArray。这个方法可以完美地确认一个对象是不是数组,无论这个对象是在哪个全局执行环境中创建的。

12
var arr = [2, 3, 4, 5];console.log(Array.isArray(arr));  //true

对于不支持该方法的浏览器中,我们可以采用下面的polyfill:

12345
if (!Array.isArray) {    Array.isArray = function (Obj) {        return Object.prototype.toString.apply(Obj) === "[object Array]";    }}

一些常用的数组对象方法

Array对象中包含着很多有用的方法,尤其是在ES5,ES6中新增了许多方法,下面我们来总结一下。

静态方法

Array.from

Array.from是ES6种新增的方法,它可以将类数组对象或可迭代对象转换成数组对象。
常见的类数组对象比如有函数的arguments值,或者NodeList,HTMLCollection对象等等。
常见的可迭代对象比如字符串,还有就是ES6新增的Set对象和Map对象。

我们来看看的基本用法,将一个arguments转换成数组对象:

1234
var arr = (function () {    return Array.from(arguments);})(1, 2, 3);console.log(arr);//[1,2,3]

也可以将一个字符串转换成数组对象:

1
console.log(Array.from("abcdefg")); //["a", "b", "c", "d", "e", "f", "g"]

Array.from还包含2个可选参数:mapFn,thisArg
mapFn:将from返回的数组再进行处理再返回
thisArg:执行mapFn函数时this指向的值
假设页面上有多个span标签,我们想获取这些span标签里面的文本,这是html代码

1234
<span>第一个span</span><span>第二个span</span><span>第三个span</span><span>第四个span</span>

我们要获取span标签里面的文本成组成一个数组,可以这么做:

12345
var spans = document.getElementsByTagName("span");    var texts = Array.from(spans, function (val, key) {        return val.innerText;})console.log(texts);

通过from函数将其转换成数组之后,再通过mapFn进行处理,获取每一个元素的innerText。

Array.from是ES6中新增的功能,必然有很多浏览器不支持,可以使用下面的polyfill:

var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var isArrayLike = function (collection) {
    var length = collection && collection.length;
    return typeof length == ‘number‘ && length >= 0 && length <= MAX_ARRAY_INDEX;
};
var isFunction = function (fn) {
    return Object.prototype.toString.call(fn) === ‘[object Function]‘;
} 

if (!Array.from) {
    Array.from = function (arrayLike, mapFn, thisArg) {
        if (!isArrayLike(arrayLike)) {
            throw new TypeError("被转换的对象不是一个类数组对象或可迭代对象");
        }
        mapFn = isFunction(mapFn) ? mapFn : function (val, key) {
            return val;
        }

        var result = [];
        for (var i = 0, length = arrayLike.length; i < length; i++) {
            result.push(mapFn.call(thisArg, arrayLike[i], i));
        }
        return result;
    }
}
 
 

Array.isArray

Array.isArray是用来判断一个对象是否是数组,在前面判断数组类型的章节我们已经提到过了,这里就不再赘述啦。

Array.of

Array.of方法是ES6中新增的方法,它可以将传入的多个参数作为一个数组返回。看下面例子:

1
console.log(Array.of(2, 3, 4, 5)); //[2, 3, 4, 5]

通过传入4个参数,返回一个包含4个元素的数组。
注意Array.of与采用Array构造函数的区别,看下面代码:

12
console.log(Array(4));//[]console.log(Array.of(4));//[4]

第一种方式采用Array构造函数的方式,创建了一个4个元素的数组,每个元素的值都是undefined。
第二中方式采用Array.of方法的方式,创建了一个1个元素的数组,元素值是4。

在不支持的浏览器中,可以采用下面的polyfill:

12345
if (!Array.of) {    Array.of = function () {        return Array.prototype.slice.call(arguments);    };}

继承自Object的方法

我们知道,所有的引用类型都继承自Object对象,自然而然地包含了Object的一些方法。在Array中,我们主要说说其继承自Object的toString方法和valueOf方法。

toString方法

toString方法会返回对象的字符串表示,对于Array来说,这个方法会将每一个元素值用逗号分隔起来,最终形成一个字符串返回:

1
console.log([1, 2, 3, 4].toString()); //1,2,3,4
valueOf方法

valueOf方法用来返回对象的原始值,对于Array来说,它将返回数组本身:

1
console.log([1, 2, 3, 4].valueOf());//[1,2,3,4]

元素判断方法

在ES5中,Array原型中增加了2个判断数组是否满足某一条件的方法,下面分别来说明一下:

every方法

every方法用来判断数组中的所有元素是否都满足条件,如果全部满足则返回true,否则false。
every的用法如下:

1
arr.every(callback[, thisArg])

其中callback是用来判断每个元素是否满足条件的函数,它包含两个参数:元素值,索引,数组本身。
thisArg参数是可选的,它表示callback中this指向的对象。
看看下面的例子:

1234
 var arr = [1, 2, 3]; console.log(arr.every(function (val, index) {    return val > 0;}));//true

在不支持的浏览器中,可以采用下面的polyfill:

1234567891011
if (Array.prototype.every) {    Array.prototype.every = function (fn, thisArg) {        var arr = this;        for (var i = 0, length = arr.length; i < length; i++) {            if (!fn.call(thisArg, arr[i], i, arr)) {                return false;            }        }        return true;    };}
some方法

some方法和every方法用法类似,但是它只需要数组中的某一个元素符合条件即返回true。
我们来看下面的例子:

1234
var arr = [1, 2, 3];console.log(arr.some(function (val, index) {    return val > 2;}));  //true

在数组中,只有一项元素大于2,则返回true。
在不支持的浏览器中,可以采用下面的polyfill:

1234567891011
if (!Array.prototype.some) {    Array.prototype.some = function (fn, thisArg) {        var arr = this;        for (var i = 0, length = arr.length; i < length; i++) {            if (fn.call(thisArg, arr[i], i)) {                return true;            }        }        return false;    };}

栈和队列方法

在js中,数组即可以像栈那个操作,也可以像队列那样操作,它提供了几个关于栈和队列的方法,这些方法相信大家非常熟悉,在这里简单地说明一下:

pop方法

pop方法会将数组的最后一个元素删除,然后返回这个被删除的元素

push方法

push方法是一个比较常用的方法,和pop方法相反,它用来将一个元素添加到元素的末尾,然后返回数组的长度。

shift方法

shift方法将第一个元素删除,然后返回这个元素

unshift方法

unshift在数组开头添加一个或多个元素,然后返回这个元素的长度:

123
var arr = [1, 2, 3];console.log(arr.unshift(3, 2, 1));//6console.log(arr);  // [3,2,1,1,2,3]

元素索引方法

indexOf方法

indexOf方法用来返回元素在数组中的位置,不存在则返回-1:

12
var arr = [1, 2, 3];console.log(arr.indexOf(2));  //1

indexOf方法有第二个可选参数,这个参数表示从哪个索引开始查找:

1
console.log(arr.indexOf(2, 1)); //1

如果这个参数大于数组长度,则直接返回-1,如果是负数,则将末尾进行抵消,然后开始查找,比如-1就从倒数第一开始查找,-2从倒数第二开始查找。
indexOf方法是ES5中提供的方法,对于不支持的浏览器,可以采用下面的polyfill:

12345678910111213141516171819
if (!Array.prototype.indexOf) {    Array.prototype.indexOf = function (element, fromIndex) {        if (this.length === 0 || this.length < fromIndex) {            return -1;        }        var index = fromIndex || 0;        var length = this.length;        if (fromIndex < 0) {            index = length + fromIndex;        }

        for (; index < length; index++) {            if (this[index] === element) {                return index;            }        }        return -1;    }}
lastIndexOf方法

lastIndexOf方法和indexOf方法类似,它用于查找元素在数组最后一项的索引,如果不存在则返回-1:

123
var arr = [1, 5, 3, 4, 5, 6, 7];console.log(arr.indexOf(5));//1console.log(arr.lastIndexOf(5));  //4

如上面例子所示,数组中存在着两个数字5,使用indexOf方法将返回1,使用lastIndexOf将返回5。
lastIndexOf方法同样拥有第二个可选参数fromIndex,表示从该位置开始逆向查找。
如果fromIndex值大于或等于数组长度,则整个数组都会被查找。
如果fromIndex值为负值,并且其绝对值小于数组长度时,将从数组末尾向前查找。
如果fromIndex只为负数,但是其绝对值大于数组长度时,则直接返回-1。

1234
var arr = [1, 5, 3, 4, 5, 6, 7];console.log(arr.lastIndexOf(5, 10)); //4 console.log(arr.lastIndexOf(5, -4));  //1console.log(arr.lastIndexOf(5, -10));  //-1

对于不支持的浏览器,可以使用下面的polyfill:

if (!Array.prototype.lastIndexOf) {
    Array.prototype.lastIndexOf = function (element, fromIndex) {
        if (this.length === 0 || (fromIndex < 0 && this.length < Math.abs(fromIndex))) {
            return -1;
        }

        var length = this.length;
        var index = fromIndex || length - 1;
        if (fromIndex < 0) {
            index = length + fromIndex;
        }

        for (; index > -1; index--) {
            if (this[index] === element) {
                return index;
            }
        }
        return -1;
    }
}

元素查找方法

findIndex方法

findIndex方法用来查找数组中符合条件的元素的索引,如果没有则返回-1。它包含两个参数:
callback:用来判断元素是否符合条件的回调函数,包含三个参数,分别是:当前元素,当前元素索引,数组
thisArg:可选参数,表示callback中this对象。
来看看下面的例子:

1234
var arr = [1, 2, 3, 4, 5, 6];    console.log(arr.findIndex(function (val, idx) {        return val > 3;})) //3

查找第一个大于3的元素的索引,结果为3。
如果是一个比较复杂元素的数组,我们可以来查找其中符合某个条件的元素的索引:

12345
 var arr = [{ a: 1 }, { a: 2 }];console.log(arr.indexOf({ a: 2 }));//-1console.log(arr.findIndex(function (val, idx) {    return val.a == 2;//1}));

如果是要查找一个复杂对象,使用indexOf可能查找不到其索引,但是我们可以通过findIndex来找到其索引。这个对于查找复杂元素非常有用。
对于不支持的浏览器,可以使用下面的polyfill:

if (!Array.prototype.findIndex) {
    Array.prototype.findIndex = function (fn, thisArg) {
        if (!isFunction(fn)) {
            throw new TypeError("fn不是一个有效的函数");
        }

        var arr = this;
        for (var i = 0, length = arr.length; i < length; i++) {
            if (fn.call(thisArg, arr[i], i, arr)) {
                return i;
            }
        }
        return -1;
    }
}
find方法

find方法用户查找满足条件的第一个元素,如果没有满足条件的元素,则返回undefined。
find方法的用法和findIndex类似,它包含两个参数:
callback:用来判断元素是否符合条件的回调函数,包含三个参数,分别是:当前元素,当前元素索引,数组
thisArg:可选参数,表示callback中this对象。
来看看下面的例子:

1234
var arr = [{ v: 1 }, { v: 3 }, { v: 4 }, { v: 5 }, { v: 6 }];    console.log(arr.find(function (val, idx) {        return val.v > 3;}))//Object {v: 4}

通过find方法,查找到第一个其属性v大于3的元素并返回。
大家在日常的开发中一定会遇到这种查找符合条件的元素的需求,find方法是一个非常有用的方法。
对于不支持的浏览器,可以采用下面的polyfill:

if (!Array.prototype.find) {
    Array.prototype.find = function (fn, thisArg) {
        if (!isFunction(fn)) {
            throw new TypeError("fn不是一个有效的函数");
        }
        var arr = this;
        for (var i = 0, length = arr.length; i < length; i++) {
            if (fn.call(thisArg, arr[i], i, arr)) {
                return arr[i];
            }
        }
    }
}

元素排序方法

在Array对象中,它还包含了一些排序的方法,主要有sort和reverse两个方法,下面来说明一下这两个方法。

sort方法

sort方法用来对数组进行排序,并返回排序后的数组。它包含一个参数,这个参数是一个函数,用它来比较每个参数之前的前后关系。如果不传,则默认按照字符串的Unicode码位点进行排序。
看看下面的例子:

12345
var arr = [2, 4, 6, 2, 4, 8, 11, 34];console.log(arr.sort().toString());  //11,2,2,34,4,4,6,8console.log(arr.sort(function (a, b) {    return a - b;}).toString());  //2,2,4,4,6,8,11,34

如上结果所示,不指定比较函数的话,11排到了2前面,所以一般来说,我们要指定这个比较函数。

通过比较函数,我们可以来自定义数组排序的规则:
(1)返回-1,则元素a则排在了b前面
(2)返回0,位置不变
(3)返回1,元素a排在b后面

reverse方法

reverse方法是一个很简单的方法,它将数组元素的顺序进行倒序,并返回这个数组。

123
var arr = [1, 2, 3, 4, 5, 6, 7];console.log(arr.reverse()); //[7, 6, 5, 4, 3, 2, 1]console.log(arr);  //[7, 6, 5, 4, 3, 2, 1]

元素循环方法

Array对象中包含一些对元素进行遍历的方法,通过对元素进行遍历,从而进行不同的操作。

filter方法

filter方法是用来过滤数组的,通过传入一个回调函数,如果元素通过回调函数的测试,则保留该元素,否则丢弃。具体用法如下:

1234
var arr = [2, 3, 4, 5, 6, 7, 8];console.log(arr.filter(function (val, idx, arr) {    return val > 3;}))  //[4, 5, 6, 7, 8]

如上所示,通过回调函数,我们过滤掉那些值不大于3的元素。
对于回调函数,它包含3个参数:元素,元素索引,当前数组。
filter函数还包含一个可选的参数thisArg,它表示回调函数中this的指向。
对于不支持的浏览器,可以采用下面的polyfill:

if (!Array.prototype.filter) {
    Array.prototype.filter = function (fn, thisArg) {
        if (!isFunction(fn)) {
            throw new TypeError("fn不是一个有效的函数");
        }
        var arr = this;
        var result = [];
        for (var i = 0, length = arr.length; i < length; i++) {
            if (fn.call(thisArg, arr[i], i, arr)) {
                result.push(arr[i]);
            }
        }
        return result;
    }
}
forEach函数

forEach简单地说就是for循环的语法糖,它用于让数组的每一项都执行给定的函数。
看看下面的例子,循环数组的每一项,分别输入每一项。

1234
var arr = [2, 3, 4, 5, 6, 7, 8];arr.forEach(function (val, idx) {    console.log(val);})

和其他循环方法一样,forEach包含两个参数:

callback:循环每一项调用的函数,包含三个参数:元素,元素索引,当前数组
thisArg:可选的参数,表示callback中this的指向。
有一点要注意的是:除非是抛出异常,否则无法终止或跳出循环。
对于不支持的浏览器,可以采用下面的polyfill:

if (!Array.prototype.forEach) {
    Array.prototype.forEach = function (fn, thisArg) {
        if (!isFunction(fn)) {
            throw new TypeError("fn不是一个有效的函数");
        }
        var arr = this;
        for (var i = 0, length = arr.length; i < length; i++) {
            fn.call(thisArg, arr[i], i, arr);
        }
    }
}
reduce函数

reduct函数我们可以将其想象成一个累加器,从左到右依次将每个元素累加起来。
看看下面的例子:

1234
var arr = [1, 2, 3, 4, 5];console.log(arr.reduce(function (prev, current, currentIndex, array) {    return prev + current;}))

这个例子中,通过reduce方法将数组的元素累加起来,计算总和。
reduct包含两个参数:
callback:累加调用函数,包含4个参数:
(1)上一次调用的返回值,默认情况下是第一个元素
(2)当前元素
(3)当前元素索引
(4)被循环的数组
initialValue:累加的初始值,不传则是第一个元素
对于不支持的浏览器,可以采用下面的polyfill:

if (Array.prototype.reduce) {
    Array.prototype.reduce = function (fn, initialValue) {
        if (!isFunction(fn)) {
            throw new TypeError("fn不是一个有效的函数");
        }
        var arr = this;
        var result = initialValue || arr[0];
        var index = !!initialValue ? -1 : 0;

        for (var length = arr.length - 1; index < length; index++) {
            result = fn(result, arr[index + 1], index + 1, arr);
        }
        return result;
    }
}
reduceRight方法

reduceRight与reduce类似,只是从右向左开始调用回调函数,这里就不再赘述。
对于不支持的浏览器,可以使用下面的polyfill:

if (!Array.prototype.reduceRight) {
    Array.prototype.reduceRight = function (fn, initialValue) {
        if (!isFunction(fn)) {
            throw new TypeError("fn不是一个有效的函数");
        }
        var arr = this;
        var result = initialValue || arr[arr.length - 1];
        var index = !!initialValue ? arr.length : arr.length - 1;

        for (; index > 0; index--) {
            result = fn(result, arr[index - 1], index - 1, arr);
        }
        return result;
    }
}
map函数

map方法用来将数组转换成另外一个数组,通过传入的函数来进行转换。来看看下面的例子:

1234
var arr = [1, 2, 3, 4, 5];console.log(arr.map(function (val, idx, array) {    return { "val": val };}))  //[Object, Object, Object, Object, Object]

和其他循环方法一样,map方法包含两个参数:
callback:循环每一项调用的函数,包含三个参数:元素,元素索引,当前数组
thisArg:可选的参数,表示callback中this的指向。

对于不支持的浏览器,可以采用下面的polyfill:

if (!Array.prototype.map) {
    Array.prototype.map = function (fn, thisArg) {
        if (!isFunction(fn)) {
            throw new TypeError("fn不是一个有效的函数");
        }

        var arr = this;
        var result = [];
        for (var i = 0, length = arr.length; i < length; i++) {
            result.push(fn.call(thisArg, arr[i], i, arr));
        }
        return result;
    }
}

数组操作方法

concat方法

concat方法用来将传入的数组参数或非数组参数与原数组本身合并成一个新数组并返回,具体用法如下:

123
var arr = [1, 2, 3];console.log(arr.concat([4, 5]));//1,2,3,4,5console.log(arr.concat(4, 5));1,2,3,4,5

从上面的例子,我们可以看出三点:
(1)传入的参数可以是数组,也可以是非数组
(2)传入的参数数量没有限定
(3)原来的数组并不会被改变

slice方法

slice方法用来剪切数组成一个新的数组,然后返回这个新数组。

123
var arr = [1, 2, 3, 4, 5, 6];var newArr = arr.slice(1, 3);console.log(newArr);//[2,3]

slice方法含有参数:
start:截取数组的开始索引,不传则默认为0
end:截取数组的结束索引,不传则截取到末尾,如果传了end参数,则被截取的数组将从start开始,于end索引处结束(不包含end)。

还有一点需要注意的是,被截取的数组的元素是引用对象的话,新数组的元素是和原数组的元素实际上是引用了同一个对象:

12345
var arr = [{ v: 1 }, { v: 2 }];var newArr = arr.slice(0, 1);newArr[0].v = 666;console.log(newArr[0]);//Object {v: 666}console.log(arr[0]);  //Object {v: 666}

如上所示,数组里的元素是引用对象,修改其中一个元素的值,另一个也会跟着变化。

splice方法

splice方法是数组操作中最为强大的方法:它可以新增,删除,替换数组的元素。
splice方法会返回被删除的元素组成的数组,它包含三个参数:
(1)start:从哪个索引开始修改数组,如果值大于数组长度,则表示从末尾开始添加内容,如果是负数,则从数组的末位开始的第几位。
(2)deleteCount:表示要删除的数组的数量,如果deleteCount的值大于start索引之后元素的数量,则后面所有元素将被删除。
(3)items:要添加的新元素,如果不传入该参数,则表示删除元素
我们来看看具体的几个例子:

删除元素:
1、只传递start参数,并且start参数大于0小于数组长度

123
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];console.log(arr.splice(2).toString());//空字符串或3,4,5,6,7,8,9,10console.log(arr.toString());//1,2,3,4,5,6,7,8,9,10或1,2

在这个例子中,splice只传递了start参数,这种情况下,根据浏览器有两种情况:
(1)在IE8等旧浏览器下,数组不会被该变
(2)在新浏览器下,数组会被删除掉从start参数开始后的所有元素

2、只传递start参数,并且start参数大于数组长度

123
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];console.log(arr.splice(15).toString());//空字符串console.log(arr.toString());//1,2,3,4,5,6,7,8,9,10

传递了大于数组长度的值之后,数组没有被修改

3、只传递start参数,并且start参数是负数

123
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];console.log(arr.splice(-2).toString());//9,10console.log(arr.toString());  //1,2,3,4,5,6,7,8

在这个例子中,我们传递了-2,也就是说,splice方法将从数组的末尾开始删除,即从末尾的第二位,最终被删除的元素就是9,10
注意:对于IE8等旧浏览器来说,数组仍然没有被修改

4、只传递start参数,并且start参数是绝对值大于数组长度的负数

123
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];console.log(arr.splice(-1).toString());//1,2,3,4,5,6,7,8,9,10console.log(arr.toString());  //

在这个例子中,我们传递了-15,其绝对值15大于数组长度,则整个数组都被删除了。
注意:对于IE8等旧浏览器来说,数组仍然没有被修改

5、传递start和deleteCount参数

123
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];console.log(arr.splice(1, 2).toString());//2,3console.log(arr.toString());//1,4,5,6,7,8,9,10

这个例子中,传递了start参数1,deleteCount参数2,表示从索引1的位置删除2个元素,所以最终结果如上所示。

6、传递start和deleteCount参数,其中start参数是负数

123
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];console.log(arr.splice(-5, 2).toString());//6,7console.log(arr.toString());//1,2,3,4,5,8,9,10

我们传递了-5给start参数,则数组将从倒数第5位开始修改,然后删除2个元素,所以最终结果如上所示。

添加元素
splice方法也可以用来添加元素,我们要通过传递deleteCount参数为0,并且items不能为空来添加元素。看看下面的例子:

123
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];console.log(arr.splice(1, 0, 2, 2, 2).toString());console.log(arr.toString());  //1,2,2,2,2,3,4,5,6,7,8,9,10

如上所示,我们在索引为1的位置开始添加了3个2作为新增的元素到数组中。

替换元素
splice方法用来替换元素和添加元素传递的参数类似,唯一的区别在于deleteCount不能为0。看看下面的例子:

123
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];console.log(arr.splice(1, 3, 2, 2, 2).toString());//2,3,4console.log(arr.toString());//1,2,2,2,5,6,7,8,9,10

这个例子中,从索引为1的元素开始,删除掉3个元素,然后再添加2,2,2这三个元素,最终数组变成1,2,2,2,5,6,7,8,9,10。

数组对象的方法总结

Array对象上包含了很多个方法,有一些方法会修改数组本身,有一些方法只是返回新数组,在这里简单总结下:
(1)修改数组本身的方法:pop,push,shift,unshift,sort,reverse,splice
(2)不修改数组本身,只返回新数组的方法:concat,slice
另外,对于旧浏览器,可以引用这个polyfill:array.polyfill.js

数组面试题

数组是在实际开发中非常经常遇到的一个对象,而关于数组的面试题也经常遇到,下面说说一些几个常见的数组面试题:

数组去重

数组去重是一个很常见的面试题,一个数组有多个元素,其中包含一些重复的元素,如何去除掉这些重复的元素呢?
最常见的方法是采用hash表去重法,看下面的代码:

Array.prototype.distinct = function () {
    var arr = this;
    var dic = {};
    var result = [];

    for (var i = 0; i < arr.length; i++) {
        var key = arr[i];
        if (!dic[key]) {
            dic[key] = arr[i];
            result.push(arr[i]);
        }
    }
    return result;
}
var arr = [1, 2, 3, 1, 2, 3];
console.log(arr.distinct());//[1,2,3]
var arr = ["a", "b", "c", "a", "b", "c"];
console.log(arr.distinct());  //["a","b","c"]

这个是最简单的情况,可以简单地实现数组的去重。但是也有一个明显的缺点:在作为key值时,元素会自动调用toString,最终导致一些复杂本来不是重复的元素被误认为重复。
我们看下面的例子:

12
var arr = [1, 2, 3, "1", "2", "3"];console.log(arr.distinct());//[1,2,3]

这个例子中,数组分别包含数字1,2,3,字符串“1”,“2”,“3”,这原本是不重复的元素,去重后就剩下了1,2,3了。我们将上面的方法优化一下:

Array.prototype.distinct = function () {
    var arr = this;
    var dic = {};
    var result = [];
    for (var i = 0; i < arr.length; i++) {
        var key = typeof (arr[i]) + arr[i];
        if (!dic[key]) {
            dic[key] = arr[i];
            result.push(arr[i]);
        }
    }
    return result;
}

我们将所谓hash表的key值的元素加上一个类型:var key = typeof (arr[i]) + arr[i],这样问题就基本解决了:

12345
var arr = [1, 2, 3, "1", "2", "3"];console.log(arr.distinct());//[1, 2, 3, "1", "2", "3"]

var arr = [1, 2, 3, "1", "true", true, true];console.log(arr.distinct());//[1, 2, 3, "1", "true", true]

如果涉及到更极端的情况,数组元素中包含undefined或null,会怎么样呢,这种情况下,我们就要再进行一层判断:

Array.prototype.distinct = function () {
    var arr = this;
    var dic = {};
    var result = [];

    var hasUnderfined = false;
    var hasNull = false;
    for (var i = 0; i < arr.length; i++) {
        var type = typeof (arr[i]);
        if (arr[i] === null) {
            if (hasNull == true) {
                continue;
            }
            hasNull = true;
        }

        if (type === "undefined") {
            if (hasUnderfined) {
                continue;
            }
            hasUnderfined = true;
        }

        var key = type + arr[i];
        if (!dic[key]) {
            dic[key] = arr[i];
            result.push(arr[i]);
        }
    }
    return result;
} 

var arr = [1, 2, 3, "1", "2", "3", undefined, undefined, null, null];
console.log(arr.distinct()); //[1, 2, 3, "1", "2", "3", undefined, null]

我们再来考虑一下,如果数组是复杂对象的情况下会如何,假设这种情况:

1
var arr = [{val : 1}, {val : 1},{ val : 2}];

通常对于这样的数据,我们是将两个{val:1}当成是一样的数据的,但是由于是引用类型,它们又是不相等的数据,如果要去重,应该如何做呢?
我们可以传入一个key值转换函数,通过这个函数来过滤这些复杂的数据:

var isFunction = function (fn) {
    return Object.prototype.toString.call(fn) === ‘[object Function]‘;
}

Array.prototype.distinct = function (keyThunk) {
    var arr = this;
    var dic = {};
    var result = [];

    keyThunk = isFunction(keyThunk) ? keyThunk : function (val) {
        return typeof (val) + val;
    }

    var hasUnderfined = false;
    var hasNull = false;
    for (var i = 0; i < arr.length; i++) {
        var type = typeof (arr[i]);

        if (arr[i] === null) {
            if (hasNull == true) {
                continue;
            }
            hasNull = true;
        }

        if (type === "undefined") {
            if (hasUnderfined) {
                continue;
            }
            hasUnderfined = true;
        }

        var key = keyThunk(arr[i]);
        if (!dic[key]) {
            dic[key] = arr[i];
            result.push(arr[i]);
        }
    }
    return result;
}

var arr = [{ val: 1 }, { val: 1 }, { val: 3 }];
console.log(arr.distinct().map(function (val) {
    return val.val
}));//[1]
console.log(arr.distinct(function (val) {
    return val.val;
}).map(function (val) {
    return val.val;
})) [1,3]

我们添加了一个key值的转换函数,这样就可以比较那些复杂对象了。

欢迎补充其他常见的面试题!

总结

本文详细总结了Javascript中Array对象,首先从基本概念开始说起,然后一一介绍了一些常用的数组方法,将其整理归类。最后解答了一些常见的面试题,欢迎大家交流!!

本方转自:http://luopq.com/2016/04/01/Array-in-Javascript/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io 谢谢作者,学到很多!

时间: 2024-08-06 11:56:46

详解Javascript中的Array对象的相关文章

详解javascript中的this对象

详解javascript中的this对象 前言 Javascript是一门基于对象的动态语言,也就是说,所有东西都是对象,一个很典型的例子就是函数也被视为普通的对象.Javascript可以通过一定的设计模式来实现面向对象的编程,其中this "指针"就是实现面向对象的一个很重要的特性.但是this也是Javascript中一个非常容易理解错,进而用错的特性.特别是对于接触静态语言比较久了的同志来说更是如此. 示例说明 我们先来看一个最简单的示例: <script type=&q

详解Javascript中的Object对象

Object是在javascript中一个被我们经常使用的类型,而且JS中的所有对象都是继承自Object对象的.虽说我们平时只是简单地使用了Object对象来存储数据,并没有使用到太多其他功能,但是Object对象其实包含了很多很有用的属性和方法,尤其是ES5增加的方法,因此,本文将从最基本的介绍开始,详细说明了Object的常用方法和应用. 基础介绍 创建对象 首先我们都知道,对象就是一组相似数据和功能的集合,我们就是用它来模拟我们现实世界中的对象的.那在Javascript中,创建对象的方

javascript中的Array对象的slice()、call()、apply()详解

来51cto已经很多年,就是没怎么写文字,今天来一篇介绍JS的,文章篇幅不是很大,大部分内容是我参考了别人的博客后经过自己理解,自己整理的.方便自己以后回顾知识.文中涉及到javascript中的Array对象的slice().call().apply(); slice()方法,在我之前做的项目中,有两种数据类型下用到过.第一种是string类型的,还有一种是Array对象: string对象的slice(): 定义和用法 slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部

sort方法实际应用详解---javascript中对一个对象数组按照对象某个属性进行排序

转载: 查看原文 在javascript中,对象和数组是两种不同的类型,这和php中的数组概念不同.在javascript中,也有一些精妙的算法,用来对一些对象进行排序.我在面试迅雷的时候,也拿到一道题,当时做题的时候考虑到时间,没有去仔细研究,回来后再读了一些方法,就知道真正的考点在哪里了. 我们现在有一组"学生"对象,包含"名字,年龄"等属性,现在要求一个算法,把这些对象放在一个数组里,可以实现按照年龄对这些对象进行排序. var sdts = [ { name

【转】详解JavaScript中的异常处理方法

有三种类型的编程错误:(1)语法错误和(2)运行时错误(3)逻辑错误:语法错误: 语法错误,也被称为解析错误,在编译时进行传统的编程语言,并出现在JavaScript解释时. 例如,下面一行将导致一个语法错误,因为它缺少一个右括号: <script type="text/javascript"> <!-- window.print(; //--> </script> 当一个语法错误在JavaScript中出现,只有在同一个线程中包含的语法错误的影响,

详解JavaScript中的replace()函数

Javascript中字符串对象有一个方法replace(),它的作用非常强大.这里把它的用法整理一下.  一.方法简介 该方法的签名是:replace([RegExp|String],[String|Function]). 该方法 返回一个新的字符串,但并不改变字符串本身. 该方法接收2个参数,第一个参数可以是字符串,也可以是一个正则表达式:第二个参数可以是一个字符串,也可以是一个函数.其中第2个参数如果是函数,那么用起来是十分强大而且灵活的,不过相对来说也比较难掌握.下面就其用法进行详细说明

this详解:JAVASCRIPT中的this到底是谁?

语法 this 全局对象 在全局执行上下文(函数之外),this引用的是全局对象. console.log(this.document === document); // true // In web browsers, the window object is also the global object: console.log(this === window); // true this.a = 37; console.log(window.a); // 37 函数 在函数里面,this这

详解JavaScript中的原型和继承-转自颜海镜大大

本文将会介绍面向对象,继承,原型等相关知识,涉及的知识点如下: 面向对象与继承 CEOC OLOO 臃肿的对象 原型与原型链 修改原型的方式 面向对象与继承 最近学习了下python,还写了篇博文<重拾编程乐趣——我的Python笔记>,加深了我对面向对象的一些理解. 我们会对我们写的程序进行抽象,而不同的语言都提供了不同的抽象工具,比如各种语言里面的数组,集合(键值数组,哈希表,字典等)等提供了对数据的抽象:而VB里面的子程序,类C语言里面的函数,提供了抽象代码段的能力. 有时我们希望将数据

详解JavaScript中的arc的方法

今天说说JavaScript在网页中画圆的函数arc! 一.arc所需要的参数设置 1 arc(x, y, radius, startAngle, endAngle, counterclockwise); 其中x,y,radius都很容易理解,那么重点说说startAngle,endAngle和counterclockwise三个参数!    二.arc参数详解 1,startAngle和endAngle分别指圆开始的角度和结束的角度,手册上面说的是开始的角度为0,结束的角度为Math.PI*2