程序员的知识教程库

网站首页 > 教程分享 正文

第19节 Array数组-Web前端开发之Javascript-零点程序员

henian88 2024-08-12 19:51:36 教程分享 13 ℃ 0 评论

本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。

数组是值的有序集合;每个值叫做一个元素,而每个元素在数组中都有一个位置,以数字表示,称为索引或下标;

虽然ECMAScript数组与其他语言中的数组都是有序的列表,但与其他语言不同的:

JavaScript数组是无类型的,数组元素可以是任意类型;即使同一个数组内,不同的元素也可以有不同的类型;

JS数组的索引是基于零的32位整数,第一个元素的索引为0;

ES数组是动态的,即它的大小是可以动态调整的,可以随着数据的添加与删除会自动增长或缩减;

创建数组:

有两种方法:

第一种:使用构造函数Array()创建:

语法:var arr = new Array();

var arr = new Array( length); // length是个整数

var arr = new Array(element, element,… element);

var arr = new Array();  // 空数组
arr = new Array(3);  // 指定长度
arr = new Array("wangwei","wujing","jingguo");  // 显式指定元素

如果预先知道数组要保存的项目数量,可以为构造函数传递该数量,这个数量实际上就是该数组length属性(实际上是预分配了一个数组空间,但数组中没有存储值,甚至数组的索引都未定义);

直接传递项,即参数就是数组的元素;

如果需要创建只有一个值的数组,并且这个值是数字,只能把该数字使用字符串的形式;当这一个值如果是非整数时,会抛出异常;

var arr = new Array(3);  // 有3个空元素
arr = new Array("3");    // 有1个元素,值为“3”
var arr = new Array(18.5);  // RangeError: Invalid array length

也可以省略new操作符,实际上是进行了数据类型转换,如:

var colors=Array(3);
var names=Array("Wang");

第二种方式:使用数组字面量(直接量)创建:

 var empty = [];  // 空数组
var num = [2,3,5,7,11];
var names = ["wangwei","wujing","jianguo"];

这是创建数组最简单的方法,即使用方括号[ ]将数组元素用逗号分隔开;

当为[ ]空数组时,与new Array()等价;

数组直接量中的值不一定是常量,也可以是任意的表达式,如:

var year = 2020;
var years = [year,year+1,year+2,year+3];

还可以包含对象或其他数组,如:

var objArr = [{name:"wangwei",age:18},[1,2],[3,{x:4,y:5}]];

如果省略数组中某个值,省略的元素的值都是undefined值:

var count = [1,,3];  // 数组3个元素,第2个的值是undefined

允许有可选的结尾逗号,但会省略最后的元素:

count = [1,2,];  // 2
count = [,,,];   // 3

不建议使用,某些浏览器会解析为3项数组,因为它们对数组的实现不一致;

总结:在实际场景中,使用数组字面量要比构造函数简单、灵活。

数组的内存分布:

数组在内存中用一串连续的区域来存放一些值;在 JavaScript 中,数组是哈希映射,在内存中不是连续的,它可以通过多种数据结构实现,其中一种是链表,因此,如果使用大数组,需要的时间很长;

在ES6中引入了类型化数组,JavaScript 引擎已经在为同种数据类型的数组分配连续的存储空间了,如ArrayBuffer,其使用的就是一块连续的内存空间。

数组元素的读和写:

要读取或设置数组元素的值时,使用“[ ]”运算符,并提供相应的基于0的数字索引下标;

var citys = new Array("蚌埠","宿州","南京");
var c1 = citys[0];  // 读取第一个元素
citys[0] = "怀远";  // 写第一个元素

读取和设置元素值的语法相同;

在越界访问不存在的数据元素时,会返回undefined;

如果设置某个值的索引超过了数组现有的项数,数组会自动增加到该索引值加1的长度;

var colors=["red","blue"];
colors[2]="green";   // 明确添加了一个新元素
var i = 3;
colors[i] = "purple";
colors[i+1] = "yellow";

数组的最大索引为4294967294(2的32次方-2);

数组是对象的特殊形式;使用方括号访问数组元素就像用方括号访问对象的属性一样;其会将指定的数字索引值转换成字符串,并将其作为属性名来使用;

var n = [];
n[0] = "one";
n[1] = "two";
console.log(n[1]);
var p = {};
p[0] = "one";  // 将0转换为”0”
p[1] = "two";
console.log(p[1]);
var o = {0:"wangwei",1:"wujing",2:"huiyuan"};
o[1] = "lili";
console.log(o[1]);  // lili

所有的数组都是对象,所以可为其创建任意名字的属性,此时这个任意名字如果不是非负整数,它就只能当作常规的对象属性,而非数组的索引;同样,如果凑巧使用了是非负整数的字符串,它就当作数组索引,而非对象属性;当使用一个浮点数和一个整数相等时情况也是一样的:

var arr = [1,2];
arr[-1.23] = true;  // 创建了一个名为“-1.23”的属性
arr["1000"] = 18;   // 数组的第1001个元素
arr[1.000] = 10;    // 等同 arr[1] 
console.log(arr)

事实上,数组索引仅仅是对象属性名的一个特殊类型,这意味着ES数组没有“越界”错误的概念,即试图查询任何对象中不存在的属性时,不会报错,只是得到undefined值。

所有的索引都是属性名,但只有在2的32次方之内的整数属性名才是索引;超出的索引只能是属性名。

var index = Math.pow(2,32) - 1; // 4294967295

var arr = new Array(index);

arr[index] = "zero"; // index作为索引

index = index + 3;

arr[index] = "net"; // index作为属性

console.log(arr[index]);

console.log(arr); // 从长度能看出来 4294967295


通常,数组的实现是经过优化的,用数字索引来访问数组元素一般来说比访问常规的对象属性要快很多,所以尽量使用数组传递数据;

var p = ["wangwei",true,18];

console.log(p[0]);

(当然,这种数据结构并不友好,如果有明确的固定结构,可以使用这种方式)


length属性:

数组对象的length(长度)属性返回或指定数组元素的个数。如:

console.log(colors.length);

注:由于下标是从0开始,所以Length是最后一个元素的下标加1;

length属性是可写的,即数组的长度可动态增大或减少,即通过设定length属性可以指定数组的长度。

var colors=["red","blue","green","skey","gray"];
console.log(colors.length);
colors.length = 3;
console.log(colors.length);
console.log(colors);

说明:即多于length的元素被删除了;

var names=["wangwei"];
names.length = 4;
console.log(names);

length属性设置为大于数组项数的值,则新增的每一项都会取得undefined值;(从本质上讲,并不会向数组中添加新的元素,它只是在数组尾部创建一个空的区域,其实就是后面要讲的稀疏数组);

利用length属性可以方便的在数组末尾添加新项;

var colors=["red","blue","green"];
colors[colors.length]="black";
colors[colors.length]="brown";
console.log(colors);

数组的最大索引为4294967294(2的32次方-2),即可以包含4292967295个元素,当索引小于此值并且不为非负时,数组会自动更新其length属性;超出这个范围,length不会更新;如:

var index = Math.pow(2,32) - 1;  // 4294967295
var arr = new Array(index);
arr[index + 3] = "zero";
console.log(arr[index + 3]);
index = index + 4;
arr[index] = "net";
console.log(index);
console.log(arr.length);
console.log(arr)

在ES中,可以用Object.defineProperty()让数组的length属性变成只读的,如:

var arr = ["wangwei",true,18];
Object.defineProperty(arr, "length", {writable: false});
console.log(arr.length);  // 3
arr.length = 0;
console.log(arr.length);  // 3

稀疏数组和密集数组:

Sparse arrays 稀疏数组:ES中的数组是稀疏的,即数组的元素的索引不一定要连续,它们之间可以有空缺;如:

var arr = new Array(5);   // 数组没有元素,但length是5
arr = [];   // 空数组,length为0
arr = [1,,4];   // 
arr[1000] = 0;  // 赋值添加了一个元素,length为1001
arr = [1,2,3,4,5];
delete arr[2];  // 使用delete也能生成稀疏数组
console.log(arr);

如果数组是稀疏数组,length值大于元素的个数,否则,该属性就是数组元素的个数;(即稀疏数组的length长度与个数是不一致的);

遍历时会跳过这些空隙:

var a1 = [1,,,4];
for(i in a1)
    console.log(i);

但当输出数组中空隙位置的值时,为undefined;

Dense([dens])arrays 稠密(密集)数组:

与稀疏相对应,则为密集数组,即元素中不存在空隙,其实密集数组基本就是平时常见的正常数组;

var arr1 = [1, 2, 3];
var arr2 = new Array(1, 2, 3); 
var dense = Array.apply(null, Array(3));   //  初始化时
var dense = Array(undefined, undefined, undefined); //  等同于
for(i in dense)
    console.log(i,dense[i]);

区别:

var array = new Array(3);   //稀疏数组
array[2] = "wangwei";
for(var i in array)
  console.log(i,array[i]);
// 区别
var dense = Array.apply(null, Array(3));  // 密集数组
dense[2] = "wujing";
for(var i in dense)
  console.log(i,dense[i]);

如:

// 密集数组
console.time('one');
Array.apply(null,Array(1e5)).forEach(function(){});
console.timeEnd('one');
// 稀疏数组
console.time('two');
Array(1e5).forEach(function(){});
console.timeEnd('two');
// one: 8.589111328125ms
// two: 2.122314453125ms

在实际应用中,绝大部分的es数组都不是稀疏数组;如果使用了稀疏数组,可以像处理非稀疏数组一样处理,只不过它们包含一些undefined值。

稀疏的数组通常在实现上比稠密的数组更慢、内存利用率更高,在这样的数组中查找元素的时间与常规元素属性的查找时间一样长;另外,在通常的情况下,我们想要直接声明一个数组并赋予其一些特定的初始值,并且为了避免问题,通常是希望申明为密集数组的;

压缩稀疏数组:可以通过filter()方法压缩其中的空隙,因为filter会跳过空隙,返回密集的数组,如:

var a1 = [1,,,4];
var a2 = a1.filter(function(x){return true;});
for(i in a2)
    console.log(i);

组方法:

ES在Array.prototype中定义了很多操作数组的方法;

将数组转换为字符串:

所有对象都具有toLocaleString()、toString()及valueOf()方法,其中,valueOf()返回的是数组本身,而toString方法将数组表示为字符串,各个元素按顺序排列组合以逗号分隔的字符串返回。(通过对每项调用toString()方法,然后用逗号把它们连接在一起构成的);如:

var colors=["red","blue",["green","black"]];
console.log(colors.valueOf());  // ["red", "blue", Array(2)]
console.log(colors.toString()); // red,blue,green,blank
console.log(colors);        // ["red", "blue", Array(2)]

以上console.log()换成alert,由于alert要接收字符串参数,所以它会在后台调用toString(),由此会得到与直接调用 toString()方法相同的结果;

这里的toString()方法与不使用参数的join()方法返回的字符串是一样的。

toLocaleString()是toString()方法的本地化版本,一般情况下会返回与toString()和valueOf()方法相同的值,但也不总是如此(其会使用本地化的分隔符);当调用数组的toLocaleString()方法时,后台会为每一项调用 toLocaleString()方法,而不是tostring()方法。

var p1={
    toLocaleString:function(){return "wangwei"},
    toString:function(){return "wangwei"}
}
var p2={
    toLocaleString:function(){return "wujing"},
    toString:function(){return "Wu"}
}
var person=[p1,p2];
console.log(person);
console.log(person.toString());
console.log(person.toLocaleString());

join()方法:将数组中所有元素使用不同的分隔符转化为字符串,分隔符号由用户指定,如:

var colors = ["red","green","blue"];
console.log(colors.join());
console.log(colors.join(","));
console.log(colors.join("|"));
console.log(colors.join(""));
console.log(colors.join(" "));
var arrStr = new Array(10);
console.log(arrStr.join('-'));  // 9个-----

如果不指定分隔符,或者传入undefined,则使用默认的逗号分隔;

join()方法是split()方法的逆向操作,后者是将字符串分割成若干块来创建一个数组。

注:如果数组中的某一项的值是null或者undefined,那么该值在以上所有方法中返回空字符串。

字符串split()方法:把字符串转换为数组;

语法: var aObj=str.split(“分隔符”);

var str = "red,blue,green";     // 字符串
var aStr=str.split(",");        // 数组
var aStr=str.split("");     // 单个字符数组

如果把空字符串声明为分隔符,split会返回字符串的每个字条符;一般用于需要逐个处理字符.

队列方法:

栈和队列是一种限制了插入和删除数据项操作的数据结构;

栈数据结构的访问规则是LIFO(先进后出),而队列数据结构的访问规则是FIFO(First-In-First-Out,先进先出)。

栈最近添加的项是最先删除的项;栈中的插入和删除操作都只发生在一个位置,即栈顶部;把一个项添加到栈中,叫做推入栈;从栈中删除一项,叫做从栈中弹出;

队列在列表的末端添加项,从列表的前端移除项;可以使用push()方法向数组末尾添加项,使用shift()方法从数组前端移除项,这两个方法模拟队列;

push()方法:

在数组尾部添加一个或多个元素,并返回数组新的长度;会修改原数组;

语法:数组名.push( [ 元素1, [ 元素2, […, [元素N ] ] ] ] );元素:可选项,可以是一个或多个JavaScript对象,使用“,”分隔。如:

var colors=new Array();
var count = colors.push("red","green");
console.log(colors);
console.log(count);  // 2
count = colors.push(["black","blue"]);
console.log(colors);
console.log(count);  // 3

push()方法实际上同手工添加数据一样.

pop()方法:

与push()方法相反,其会删除数组末尾的一个元素,并将其返回;会修改原数组;

语法:数组名.pop();

var colors=new Array();
colors.push("red","green","blue");
var item = colors.pop();
console.log(item);      // blue
console.log(colors);

unshift()方法:

在数组顶端插入元素,一次可以插入单个或多个元素,所有元素按顺序插入,操作完成后返回新数组的长度;会修改原数组;其与push()类似;

语法:数组名.unshift( [元素1, [ 元素2, [ 元素3, […, [元素N ] ] ] ] ] );

var colors=new Array();
colors.push("red","green","blue");
var newLen = colors.unshift("black","gray");
newLen = colors.unshift("r",["g","b"]);
console.log(newLen);
console.log(colors);

unshift()方法将引发所有下标的改动;

在一次性插入多个参数时,插入的元素的顺序和它们在参数列表中的顺序一致;

如果不计较元素插入的位置,则推荐使用push方法,因为,unshift()方法可能会影响依靠下标才能准确进行的计算。

shift()方法:

shift()移除数组的第一个元素并将其返回;该方法执行后数组剩下的元素向前移动,下标索引号重新调整从0开始按顺序赋予所有元素;会修改原数据;其与pop方法类似;

语法:数组名.shift();

var colors=new Array();
colors.push("red","green","blue");
var shiftItem = colors.shift();
console.log(colors);
console.log(shiftItem);

调用push()方法和shift(),可以使Array对象具有队列一样的行为(先进先出);

var colors=new Array();
var count = colors.push("red","green","blue");
count = colors.push("black");
var item = colors.shift();
console.log(colors);

使用unshift()和pop()方法,可以从相反的方向来模拟队列,即在数组的前端添加项,在末端移除项;

var colors=new Array();
var count = colors.push("red","green","blue");
count = colors.unshift("black");
var item = colors.pop();
console.log(colors);

数组排序:

排序算法:选择排序和冒泡排序;

选择排序的原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾;以此类推,直到所有元素均排序完毕,如下:

2、5、8、1、4 原数组

1、5、8、2、4 第一次,把小值1放到起始位置

1、2、8、5、4 第二次,从5824中找出2放到1后面

1、2、4、8、5 第三次,从854中找出4放到2后面

1、2、4、5、8 第三次,从85中找出5放到4后面

var arr = [3,5,8,1,4];
// 第一次排序,找到最小值1,第2次把i初始值换成2,把arr[0]换成arr[1]
for(var i = 1; i<arr.length; i++){
    if(arr[0] > arr[i]){
        var temp;
        temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
    }
}
// 排序所有
for(var i = 1; i<arr.length; i++){
    for(var j = i; j < arr.length; j++){
        if(arr[i-1] > arr[j]){
            var temp;
            temp = arr[j];
            arr[j] = arr[i-1];
            arr[i-1] = temp;
        }
    }
}
console.log(arr);
// 封装成函数
function selectSort(arr){
    var len = arr.length;
    for(var i = 1; i<len; i++){
        for(var j = i; j < len; j++){
            if(arr[i-1] > arr[j]){
                var temp;
                temp = arr[j];
                arr[j] = arr[i-1];
                arr[i-1] = temp;
            }
        }
    }
    return arr;
}
console.log(selectSort(arr));

冒泡排序:重复地访问要排序的数列,一次比较相邻的两个元素,如果它们的顺序错误就把它们进行交换;

4、8、8、8、8

1、4、5、5、5

8、1、4、4、4

5、5、1、2、2

2、2、2、1、1

5大于2,不动、8大于5,不动、1小于8,换、4小于交换后的8,换、5大于2,不动、1小于5,换、4小于交换后的5,换、1小于2,换

var arr = [3,5,8,1,4];
// 封装成函数
function bubbleSort(arr){
    var len = arr.length;
    for(var i = 1; i<len; i++){
        for(var j = 0; j < len - i; j++){
            if(arr[j] > arr[j + 1]){
                var temp;
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
    return arr;
}
console.log(bubbleSort(arr));

数组中存在两个可以直接用来重排序的方法:reverse()和sort()方法;

reverse()方法将一个Array对象中所有元素的次序反转,会修改原数组;

var values = [1,2,3,4,5];
values.reverse();
alert(values);
 
var colors=new Array();
colors.push("red","green","blue");
colors.reverse();
alert(colors);

reverse()方法很直观,但不够灵活;

sort()方法:对数组元素进行排序后并返回;默认将一个数组中的所有元素进行升序排序:会改变原数组;

var values = [0,1,5,10,15];
values.sort();
console.log(values);
var arr = new Array("banana",undefined,"cherry",null,"apple");
arr.sort();
console.log(arr);

如果数组元素中undefined或null,它们会被排列在数组的尾部;

实际上,sort()方法是把元素转换成字符串进行排序的;

sort()方法可以接收一个比较函数,以确定一个排序的规则;比较函数接收两个参数,如果第一个参数位于第二个之前则返回一个负数,如果两个参数相等则返回0,如果第一个参数位于第二个之后则返回一个正数;如:

function compare(value1,value2){
    if(value1<value2){
        return -1;
    }else if(value1>value2){
        return 1;
    }else{
        return 0;
    }
}
var values = [0,1,5,10,15];
values.sort(compare);

对于数值类型或者valueOf()方法会返回数值类型的对象类型,可以使用一个更简单的比较函数,这个函数只要用两个值互减即可,如:

function compare(value1,value2){
    return value1 - value2;
}

说明:由于该函数通过返回一个小于零、等于零或大于零的值来影响排序结果,因此减法操作就可以适当地处理所有这些情况。

// 或者使用匿名函数
values.sort(function(value1,value2){
    return value1 - value2;  // 升序
    // return value2 - value1;     // 降序
});
console.log(values);

如果只是反转,使用reverse()更快一些;

随机排序:

var arr = [1,2,3,4,5,6,7,8,9];
var randomArr = arr.sort(function(){
    return Math.random() - 0.5;
});
console.log(randomArr);

对于字符串排序,主要是大小写的问题:

var str = ["wangwei","juzi","Wujing"];
// console.log(str.sort());  // "Wujing", "juzi", "wangwei"
str.sort(function(s,t){
    var s = s.toLowerCase();
    var t = t.toLowerCase();
    if(s < t) return -1;
    if(s > t) return 1;
    return 0;
});
console.log(str);

另外,有时需要按字符串长度进行排序,此时还应该同时判断字符是否为双节字:

function getDword(str){
    var len = str.length;
    for(var i=0; i<str.length; i++){
        if(str.charCodeAt(i) > 255)
            len++;
    }
    return len;
}
var arr = ['zero零点','王唯','零点说','zeronetwork','零点网络'];
var sortArr = arr.sort(function(s1,s2){
    return getDword(s1) - getDword(s2);
});
console.log(sortArr);

对象排序,比较的主要是对象的valueof()值:

var o = {valueOf:function(){return "b";}};
var arr = [o,
    {valueOf:function(){return 15}},
    {valueOf:function(){return 18}},
    {valueOf:function(){return 4}}
];
arr.sort(function(o1,o2){return o1 - o2});
console.log(arr);
for(var i in arr)
    console.log(arr[i].valueOf());

或者按指定的对象属性进行排序:

var wang = {name:"wangwei",sex:"男",age:18};
var wu = {name:"wujing",sex:"女",age:28};
var guo = {name:"jianguo",sex:"男",age: 25};
var arr = [wang,wu,guo];
var pArr = arr.sort(function(p1,p2){
    return p1.age - p2.age;
});
console.log(pArr);


本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表