对象

对象简介

面向对象简介

面向对象:可以创建自定义的类型,很好的支持继承和多态。
面向对象的特征:封装、继承、多态。

对象的概念

在 JavaScript 中,对象是一组无序的相关属性和方法的集合。
对象的作用是:封装信息。比如Student类里可以封装学生的姓名、年龄、成绩等。
对象具有特征(属性)和行为(方法)。

为什么需要对象

保存一个值时,可以使用变量,保存多个值(一组值)时,可以使用数组
比如,如果要保存一个人的信息,通过数组的方式可以这样保存:

  1. var arr = ['王二', 35, '男', '180'];

上面这种表达方式比较乱。而如果用JS中的对象来表达,结构会更清晰。如下:

  1. var person = {};
  2. person.name = '王二';
  3. person.age = 35;
  4. person.sex = '男';
  5. person.height = '180';

由此可见,对象里面的属性均是键值对

  • 键:相当于属性名。
  • 值:相当于属性值,可以是任意类型的值(数字类型、字符串类型、布尔类型,函数类型等)。

两条补充

补充1:对象的属性值可以是任何的数据类型,也可以是个函数:(也称之为方法)

  1. var obj = new Object();
  2. obj.sayName = function () {
  3. console.log('smyhvae');
  4. };
  5. console.log(obj.sayName); //没加括号,就是获取方法
  6. console.log('-----------');
  7. console.log(obj.sayName()); //加了括号,就是调用方法。即:执行函数内容,并执行函数体的内容

打印结果:

(二)JavaScript 对象简介 - 图1

补充2:对象中的属性值,也可以是一个对象

举例:

  1. //创建对象 obj1
  2. var obj1 = new Object();
  3. obj1.test = undefined;
  4. //创建对象 obj2
  5. var obj2 = new Object();
  6. obj2.name = "smyhvae";
  7. //将整个 obj2 对象,设置为 obj1 的属性
  8. obj1.test = obj2;
  9. console.log(obj1.test.name);

打印结果为:smyhvae

对象和数据类型之间的关系

数据类型分类
  • 基本数据类型(值类型):String 字符串、Number 数值、Boolean 布尔值、Null 空值、Undefined 未定义。
  • 引用数据类型(引用类型):Object 对象。

基本数据类型

基本数据类型的值直接保存在栈内存中,值与值之间是独立存在,修改一个变量不会影响其他的变量。

对象

只要不是那五种基本数据类型,就全都是对象。

如果使用基本数据类型的数据,我们所创建的变量都是独立的,不能成为一个整体。

对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。

对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间。变量保存的是对象的内存地址(对象的引用)。

换而言之,对象的值是保存在堆内存中的,而对象的引用(即变量)是保存在栈内存中的。

如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响

上面这句话很重要,我们来看看下面的例子。

传值和传址的区别

传值

代码举例:

  1. let a = 1;
  2. let b = a;// 将 a 赋值给 b
  3. b = 2; // 修改 b 的值

上方代码中,当我修改 b 的值之后,a 的值并不会发生改变。这个大家都知道。我们继续往下看。

传址(一个经典的例子)

代码举例:

  1. var obj1 = new Object();
  2. obj1.name = "孙悟空";
  3. var obj2 = obj1; // 将 obj1 的地址赋值给 obj2。从此, obj1 和 obj2 指向了同一个堆内存空间
  4. //修改obj2的name属性
  5. obj2.name = "猪八戒";

上面的代码中,当我修改 obj2 的name属性后,会发现,obj1 的 name 属性也会被修改。因为obj1和obj2指向的是堆内存中的同一个地址。

这个例子要尤其注意,实战开发中,很容易忽略。

对于引用类型的数据,赋值相当于地址拷贝,a、b指向了同一个堆内存地址。所以改了b,a也会变;本质上a、b就是一个东西。

如果你打算把引用类型 A 的值赋值给 B,让A和B相互不受影响的话,可以通过 Object.assign() 来复制对象。效果如下:

  1. var obj1 = {name: '孙悟空'};
  2. // 复制对象:把 obj1 赋值给 obj3。两者之间互不影响
  3. var obj3 = Object.assign({}, obj1);

对象的分类

1.内置对象:

  • 由ES标准中定义的对象,在任何的ES的实现中都可以使用
  • 比如:Object、Math、Date、String、Array、Number、Boolean、Function等。

2.宿主对象:

  • 由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象。
  • 比如 BOM DOM。比如consoledocument

3.自定义对象:

  • 由开发人员自己创建的对象

通过 new 关键字创建出来的对象实例,都是属于对象类型,比如Object、Array、Date等。

基本包装类型

基本数据类型不能绑定属性和方法

属性和方法只能添加给对象,不能添加给基本数据类型。

1、基本数据类型:

注意,基本数据类型string无法绑定属性和方法的。比如说:

  1. var str = 'qianguyihao';
  2. str.aaa = 12;
  3. console.log(typeof str); //打印结果为:string
  4. console.log(str.aaa); //打印结果为:undefined

上方代码中,当我们尝试打印str.aaa的时候,会发现打印结果为:undefined。也就是说,不能给 string 绑定属性和方法。

当然,我们可以打印 str.length、str.indexOf(“m”)等等。因为这两个方法的底层做了数据类型转换(临时string 字符串转换为 String 对象,然后再调用内置方法),也就是我们在上一段中讲到的包装类

2、引用数据类型:

引用数据类型String是可以绑定属性和方法的。如下:

  1. var strObj = new String('smyhvae');
  2. strObj.aaa = 123;
  3. console.log(strObj);
  4. console.log(typeof strObj); //打印结果:Object
  5. console.log(strObj.aaa);

打印结果:

(二)JavaScript 对象简介 - 图2

内置对象 Number 也有一些自带的方法,比如:

  • Number.MAX_VALUE;
  • Number.MIN_VALUE;

内置对象 Boolean 也有一些自带的方法,但是用的不多。

基本包装类型

介绍

我们都知道,js 中的数据类型包括以下几种。

  • 基本数据类型:String、Number、Boolean、Null、Undefined
  • 引用数据类型:Object

JS 为我们提供了三个基本包装类

  • String():将基本数据类型字符串,转换为 String 对象。
  • Number():将基本数据类型的数字,转换为 Number 对象。
  • Boolean():将基本数据类型的布尔值,转换为 Boolean 对象。

通过上面这这三个包装类,我们可以将基本数据类型的数据转换为对象

代码举例:

  1. let str1 = 'qianguyihao';
  2. let str2 = new String('qianguyihao');
  3. let num = new Number(3);
  4. let bool = new Boolean(true);
  5. console.log(typeof str1); // 打印结果:string
  6. console.log(typeof str2); // 注意,打印结果:object

需要注意的是:我们在实际应用中一般不会使用基本数据类型的对象。如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果。

比如说:

  1. var boo1 = new Boolean(true);
  2. var boo2 = new Boolean(true);
  3. console.log(boo1 === boo2); // 打印结果竟然是:false

再比如说:

  1. var boo3 = new Boolean(false);
  2. if (boo3) {
  3. console.log('qianguyihao'); // 这行代码竟然执行了
  4. }

基本包装类型的作用

当我们对一些基本数据类型的值去调用属性和方法时,JS引擎会临时使用包装类将基本数据类型转换为引用数据类型(即“隐式类型转换”),这样的话,基本数据类型就有了属性和方法,然后再调用对象的属性和方法;调用完以后,再将其转换为基本数据类型。

举例:

  1. var str = 'qianguyihao';
  2. console.log(str.length); // 打印结果:11

比如,上面的代码,执行顺序是这样的:

  1. // 步骤(1):把简单数据类型 string 转换为 引用数据类型 String,保存到临时变量中
  2. var temp = new String('qianguyihao');
  3. // 步骤(2):把临时变量的值 赋值给 str
  4. str = temp;
  5. // 步骤(3):销毁临时变量
  6. temp = null;

在底层,字符串以字符数组的形式保存

在底层,字符串是以字符数组的形式保存的。代码举例:

  1. var str = 'smyhvae';
  2. console.log(str.length); // 获取字符串的长度
  3. console.log(str[2]); // 获取字符串中的第3个字符(下标为2的字符)

上方代码中,smyhvae这个字符串在底层是以["s", "m", "y", "h", "v", "a", "e"]的形式保存的。因此,我们既可以获取字符串的长度,也可以获取指定索引 index 位置的单个字符。这很像数组中的操作。

再比如,String 对象的很多内置方法,也可以直接给字符串用。此时,也是临时将字符串转换为 String 对象,然后再调用内置方法。

内置对象 String:字符串的常见方法

内置对象简介

JavaScript 中的对象分为3种:自定义对象 、内置对象、 浏览器对象。

前面两种对象:是JS的基础内容,属于 ECMAScript; 第三个浏览器对象:属于JS独有,即 JS 内置的API。

内置对象:就是指这个语言自带的一些对象,供开发者使用,这些对象提供了一些常用或者最基本而必要的功能(属性和方法)。

内置对象最大的优点就是帮助我们快速开发。

JavaScript的内置对象

内置对象 对象说明
Arguments 函数参数集合
Array 数组
Boolean 布尔对象
Math 数学对象
Date 日期时间
Error 异常对象
Function 函数构造器
Number 数值对象
Object 基础对象
RegExp 正则表达式对象
String 字符串对象

前面的几篇文章中,我们专门讲到了数组 Array。今天这篇文章,我们先来讲一下内置对象 Math。

字符串前言

在日常开发中,String 对象(字符串对象)的使用频率是非常高的。所以有必要详细介绍。

需要注意的是:字符串的所有方法,都不会改变原字符串(字符串的不可变性),操作完成后会返回一个新的值。

字符串的常见方法如下。

查找字符串

1、indexOf()/lastIndexOf():获取字符串中指定内容的索引

这个方法,是使用频率最高的一个方法。

语法 1

  1. 索引值 = str.indexOf(想要查询的字符串);

备注:indexOf() 是从前向后查找字符串的位置。同理,lastIndexOf()是从后向前寻找。

解释:可以检索一个字符串中是否含有指定内容。如果字符串中含有该内容,则会返回其第一次出现的索引;如果没有找到指定的内容,则返回 -1。

因此可以得出一个重要技巧:

  • 如果获取的索引值为 0,说明字符串是以查询的参数为开头的
  • 如果获取的索引值为-1,说明这个字符串中没有指定的内容。

举例 1:(查找单个字符)

  1. const str = 'abcdea';
  2. //给字符查索引(索引值为0,说明字符串以查询的参数为开头)
  3. console.log(str.indexOf('c'));
  4. console.log(str.lastIndexOf('c'));
  5. console.log(str.indexOf('a'));
  6. console.log(str.lastIndexOf('a'));

打印结果:

(二)JavaScript 对象简介 - 图3

举例 2:(查找字符串)

  1. const name = 'qianguyihao';
  2. console.log(name.indexOf('yi')); // 打印结果:6

语法 2

这个方法还可以指定第二个参数,用来指定查找的起始位置。语法如下:

  1. 索引值 = str.indexOf(想要查询的字符串, [起始位置]);

举例 3:(两个参数时,需要特别注意)

  1. var str = 'qianguyihao';
  2. result = str.indexOf('a', 3); // 从下标为3的位置开始查找 'a'这个字符 【重要】
  3. console.log(result); // 打印结果:9

上方代码中,indexOf()方法中携带了两个参数,具体解释请看注释。

indexOf 举例

案例:查找字符串”qianguyihao”中,所有 a 出现的位置以及次数。

思路:

(1)先查找第一个 a 出现的位置。

(2)只要 indexOf 返回的结果不是 -1 就继续往后查找。

(3)因为 indexOf 只能查找到第一个,所以后面的查找,可以利用第二个参数,在当前索引加 1,从而继续查找。

代码实现:

  1. var str = 'qianguyihao';
  2. var index = str.indexOf('a');
  3. var num = 0;
  4. while (index !== -1) {
  5. console.log(index);
  6. num++; // 每打印一次,就计数一次
  7. index = str.indexOf('o', index + 1);
  8. }
  9. console.log('a 出现的次数是: ' + num);

2、search():获取字符串中指定内容的索引(参数里一般是正则)

语法

  1. 索引值 = str.search(想要查找的字符串);
  2. 索引值 = str.search(正则表达式);

备注:search() 方法里的参数,既可以传字符串,也可以传正则表达式。

解释:可以检索一个字符串中是否含有指定内容。如果字符串中含有该内容,则会返回其第一次出现的索引;如果没有找到指定的内容,则返回 -1。

举例:

  1. const name = 'qianguyihao';
  2. console.log(name.search('yi')); // 打印结果:6
  3. console.log(name.search(/yi/i)); // 打印结果:6

备注:上方的/yi/i采用的是正则表达式的写法,意思是,让 name去匹配字符yi,忽略大小写。我们在后面会专门介绍正则表达式。

3、includes():字符串中是否包含指定的内容

语法

  1. 布尔值 = str.includes(想要查找的字符串, [position]);

解释:判断一个字符串中是否含有指定内容。如果字符串中含有该内容,则会返回 true;否则返回 false。

参数中的 position:如果不指定,则默认为0;如果指定,则规定了检索的起始位置。

  1. const name = 'qianguyihao';
  2. console.log(name.includes('yi')); // 打印结果:true
  3. console.log(name.includes('haha')); // 打印结果:false
  4. console.log(name.includes('yi',7)); // 打印结果:false

4、startsWith():字符串是否以指定的内容开头

语法

  1. 布尔值 = str.startsWith(想要查找的内容, [position]);

解释:判断一个字符串是否以指定的子字符串开头。如果是,则返回 true;否则返回 false。

参数中的position

  • 如果不指定,则默认为0。
  • 如果指定,则规定了检索的起始位置。检索的范围包括:这个指定位置开始,直到字符串的末尾。即:[position, str.length)

举例:

  1. const name = 'abcdefg';
  2. console.log(name.startsWith('a')); // 打印结果:true
  3. console.log(name.startsWith('b')); // 打印结果:false
  4. // 因为指定了起始位置为3,所以是在 defg 这个字符串中检索。
  5. console.log(name.startsWith('d',3)); // 打印结果:true
  6. console.log(name.startsWith('c',3)); // 打印结果:false

5、endsWith():字符串是否以指定的内容结尾

语法

  1. 布尔值 = str.endsWith(想要查找的内容, [position]);

解释:判断一个字符串是否以指定的子字符串结尾。如果是,则返回 true;否则返回 false。

参数中的position

  • 如果不指定,则默认为 str.length。
  • 如果指定,则规定了检索的结束位置。检索的范围包括:从第一个字符串开始,直到这个指定的位置。即:[0, position)
  • 或者你可以这样简单理解:endsWith() 方法里的position,表示检索的长度

注意:startsWith() 和 endsWith()这两个方法,他们的 position 的含义是不同的,请仔细区分。

举例:

  1. const name = 'abcdefg';
  2. console.log(name.endsWith('g')); // 打印结果:true
  3. console.log(name.endsWith('f')); // 打印结果:false
  4. // 因为指定了截止位置为3,所以是在 abc 这个长度为3字符串中检索
  5. console.log(name.endsWith('c', 3)); // 打印结果:true
  6. console.log(name.endsWith('d', 3)); // 打印结果:false

注意看上方的注释。

参考链接:JavaScript endsWith()介绍

获取指定位置的字符

1、charAt(index)

语法:

  1. 字符 = str.charAt(index);

解释:返回字符串指定位置的字符。这里的 str.charAt(index)str[index]的效果是一样的。

注意:字符串中第一个字符的下标是 0。如果参数 index 不在 [0, string.length) 之间,该方法将返回一个空字符串。

代码举例

  1. var str = new String('smyhvae');
  2. for (var i = 0; i < str.length; i++) {
  3. console.log(str.charAt(i));
  4. }

打印结果:

(二)JavaScript 对象简介 - 图4

上面这个例子一般不用。一般打印数组和 json 的时候用索引,打印 String 不建议用索引。

2、str[index]

str.charAt(index)str[index]的效果是一样的,不再赘述。区别在于:str[index]是 H5 标准里新增的特性。

3、charCodeAt(index)

语法:

  1. 字符 = str.charCodeAt(index);

解释:返回字符串指定位置的字符的 Unicode 编码。不会修改原字符串。

在实际应用中,通过这个方法,我们可以判断用户按下了哪个按键。

代码举例:打印字符串的占位长度

提示:一个英文占一个位置,一个中文占两个位置。

思路:判断该字符是否在 0-127 之间(在的话是英文,不在是非英文)。

代码实现:

  1. <script>
  2. // sort(); 底层用到了charCodeAt();
  3. var str = 'I love my country!我你爱中国!';
  4. //需求:求一个字符串占有几个字符位。
  5. //思路;如果是英文,站一个字符位,如果不是英文占两个字符位。
  6. //技术点:判断该字符是否在0-127之间。(在的话是英文,不在是非英文)
  7. alert(getZFWlength(str));
  8. alert(str.length);
  9. //定义方法:字符位
  10. function getZFWlength(string) {
  11. //定义一个计数器
  12. var count = 0;
  13. for (var i = 0; i < string.length; i++) {
  14. //对每一位字符串进行判断,如果Unicode编码在0-127,计数器+1;否则+2
  15. if (string.charCodeAt(i) < 128 && string.charCodeAt(i) >= 0) {
  16. count++;
  17. } else {
  18. count += 2;
  19. }
  20. }
  21. return count;
  22. }
  23. </script>

打印结果:

  1. 30
  2. 24

从打印结果可以看出:字符串的长度是 24,但是却占了 30 个字符位(一个中文占两个字符位)。

另外,sort()方法其实底层也是用到了 charCodeAt(),因为用到了 Unicode 编码。

字符串截取

1、slice()

slice() 方法用的最多。

语法:

  1. 新字符串 = str.slice(开始索引, 结束索引); //两个参数都是索引值。包左不包右。

解释:从字符串中截取指定的内容。不会修改原字符串,而是将截取到的内容返回。

注意:上面的参数,包左不包右。参数举例如下:

  • (2, 5) 截取时,包左不包右。
  • (2) 表示从指定的索引位置开始,截取到最后
  • (-3) 表示从倒数第三个开始,截取到最后。
  • (1, -1) 表示从第一个截取到倒数第一个。
  • (5, 2) 表示前面的大,后面的小,返回值为空。

2、substring()

语法:

  1. 新字符串 = str.substring(开始索引, 结束索引); //两个参数都是索引值。包左不包右。

解释:从字符串中截取指定的内容。和slice()类似。

substring()slice()是类似的。但不同之处在于:

  • substring()不能接受负值作为参数。如果传递了一个负值,则默认使用 0。
  • substring()还会自动调整参数的位置,如果第二个参数小于第一个,则自动交换。比如说, substring(1, 0)相当于截取的是第一个字符。

3、substr()

语法:

  1. 字符串 = str.substr(开始索引, 截取的长度);

解释:从字符串中截取指定的内容。不会修改原字符串,而是将截取到的内容返回。

注意,这个方法的第二个参数截取的长度,不是结束索引。

参数举例:

  • (2,4) 从索引值为 2 的字符开始,截取 4 个字符。
  • (1) 从指定位置开始,截取到最后。
  • (-3) 从倒数第几个开始,截取到最后。

备注:ECMAscript 没有对 substr() 方法进行标准化,因此不建议使用它。

String.fromCharCode()

String.fromCharCode():根据字符的 Unicode 编码获取字符。

代码举例:

  1. var result1 = String.fromCharCode(72);
  2. var result2 = String.fromCharCode(20013);
  3. console.log(result1); // 打印结果:H
  4. console.log(result2); // 打印结果:中

concat()

语法:

  1. 新字符串 = str1.concat(str2); //连接两个字符串

解释:字符串的连接。

这种方法基本不用,直接把两个字符串相加就好。

是的,你会发现,数组中也有concat()方法,用于数组的连接。这个方法在数组中用得挺多的。

代码举例:

  1. var str1 = 'qiangu';
  2. var str2 = 'yihao';
  3. var result = str1.concat(str2);
  4. console.log(result); // 打印结果:qianguyihao

split():字符串转换为数组 【重要】

语法:

  1. 新的数组 = str.split(分隔符);

解释:通过指定的分隔符,将一个字符串拆分成一个数组。不会改变原字符串。

备注:split()这个方法在实际开发中用得非常多。一般来说,从接口拿到的 json 数据中,经常会收到类似于"q, i, a, n"这样的字符串,前端需要将这个字符串拆分成['q', 'i', 'a', 'n']数组,这个时候split()方法就派上用场了。

代码举例 1

  1. var str = 'qian, gu, yi, hao'; // 用逗号隔开的字符串
  2. var array = str.split(','); // 将字符串 str 拆分成数组,通过逗号来拆分
  3. console.log(array); // 打印结果是数组:["qian", " gu", " yi", " hao"]

代码举例 2

  1. //split()方法:字符串变数组
  2. var str3 = '千古壹号|qianguyihao|许嵩';
  3. console.log('结果1:' +str3.split()); // 无参数,表示:把整个字符串作为一个元素添加到数组中。
  4. console.log(str3.split('')); // 参数为空字符串,则表示:分隔字符串中每一个字符,分别添加到数组中
  5. console.log(str3.split('|')); // 参数为指定字符,表示:用 '|' 分隔字符串。此分隔符将不会出现在数组的任意一个元素中
  6. console.log(str3.split('许')); // 同上

打印结果:(都是数组)

(二)JavaScript 对象简介 - 图5

replace()

语法:

  1. 新的字符串 = str.replace(被替换的子串,新的子串);

解释:将字符串中的指定内容,替换为新的内容并返回。不会修改原字符串。

注意:这个方法,默认只会替换第一个被匹配到的字符。如果要全局替换,需要使用正则。

代码举例:

  1. //replace()方法:替换
  2. var str2 = 'Today is fine day,today is fine day !';
  3. console.log(str2);
  4. console.log(str2.replace('today', 'tomorrow')); //只能替换第一个today
  5. console.log(str2.replace(/today/gi, 'tomorrow')); //这里用到了正则,才能替换所有的today

repeat():重复字符串

语法:

  1. newStr = str.repeat(重复的次数);

解释:将字符串重复指定的次数。会返回新的值,不会修改原字符串。

举例1:

  1. const name = 'qianguyihao';
  2. console.log(name.repeat(2)); // 打印内容:qianguyihaoqianguyihao

举例2:(模糊字符串的后四位)

  1. const telephone = '13088889999';
  2. const mix_telephone = telephone.slice(0, -4) + '*'.repeat(4); // 模糊电话号码的后四位
  3. console.log(telephone); // 打印结果:13088889999
  4. console.log(mix_telephone); // 打印结果:1308888****

trim()

trim():去除字符串前后的空白。

代码举例:

  1. //去除字符串前后的空格,trim();
  2. let str = ' a b c ';
  3. console.log(str);
  4. console.log(str.length);
  5. console.log(str.trim());
  6. console.log(str.trim().length);

打印结果:

(二)JavaScript 对象简介 - 图6

大小写转换

举例:

  1. var str = 'abcdEFG';
  2. //转换成小写
  3. console.log(str.toLowerCase());
  4. //转换成大写
  5. console.log(str.toUpperCase());

html 方法

  • anchor() 创建 a 链接
  • big()
  • sub()
  • sup()
  • link()
  • bold()

注意,str.link() 返回值是字符串。

举例:

  1. var str = '你好';
  2. console.log(str.anchor());
  3. console.log(str.big());
  4. console.log(str.sub());
  5. console.log(str.sup());
  6. console.log(str.link('http://www.baidu.com'));
  7. console.log(str.bold());

(二)JavaScript 对象简介 - 图7

字符串练习

练习 1:”smyhvaevaesmyh”查找字符串中所有 m 出现的位置。

代码实现:

  1. var str2 = 'smyhvaevaesmyh';
  2. for (var i = 0; i < str2.length; i++) {
  3. //如果指定位置的符号=== "o"
  4. //str2[i]
  5. if (str2.charAt(i) === 'm') {
  6. console.log(i);
  7. }
  8. }

练习 2:判断一个字符串中出现次数最多的字符,统计这个次数

  1. <script>
  2. var str2 = 'smyhvaevaesmyhvae';
  3. //定义一个json,然后判断json中是够有该属性,如果有该属性,那么值+1;否则创建一个该属性,并赋值为1;
  4. var json = {};
  5. for (var i = 0; i < str2.length; i++) {
  6. //判断:如果有该属性,那么值+1;否则创建一个该属性,并赋值为1;
  7. var key = str2.charAt(i);
  8. if (json[key] === undefined) {
  9. json[key] = 1;
  10. } else {
  11. json[key] += 1;
  12. }
  13. }
  14. console.log(json);
  15. console.log('----------------');
  16. //获取json中属性值最大的选项
  17. var maxKey = '';
  18. var maxValue = 0;
  19. for (var k in json) {
  20. // if(maxKey == ""){
  21. // maxKey = k;
  22. // maxValue = json[k];
  23. // }else{
  24. if (json[k] > maxValue) {
  25. maxKey = k;
  26. maxValue = json[k];
  27. }
  28. // }
  29. }
  30. console.log(maxKey);
  31. console.log(maxValue);
  32. </script>

内置对象:Number和Math

内置对象 Number 的常见方法

Number.isInteger() 判断是否为整数

语法:

  1. 布尔值 = Number.isInteger(数字);

toFixed() 小数点后面保留多少位

语法:

  1. 字符串 = myNum.toFixed(num);

解释:将数字 myNum 的小数点后面保留 num 位小数(四舍五入),并返回。不会改变原数字。注意,返回结果是字符串

参数 num:指定了小数点后面的位数。

举例:

  1. let num = 3.456;
  2. let num2 = num.toFixed(2);
  3. console.log(num); // 打印结果:3.456
  4. console.log(num2); // 打印结果:3.46
  5. console.log(typeof num); // number
  6. console.log(typeof num2); // string

上方代码中,num2的结果是3.46,但是请注意,num的类型Number型,而num2的类型却是String型。

内置对象 Math 的常见方法

Math 和其他的对象不同,它不是一个构造函数,不需要创建对象。所以我们不需要 通过 new 来调用,而是直接使用里面的属性和方法即可。

Math属于一个工具类,里面封装了数学运算相关的属性和方法。如下:

方法 描述 备注
Math.PI 圆周率 Math对象的属性
Math.abs() 返回绝对值
Math.random() 生成0-1之间的随机浮点数 取值范围是 [0,1)
Math.floor() 向下取整(往小取值)
Math.ceil() 向上取整(往大取值)
Math.round() 四舍五入取整(正数四舍五入,负数五舍六入)
Math.max(x, y, z) 返回多个数中的最大值
Math.min(x, y, z) 返回多个数中的最小值
Math.pow(x,y) 乘方:返回 x 的 y 次幂
Math.sqrt() 开方:对一个数进行开方运算

举例

  1. var num = -0.6;
  2. console.log(Math.abs(num)); //取绝对值
  3. console.log(Math.floor(num)); //向下取整,向小取
  4. console.log(Math.ceil(num)); //向上取整,向大取
  5. console.log(Math.round(num)); //四舍五入取整(正数四舍五入,负数五舍六入)
  6. console.log(Math.random()); //生成0-1之间的随机数

运行结果:

  1. 0.6
  2. -1
  3. -0
  4. -1
  5. 0.6453756205275165

Math.abs():获绝对值

方法定义:返回绝对值。

注意:

  • 参数中可以接收字符串类型的数字,此时会将字符串做隐式类型转换,然后再调用 Math.abs() 方法。

代码举例:

  1. console.log(Math.abs(2)); // 2
  2. console.log(Math.abs(-2)); // 2
  3. // 先做隐式类型转换,将 '-2'转换为数字类型 -2,然后再调用 Math.abs()
  4. console.log(Math.abs('-2'));
  5. console.log(Math.abs('hello')); // NaN

Math.random() 方法:生成随机数

方法定义:生成 [0, 1) 之间的随机浮点数

我们来看几个例子。

生成 [0, x) 之间的随机数
  1. Math.round(Math.random()*x)

生成 [x, y) 之间的随机数
  1. Math.round(Math.random()*(y-x)+x)

【重要】生成 [x, y]之间的随机整数

也就是说:生成两个整数之间的随机整数,并且要包含这两个整数

这个功能很常用,我们可以将其封装成一个方法,代码实现如下:

  1. /*
  2. * 生成两个整数之间的随机整数,并且要包含这两个整数
  3. */
  4. function getRandom(min, max) {
  5. return Math.floor(Math.random() * (max - min + 1)) + min;
  6. }
  7. console.log(getRandom(1, 10));

举例:随机点名

根据上面的例子,我们还可以再延伸一下,来看看随机点名的例子。

  1. /*
  2. * 生成两个整数之间的随机整数,并且要包含这两个整数
  3. */
  4. function getRandom(min, max) {
  5. return Math.floor(Math.random() * (max - min + 1)) + min;
  6. }
  7. const arr = ['许嵩', '邓紫棋', '毛不易', '解忧邵帅'];
  8. const index = getRandom(0, arr.length - 1); // 生成随机的index
  9. console.log(arr[index]); // 随机点名

pow():乘方

如果想计算 a 的 b 次方,可以使用如下函数:

  1. Math.pow(a, b);

Math的中文是“数学”,pow是“幂”。

举例1:

(二)JavaScript 对象简介 - 图8

代码实现:

  1. var a = Math.pow(3, Math.pow(2, 2));
  2. console.log(a);

举例2:

(二)JavaScript 对象简介 - 图9

代码实现:

  1. var a = Math.pow(Math.pow(3, 2), 4);
  2. console.log(a);

sqrt():开方

如果想计算数值a的开二次方,可以使用如下函数:

  1. Math.sqrt(a);

sqrt即“square 开方”。比如:

  1. var a = Math.sqrt(36);

url 编码和解码

URI (Uniform ResourceIdentifiers,通用资源标识符)进行编码,以便发送给浏览器。有效的URI中不能包含某些字符,例如空格。而这URI编码方法就可以对URI进行编码,它们用特殊的UTF-8编码替换所有无效的字符,从而让浏览器能够接受和理解。

  1. encodeURIComponent(); //把字符串作为 URI 组件进行编码
  2. decodeURIComponent(); //把字符串作为 URI 组件进行解码

举例:

  1. var url = "http://www.cnblogs.com/smyhvae/";
  2. var str = encodeURIComponent(url);
  3. console.log(str); //打印url的编码
  4. console.log(decodeURIComponent(str)); //对url进行编码后,再解码,还原为url

打印结果:

(二)JavaScript 对象简介 - 图10

内置对象:Date

Date 对象在实际开发中,使用得很频繁,且容易在细节地方出错,需要引起重视。

内置对象 Date 用来处理日期和时间。

需要注意的是:与 Math 对象不同,Date 对象是一个构造函数 ,需要先实例化后才能使用。

创建Date对象

创建Date对象有两种写法:

  • 写法一:如果Date()不写参数,就返回当前时间对象
  • 写法二:如果Date()里面写参数,就返回括号里输入的时间对象

针对这两种写法,我们来具体讲一讲。

写法一:不传递参数时,则获取系统的当前时间对象

代码举例:

  1. var date1 = new Date();
  2. console.log(date1);
  3. console.log(typeof date1);

代码解释:不传递参数时,表示的是获取系统的当前时间对象。也可以理解成是:获取当前代码执行的时间。

打印结果:

  1. Mon Feb 17 2020 21:57:22 GMT+0800 (中国标准时间)
  2. object

写法二:传递参数

传递参数时,表示获取指定时间的时间对象。参数中既可以传递字符串,也可以传递数字,也可以传递时间戳。

通过传参的这种写法,我们可以把时间字符串/时间数字/时间戳,按照指定的格式,转换为时间对象。

举例1:(参数是字符串)

  1. const date11 = new Date('2020/02/17 21:00:00');
  2. console.log(date11); // Mon Feb 17 2020 21:00:00 GMT+0800 (中国标准时间)
  3. const date12 = new Date('2020/04/19'); // 返回的就是四月
  4. console.log(date12); // Sun Apr 19 2020 00:00:00 GMT+0800 (中国标准时间)
  5. const date13 = new Date('2020-05-20');
  6. console.log(date13); // Wed May 20 2020 08:00:00 GMT+0800 (中国标准时间)
  7. const date14 = new Date('Wed Jan 27 2017 12:00:00 GMT+0800 (中国标准时间)');
  8. console.log(date14); // Fri Jan 27 2017 12:00:00 GMT+0800 (中国标准时间)

举例2:(参数是多个数字)

  1. const date21 = new Date(2020, 2, 18); // 注意,第二个参数返回的是三月,不是二月
  2. console.log(date21); // Wed Mar 18 2020 00:00:00 GMT+0800 (中国标准时间)
  3. const date22 = new Date(2020, 3, 18, 22, 59, 58);
  4. console.log(date22); // Sat Apr 18 2020 22:59:58 GMT+0800 (中国标准时间)
  5. const params = [2020, 06, 12, 16, 20, 59];
  6. const date23 = new Date(...params);
  7. console.log(date23); // Sun Jul 12 2020 16:20:59 GMT+0800 (中国标准时间)

举例3:(参数是时间戳)

  1. const date31 = new Date(1591950413388);
  2. console.log(date31); // Fri Jun 12 2020 16:26:53 GMT+0800 (中国标准时间)
  3. // 先把时间对象转换成时间戳,然后把时间戳转换成时间对象
  4. const timestamp = new Date().getTime();
  5. const date32 = new Date(timestamp);
  6. console.log(date32); // Fri Jun 12 2020 16:28:21 GMT+0800 (中国标准时间)

日期的格式化

上一段内容里,我们获取到了 Date 对象,但这个对象,打印出来的结果并不是特别直观。

如果我们需要获取日期的指定部分,就需要用到 Date对象自带的方法。

获取了日期指定的部分之后,我们就可以让日期按照指定的格式,进行展示(即日期的格式化)。比如说,我期望能以 2020-02-02 19:30:59 这种格式进行展示。

在这之前,我们先来看看 Date 对象有哪些方法。

Date对象的方法

Date对象 有如下方法,可以获取日期和时间的指定部分

方法名 含义 备注
getFullYear() 获取年份
getMonth() 获取月: 0-11 0代表一月
getDate() 获取日:1-31 获取的是几号
getDay() 获取星期:0-6 0代表周日,1代表周一
getHours() 获取小时:0-23
getMinutes() 获取分钟:0-59
getSeconds() 获取秒:0-59
getMilliseconds() 获取毫秒 1s = 1000ms

代码举例

  1. // 我在执行这行代码时,当前时间为 2019年2月4日,周一,13:23:52
  2. var myDate = new Date();
  3. console.log(myDate); // 打印结果:Mon Feb 04 2019 13:23:52 GMT+0800 (中国标准时间)
  4. console.log(myDate.getFullYear()); // 打印结果:2019
  5. console.log(myDate.getMonth() + 1); // 打印结果:2
  6. console.log(myDate.getDate()); // 打印结果:4
  7. var dayArr = ['星期日', '星期一', '星期二', '星期三', '星期四','星期五', '星期六'];
  8. console.log(myDate.getDay()); // 打印结果:1
  9. console.log(dayArr[myDate.getDay()]); // 打印结果:星期一
  10. console.log(myDate.getHours()); // 打印结果:13
  11. console.log(myDate.getMinutes()); // 打印结果:23
  12. console.log(myDate.getSeconds()); // 打印结果:52
  13. console.log(myDate.getMilliseconds()); // 打印结果:393
  14. console.log(myDate.getTime()); // 获取时间戳。打印结果:1549257832393

获取了日期和时间的指定部分之后,我们把它们用字符串拼接起来,就可以按照自己想要的格式,来展示日期。

举例:年月日的格式化

代码举例:

  1. console.log(formatDate());
  2. /*
  3. 方法:日期格式化。
  4. 格式要求:今年是:2020年02月02日 08:57:09 星期日
  5. */
  6. function formatDate() {
  7. var date = new Date();
  8. var year = date.getFullYear(); // 年
  9. var month = date.getMonth() + 1; // 月
  10. var day = date.getDate(); // 日
  11. var week = date.getDay(); // 星期几
  12. var weekArr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
  13. var hour = date.getHours(); // 时
  14. hour = hour < 10 ? '0' + hour : hour; // 如果只有一位,则前面补零
  15. var minute = date.getMinutes(); // 分
  16. minute = minute < 10 ? '0' + minute : minute; // 如果只有一位,则前面补零
  17. var second = date.getSeconds(); // 秒
  18. second = second < 10 ? '0' + second : second; // 如果只有一位,则前面补零
  19. var result = '今天是:' + year + '年' + month + '月' + day + '日 ' + hour + ':' + minute + ':' + second + ' ' + weekArr[week];
  20. return result;
  21. }

获取时间戳

时间戳的定义和作用

时间戳:指的是从格林威治标准时间的1970年1月1日,0时0分0秒到当前日期所花费的毫秒数(1秒 = 1000毫秒)。

计算机底层在保存时间时,使用的都是时间戳。时间戳的存在,就是为了统一时间的单位。

我们经常会利用时间戳来计算时间,因为它更精确。而且,在实战开发中,接口返回给前端的日期数据,都是以时间戳的形式。

我们再来看下面这样的代码:

  1. var myDate = new Date("1970/01/01 0:0:0");
  2. console.log(myDate.getTime()); // 获取时间戳

打印结果(可能会让你感到惊讶)

  1. -28800000

为啥打印结果是-28800000,而不是0呢?这是因为,我们的当前代码,是在中文环境下运行的,与英文时间会存在8个小时的时差(中文时间比英文时间早了八个小时)。如果代码是在英文环境下运行,打印结果就是0

getTime():获取时间戳

getTime() 获取日期对象的时间戳(单位:毫秒)。这个方法在实战开发中,用得比较多。但还有比它更常用的写法,我们往下看。

获取 Date 对象的时间戳

代码演示:

  1. // 方式一:获取 Date 对象的时间戳(最常用的写法)
  2. const timestamp1 = +new Date();
  3. console.log(timestamp1); // 打印结果举例:1589448165370
  4. // 方式二:获取 Date 对象的时间戳(较常用的写法)
  5. const timestamp2 = new Date().getTime();
  6. console.log(timestamp2); // 打印结果举例:1589448165370
  7. // 方式三:获取 Date 对象的时间戳
  8. const timestamp3 = new Date().valueOf();
  9. console.log(timestamp3); // 打印结果举例:1589448165370
  10. // 方式4:获取 Date 对象的时间戳
  11. const timestamp4 = new Date() * 1;
  12. console.log(timestamp4); // 打印结果举例:1589448165370
  13. // 方式5:获取 Date 对象的时间戳
  14. const timestamp5 = Number(new Date());
  15. console.log(timestamp5); // 打印结果举例:1589448165370

上面这五种写法都可以获取任意 Date 对象的时间戳,最常见的写法是方式一,其次是方式二。

根据前面所讲的关于「时间戳」的概念,上方代码获取到的时间戳指的是:从 1970年1月1日,0时0分0秒 到现在所花费的总毫秒数。

获取当前时间的时间戳

如果我们要获取当前时间的时间戳,除了上面的几种方式之外,还有另一种方式。代码如下:

  1. // 方式六:获取当前时间的时间戳(很常用的写法)
  2. console.log(Date.now()); // 打印结果举例:1589448165370

上面这种方式六,用得也很多。只不过,Date.now()是H5标准中新增的特性,如果你的项目需要兼容低版本的IE浏览器,就不要用了。这年头,谁还用IE呢?

利用时间戳检测代码的执行时间

我们可以在业务代码的前面定义 时间戳1,在业务代码的后面定义 时间戳2。把这两个时间戳相减,就能得出业务代码的执行时间。

format()

将时间对象转换为指定格式。

参考链接:https://www.cnblogs.com/tugenhua0707/p/3776808.html

练习

举例1:模拟日历

要求每天打开这个页面,都能定时显示当前的日期。

代码实现:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8" />
  5. <title></title>
  6. <style>
  7. div {
  8. width: 800px;
  9. margin: 200px auto;
  10. color: red;
  11. text-align: center;
  12. font: 600 30px/30px 'simsun';
  13. }
  14. </style>
  15. </head>
  16. <body>
  17. <div></div>
  18. <script>
  19. //模拟日历
  20. //需求:每天打开这个页面都能定时显示年月日和星期几
  21. function getCurrentDate() {
  22. //1.创建一个当前日期的日期对象
  23. const date = new Date();
  24. //2.然后获取其中的年、月、日和星期
  25. const year = date.getFullYear();
  26. const month = date.getMonth();
  27. const hao = date.getDate();
  28. const week = date.getDay();
  29. // console.log(year+" "+month+" "+hao+" "+week);
  30. //3.赋值给div
  31. const arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
  32. const div = document.getElementsByTagName('div')[0];
  33. return '今天是:' + year + '年' + (month + 1) + '月' + hao + '日 ' + arr[week];
  34. }
  35. const div = document.getElementsByTagName('div')[0];
  36. div.innerText = getCurrentDate();
  37. </script>
  38. </body>
  39. </html>

实现效果:

(二)JavaScript 对象简介 - 图11

举例2:发布会倒计时

实现思路:

  • 设置一个定时器,每间隔1毫秒就自动刷新一次div的内容。
  • 核心算法:输入的时间戳减去当前的时间戳,就是剩余时间(即倒计时),然后转换成时分秒。

代码实现:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8" />
  5. <title></title>
  6. <style>
  7. div {
  8. width: 1210px;
  9. margin: 200px auto;
  10. color: red;
  11. text-align: center;
  12. font: 600 30px/30px 'simsun';
  13. }
  14. </style>
  15. </head>
  16. <body>
  17. <div></div>
  18. <script>
  19. var div = document.getElementsByTagName('div')[0];
  20. var timer = setInterval(() => {
  21. countDown('2022/02/03 11:20:00');
  22. }, 1);
  23. function countDown(myTime) {
  24. var nowTime = new Date();
  25. var future = new Date(myTime);
  26. var timeSum = future.getTime() - nowTime.getTime(); //获取时间差:发布会时间减去此刻的毫秒值
  27. var day = parseInt(timeSum / 1000 / 60 / 60 / 24); // 天
  28. var hour = parseInt((timeSum / 1000 / 60 / 60) % 24); // 时
  29. var minu = parseInt((timeSum / 1000 / 60) % 60); // 分
  30. var sec = parseInt((timeSum / 1000) % 60); // 秒
  31. var millsec = parseInt(timeSum % 1000); // 毫秒
  32. //细节处理:所有的时间小于10的时候,在前面自动补0,毫秒值要补双0(比如如,把 8 秒改成 08 秒)
  33. day = day < 10 ? '0' + day : day; //day小于10吗?如果小于,就补0;如果不小于,就是day本身
  34. hour = hour < 10 ? '0' + hour : hour;
  35. minu = minu < 10 ? '0' + minu : minu;
  36. sec = sec < 10 ? '0' + sec : sec;
  37. if (millsec < 10) {
  38. millsec = '00' + millsec;
  39. } else if (millsec < 100) {
  40. millsec = '0' + millsec;
  41. }
  42. // 兜底处理
  43. if (timeSum < 0) {
  44. div.innerHTML = '距离苹果发布会还有00天00小时00分00秒000毫秒';
  45. clearInterval(timer);
  46. return;
  47. }
  48. // 前端要显示的文案
  49. div.innerHTML = '距离苹果发布会还有' + day + '天' + hour + '小时' + minu + '分' + sec + '秒' + millsec + '毫秒';
  50. }
  51. </script>
  52. </body>
  53. </html>

实现效果:

(二)JavaScript 对象简介 - 图12

Moment.js

Moment.js 是一个轻量级的JavaScript时间库,我们可以利用它很方便地进行时间操作,提升开发效率。

使用举例:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.26.0/moment.min.js"></script>
  10. <script>
  11. // 按照指定的格式,格式化当前时间
  12. console.log(moment().format('YYYY-MM-DD HH:mm:ss')); // 打印结果举例:2020-06-12 16:38:38
  13. console.log(typeof moment().format('YYYY-MM-DD HH:mm:ss')); // 打印结果:string
  14. // 按照指定的格式,格式化指定的时间
  15. console.log(moment('2020/06/12 18:01:59').format('YYYY-MM-DD HH:mm:ss')); // 打印结果:2020-06-12 18:01:59
  16. // 按照指定的格式,获取七天后的时间
  17. console.log(moment().add(7, 'days').format('YYYY-MM-DD hh:mm:ss')); // 打印结果举例:2020-06-19 04:43:56
  18. </script>
  19. </body>
  20. </html>

数组

之前学习的数据类型,只能存储一个值(字符串也为一个值)。如果我们想存储多个值,就可以使用数组。

数组简介

数组(Array)是属于内置对象,我们可以在MDN网站上查询它的各种方法。

数组和普通对象的功能类似,也是用来存储一些值的。不同的是:

  • 普通对象是使用字符串作为属性名的,而数组是使用数字作为索引来操作元素。索引:从 0 开始的整数就是索引。

数组的存储性能比普通对象要好。在实际开发中我们经常使用数组来存储一些数据(尤其是列表数据),使用频率非常高。

(二)JavaScript 对象简介 - 图13

比如说,上面这个页面的列表数据,它的结构就是一个大数组。

数组中的元素可以是任意的数据类型,也可以是对象,也可以是函数,也可以是数组。数组的元素中,如果存放的是数组,我们就称这种数组为二维数组。

接下来,我们讲一讲数组的基本操作。

创建数组对象

方式一:使用字面量创建数组

举例:

  1. var arr1 = []; // 创建一个空的数组
  2. var arr2 = [1, 2, 3]; // 创建带初始值的数组

方式一最简单,也用得最多。

方式二:使用构造函数创建数组

语法:

  1. let arr = new Array(参数);
  2. let arr = Array(参数);

如果参数为空,则表示创建一个空数组;如果参数是一个数值时,表示数组的长度;如果有多个参数时,表示数组中的元素。

来举个例子:

  1. // 方式一
  2. var arr1 = [11, 12, 13];
  3. // 方式二
  4. var arr2 = new Array(); // 参数为空
  5. var arr3 = new Array(4); // 参数为一个数值
  6. var arr4 = new Array(15, 16, 17); // 参数为多个数值
  7. console.log(typeof arr1); // 打印结果:object
  8. console.log('arr1 = ' + JSON.stringify(arr1));
  9. console.log('arr2 = ' + JSON.stringify(arr2));
  10. console.log('arr3 = ' + JSON.stringify(arr3));
  11. console.log('arr4 = ' + JSON.stringify(arr4));

打印结果:

  1. object;
  2. arr1 = [11, 12, 13];
  3. arr2 = [];
  4. arr3 = [null, null, null, null];
  5. arr4 = [15, 16, 17];

从上方打印结果的第一行里,可以看出,数组的类型其实也是属于对象

数组中的元素的类型

数组中可以存放任意类型的数据,例如字符串、数字、布尔值、对象等。

比如:

  1. const arr = ['qianguyihao', 28, true, { name: 'qianguyihao' }];

我们甚至还可以存放多维数组(数组里面放数组)。比如:

  1. const arr2 = [
  2. [11, 12, 13],
  3. [21, 22, 23],
  4. ];

数组的基本操作

数组的索引

索引 (下标) :用来访问数组元素的序号,代表的是数组中的元素在数组中的位置(下标从 0 开始算起)。

数组可以通过索引来访问、设置、修改对应的数组元素。我们继续看看。

向数组中添加元素

语法:

  1. 数组[索引] = 值;

代码举例:

  1. var arr = [];
  2. // 向数组中添加元素
  3. arr[0] = 10;
  4. arr[1] = 20;
  5. arr[2] = 30;
  6. arr[3] = 40;
  7. arr[5] = 50;
  8. console.log(JSON.stringify(arr));

打印结果:

  1. [10,20,30,40,null,50]

获取数组中的元素

语法:

  1. 数组[索引];

如果读取不存在的索引(比如元素没那么多),系统不会报错,而是返回 undefined。

代码举例:

  1. var arr = [21, 22, 23];
  2. console.log(arr[0]); // 打印结果:21
  3. console.log(arr[5]); // 打印结果:undefined

获取数组的长度

可以使用length属性来获取数组的长度(即“元素的个数”)。

数组的长度是元素个数,不要跟索引号混淆。

语法:

  1. 数组的长度 = 数组名.length

代码举例:

  1. var arr = [21, 22, 23];
  2. console.log(arr.length); // 打印结果:3

补充:

对于连续的数组,使用 length 可以获取到数组的长度(元素的个数);对于非连续的数组(即“稀疏数组”,下一段会讲),length 的值会大于元素的个数。因此,尽量不要创建非连续的数组。

修改数组的长度(修改 length)
  • 如果修改的 length 大于原长度,则多出部分会空出来,置为 null。
  • 如果修改的 length 小于原长度,则多出的元素会被删除,数组将从后面删除元素。
  • (特例:伪数组 arguments 的长度可以修改,但是不能修改里面的元素,后面单独讲。)

代码举例:

  1. var arr1 = [11, 12, 13];
  2. var arr2 = [21, 22, 23];
  3. // 修改数组 arr1 的 length
  4. arr1.length = 1;
  5. console.log(JSON.stringify(arr1));
  6. // 修改数组 arr2 的 length
  7. arr2.length = 5;
  8. console.log(JSON.stringify(arr2));

打印结果:

  1. [11][(21, 22, 23, null, null)];

遍历数组

遍历: 就是把数组中的每个元素从头到尾都访问一次。

最简单的做法是通过 for 循环,遍历数组中的每一项。举例:

  1. var arr = [10, 20, 30, 40, 50];
  2. for (var i = 0; i < arr.length; i++) {
  3. console.log(arr[i]); // 打印出数组中的每一项
  4. }

下一篇文章,会学习数组的各种方法,到时候,会有更多的做法去遍历数组。

稀疏数组与密集数组

  • 稀疏数组:索引不连续、数组长度大于元素个数的数组,可以简单理解为有 empty(有空隙)的数组。
  • 密集数组:索引连续、数组长度等于元素个数的数组。

参考链接:

案例

例 1:翻转数组

代码实现:

  1. var arr = [10, 20, 30, 40, 50]; // 原始数组
  2. var newArr = []; // 翻转后的数组
  3. for (var i = 0; i < arr.length; i++) {
  4. newArr[i] = arr[arr.length - i - 1];
  5. }
  6. console.log(JSON.stringify(newArr));

打印结果:

  1. [50,40,30,20,10]

例 2:冒泡排序

代码实现:

  1. var arr = [20, 10, 50, 30, 40];
  2. for (var i = 0; i < arr.length - 1; i++) {
  3. for (var j = 0; j < arr.length - i - 1; j++) {
  4. if (arr[j] > arr[j + 1]) {
  5. var temp = arr[j];
  6. arr[j] = arr[j + 1];
  7. arr[j + 1] = temp;
  8. }
  9. }
  10. }
  11. console.log(JSON.stringify(arr));

打印结果:

  1. [10,20,30,40,50]

数组的常见方法

数组的方法清单

数组的类型相关
方法 描述 备注
Array.isArray() 判断是否为数组
toString() 将数组转换为字符串
Array.from(arrayLike) 伪数组转化为真数组
Array.of(value1, value2, value3) 创建数组:将一系列值转换成数组

注意,获取数组的长度是用length属性,不是方法。关于 length属性,详见上一篇文章。

数组元素的添加和删除
方法 描述 备注
push() 向数组的最后面插入一个或多个元素,返回结果为新数组的长度 会改变原数组
pop() 删除数组中的最后一个元素,返回结果为被删除的元素 会改变原数组
unshift() 在数组最前面插入一个或多个元素,返回结果为新数组的长度 会改变原数组
shift() 删除数组中的第一个元素,返回结果为被删除的元素 会改变原数组
slice() 从数组中提取指定的一个或多个元素,返回结果为新的数组 不会改变原数组
splice() 从数组中删除指定的一个或多个元素,返回结果为被删除元素组成的新数组 会改变原数组
fill() 填充数组:用固定的值填充数组,返回结果为新的数组 会改变原数组

数组的合并和拆分
方法 描述 备注
concat() 合并数组:连接两个或多个数组,返回结果为新的数组 不会改变原数组
join() 将数组转换为字符串,返回结果为转换后的字符串 不会改变原数组
split() 将字符串按照指定的分隔符,组装为数组 不会改变原字符串

注意,split()是字符串的方法,不是数组的方法。

数组排序
方法 描述 备注
reverse() 反转数组,返回结果为反转后的数组 会改变原数组
sort() 对数组的元素,默认按照Unicode 编码,从小到大进行排序 会改变原数组

查找数组的元素
方法 描述 备注
indexOf(value) 从前往后索引,检索一个数组中是否含有指定的元素
lastIndexOf(value) 从后往前索引,检索一个数组中是否含有指定的元素
includes(item) 数组中是否包含指定的内容
find(function()) 找出第一个满足「指定条件返回 true」的元素
findIndex(function()) 找出第一个满足「指定条件返回 true」的元素的 index
every() 确保数组中的每个元素都满足「指定条件返回 true」,则停止遍历,此方法才返回 true 全真才为真。要求每一项都返回 true,最终的结果才返回 true
some() 数组中只要有一个元素满足「指定条件返回 true」,则停止遍历,此方法就返回 true 一真即真。只要有一项返回 true,最终的结果就返回 true

遍历数组
方法 描述 备注
for 循环 这个大家都懂
forEach() 和 for 循环类似,但需要兼容 IE8 以上 forEach() 没有返回值。也就是说,它的返回值是 undefined
map() 对原数组中的每一项进行加工,将组成新的数组 不会改变原数组
filter() 过滤数组:返回结果是 true 的项,将组成新的数组,返回结果为新的数组 不会改变原数组
reduce 接收一个函数作为累加器,返回值是回调函数累计处理的结果

isArray():判断是否为数组

语法:

  1. 布尔值 = Array.isArray(被检测的数组);

以前,我们会通过 A instanceof B来判断 A 是否属于 B 类型。但是在数组里,这种 instanceof 方法已经用的不多了,因为有 isArray()方法。

toString():数组转换为字符串

  1. 字符串 = 数组.toString();

解释:把数组转换成字符串,每一项用,分割。

备注:大多数的数据类型都可以使用.toString()方法,将其转换为字符串。

知识点补充:将数组转换为字符串,有哪些方式

方式 1:

  1. 字符串 = 数组.toString();

方式 2:

  1. 字符串 = String(数组);

方式 3:

  1. 字符串 = 数组.join(','); // 将数组转为字符串,每一项用 英文逗号 分隔

Array.from():将伪数组转换为真数组

语法

  1. array = Array.from(arrayLike);

作用:将伪数组或可遍历对象转换为真数组

代码举例:

  1. const name = 'qianguyihao';
  2. console.log(Array.from(name)); // 打印结果是数组:["q","i","a","n","g","u","y","i","h","a","o"]

伪数组与真数组的区别

伪数组:包含 length 属性的对象或可迭代的对象。

另外,伪数组的原型链中没有 Array.prototype,而真数组的原型链中有 Array.prototype。因此伪数组没有数组的一般方法,比如 pop()、join() 等方法。

伪数组举例
  1. <body>
  2. <button>按钮1</button>
  3. <button>按钮2</button>
  4. <button>按钮3</button>
  5. <script>
  6. let btnArray = document.getElementsByTagName('button');
  7. console.log(btnArray);
  8. console.log(btnArray[0]);
  9. </script>
  10. </body>

上面的布局中,有三个 button 标签,我们通过getElementsByTagName获取到的btnArray实际上是伪数组,并不是真实的数组:

(二)JavaScript 对象简介 - 图14

既然btnArray是伪数组,它就不能使用数组的一般方法,否则会报错:

(二)JavaScript 对象简介 - 图15

解决办法:采用Array.from方法将btnArray这个伪数组转换为真数组即可:

  1. Array.from(btnArray);

然后就可以使用数组的一般方法了:

(二)JavaScript 对象简介 - 图16

Array.of():创建数组

语法

  1. Array.of(value1, value2, value3);

作用:根据参数里的内容,创建数组。

举例

  1. const arr = Array.of(1, 'abc', true);
  2. console.log(arr); // 打印结果是数组:[1, "abc", true]

补充:new Array()Array.of()的区别在于:当参数只有一个时,前者表示数组的长度,后者表示数组中的内容。

数组元素的添加和删除

push()

push():向数组的最后面插入一个或多个元素,返回结果为新数组的长度。会改变原数组,因为原数组变成了新数组。

语法:

  1. 新数组的长度 = 数组.push(元素);

代码举例:

  1. var arr = ['王一', '王二', '王三'];
  2. var result1 = arr.push('王四'); // 末尾插入一个元素
  3. var result2 = arr.push('王五', '王六'); // 末尾插入多个元素
  4. console.log(result1); // 打印结果:4
  5. console.log(result2); // 打印结果:6
  6. console.log(JSON.stringify(arr)); // 打印结果:["王一","王二","王三","王四","王五","王六"]

pop()

pop():删除数组中的最后一个元素,返回结果为被删除的元素

语法:

  1. 被删除的元素 = 数组.pop();

代码举例:

  1. var arr = ['王一', '王二', '王三'];
  2. var result1 = arr.pop();
  3. console.log(result1); // 打印结果:王三
  4. console.log(JSON.stringify(arr)); // 打印结果:["王一","王二"]

unshift()

unshift():在数组最前面插入一个或多个元素,返回结果为新数组的长度。会改变原数组,因为原数组变成了新数组。插入元素后,其他元素的索引会依次调整。

语法:

  1. 新数组的长度 = 数组.unshift(元素);

代码举例:

  1. var arr = ['王一', '王二', '王三'];
  2. var result1 = arr.unshift('王四'); // 最前面插入一个元素
  3. var result2 = arr.unshift('王五', '王六'); // 最前面插入多个元素
  4. console.log(result1); // 打印结果:4
  5. console.log(result2); // 打印结果:6
  6. console.log(JSON.stringify(arr)); // 打印结果:["王五","王六","王四","王一","王二","王三"]

shift()

shift():删除数组中的第一个元素,返回结果为被删除的元素

语法:

  1. 被删除的元素 = 数组.shift();

代码举例:

  1. var arr = ['王一', '王二', '王三'];
  2. var result1 = arr.shift();
  3. console.log(result1); // 打印结果:王一
  4. console.log(JSON.stringify(arr)); // 打印结果:["王二","王三"]

slice()

slice():从数组中提取指定的一个或者多个元素,返回结果为新的数组(不会改变原来的数组)。

备注:该方法不会改变原数组,而是将截取到的元素封装到一个新数组中返回。

语法

  1. 新数组 = 原数组.slice(开始位置的索引, 结束位置的索引); //注意:包含开始索引,不包含结束索引

举例:

  1. const arr = ['a', 'b', 'c', 'd', 'e', 'f'];
  2. const result1 = arr.slice(); // 不加参数时,则获取所有的元素。相当于数组的整体赋值
  3. const result2 = arr.slice(2); // 从第二个值开始提取,直到末尾
  4. const result3 = arr.slice(-2); // 提取最后两个元素
  5. const result4 = arr.slice(2, 4); // 提取从第二个到第四个之间的元素(不包括第四个元素)
  6. const result5 = arr.slice(4, 2); // 空
  7. console.log('arr:' + JSON.stringify(arr));
  8. console.log('result1:' + JSON.stringify(result1));
  9. console.log('result2:' + JSON.stringify(result2));
  10. console.log('result3:' + JSON.stringify(result3));
  11. console.log('result4:' + JSON.stringify(result4));
  12. console.log('result5:' + JSON.stringify(result5));

打印结果:

  1. arr: ['a', 'b', 'c', 'd', 'e', 'f'];
  2. result1: ['a', 'b', 'c', 'd', 'e', 'f'];
  3. result2: ['c', 'd', 'e', 'f'];
  4. result3: ['e', 'f'];
  5. result4: ['c', 'd'];
  6. result5: [];

补充

很多前端开发人员会用 slice()将伪数组,转化为真数组。写法如下:

  1. // 方式1
  2. array = Array.prototype.slice.call(arrayLike);
  3. // 方式2
  4. array = [].slice.call(arrayLike);

ES6 看不下去这种蹩脚的转化方法,于是出了一个新的 API:(专门用来将伪数组转化成真数组)

  1. array = Array.from(arrayLike);

关于这个 API 的详细介绍,上面的内容已经讲了,请往前翻。

splice()

splice():从数组中删除指定的一个或多个元素,返回结果为被删除元素组成的新数组(会改变原来的数组)。

备注:该方法会改变原数组,会将指定元素从原数组中删除;被删除的元素会封装到一个新的数组中返回。

语法:

  1. 新数组 = 原数组.splice(起始索引index, 需要删除的个数);
  2. 新数组 = 原数组.splice(起始索引index, 需要删除的个数, 新的元素1, 新的元素2...);

上方语法中,第三个及之后的参数,表示:删除元素之后,向原数组中添加新的元素,这些元素将会自动插入到起始位置索引的前面。也可以理解成:删除了哪些元素,就在那些元素的所在位置补充新的内容。

slice()方法和splice()方法很容易搞混,请一定要注意区分。

举例 1:

  1. var arr1 = ['a', 'b', 'c', 'd', 'e', 'f'];
  2. var result1 = arr1.splice(1); //从第index为1的位置开始,删除元素
  3. console.log('arr1:' + JSON.stringify(arr1));
  4. console.log('result1:' + JSON.stringify(result1));

打印结果:

  1. arr1:["a"]
  2. result1:["b","c","d","e","f"]

举例 2:

  1. var arr2 = ['a', 'b', 'c', 'd', 'e', 'f'];
  2. var result2 = arr2.splice(-2); //删除最后两个元素
  3. console.log('arr2:' + JSON.stringify(arr2));
  4. console.log('result2:' + JSON.stringify(result2));

打印结果:

  1. arr2:["a","b","c","d"]
  2. result2:["e","f"]

举例 3:

  1. var arr3 = ['a', 'b', 'c', 'd', 'e', 'f'];
  2. var result3 = arr3.splice(1, 3); //从第index为1的位置开始删除元素,一共删除三个元素
  3. console.log('arr3:' + JSON.stringify(arr3));
  4. console.log('result3:' + JSON.stringify(result3));

打印结果:

  1. arr3:["a","e","f"]
  2. result3:["b","c","d"]

举例4:(删除指定元素,用得很多)

  1. const arr4 = ['a', 'b', 'c', 'd'];
  2. arr4.splice(arr4.indexOf('c'), 1); // 删除数组中的'c'这个元素
  3. console.log('arr4:' + JSON.stringify(arr4));

举例 5:(我们来看看第三个参数的用法)

  1. var arr5 = ['a', 'b', 'c', 'd', 'e', 'f'];
  2. //从第index为1的位置开始删除元素,一共删除三个元素。并且在 index=1 的前面追加两个元素
  3. var result5 = arr5.splice(1, 3, '千古壹号', 'vae');
  4. console.log('arr5' + JSON.stringify(arr5));
  5. console.log('result4:' + JSON.stringify(result5));

打印结果:

  1. arr5["a","千古壹号","vae","e","f"]
  2. result5["b","c","d"]

fill()

fill():用一个固定值填充数组,返回结果为新的数组。会改变原数组。

语法:

  1. // 用一个固定值填充数组。数组里的每个元素都会被这个固定值填充
  2. 新数组 = 数组.fill(固定值);
  3. // 从 startIndex 开始的数组元素,用固定值填充
  4. 新数组 = 数组.fill(固定值, startIndex);
  5. // 从 startIndex 到 endIndex 之间的元素(包左不包右),用固定值填充
  6. 新数组 = 数组.fill(固定值, startIndex, endIndex);

举例1:

  1. // 创建一个长度为4的空数组,然后用 'f' 来填充这个空数组
  2. console.log(Array(4).fill('f')); // ['f', 'f', 'f,' 'f']
  3. // 将现有数组的每一个元素都进行填充
  4. console.log(['a', 'b', 'c', 'd'].fill('f')); // ['f', 'f', 'f,' 'f']

举例2:

  1. // 指定位置进行填充
  2. let arr1 = ['a', 'b', 'c', 'd'];
  3. let arr2 = arr1.fill('f', 1, 3);
  4. console.log(arr1); // ['a', 'f', 'f,' 'd']
  5. console.log(arr2); // ['a', 'f', 'f,' 'd']

数组的合并和拆分

concat()

concat():连接两个或多个数组,返回结果为新的数组。不会改变原数组。concat()方法的作用是数组合并

语法:

  1. 新数组 = 数组1.concat(数组2, 数组3 ...);

举例:

  1. const arr1 = [1, 2, 3];
  2. const arr2 = ['a', 'b', 'c'];
  3. const arr3 = ['千古壹号', 'vae'];
  4. const result1 = arr1.concat(arr2);
  5. const result2 = arr2.concat(arr1, arr3);
  6. console.log('arr1 =' + JSON.stringify(arr1));
  7. console.log('arr2 =' + JSON.stringify(arr2));
  8. console.log('arr3 =' + JSON.stringify(arr3));
  9. console.log('result1 =' + JSON.stringify(result1));
  10. console.log('result2 =' + JSON.stringify(result2));

打印结果:

  1. arr1 = [1, 2, 3];
  2. arr2 = ['a', 'b', 'c'];
  3. arr3 = ['千古壹号', 'vae'];
  4. result1 = [1, 2, 3, 'a', 'b', 'c'];
  5. result2 = ['a', 'b', 'c', 1, 2, 3, '千古壹号', 'vae'];

从打印结果中可以看到,原数组并没有被修改。

数组合并的另一种方式

我们可以使用...这种展开语法,将两个数组进行合并。举例如下:

  1. const arr1 = [1, 2, 3];
  2. const result = ['a', 'b', 'c', ...arr1];
  3. console.log(JSON.stringify(result)); // 打印结果:["a","b","c",1,2,3]

join()

join():将数组转换为字符串,返回结果为转换后的字符串(不会改变原来的数组)。

补充:join()方法可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符;如果不指定连接符,则默认使用 , 作为连接符,此时和 toString()的效果是一致的

语法:

  1. 新的字符串 = 原数组.join(参数); // 参数选填

代码举例:

  1. var arr = ['a', 'b', 'c'];
  2. var result1 = arr.join(); // 这里没有指定连接符,所以默认使用 , 作为连接符
  3. var result2 = arr.join('-'); // 使用指定的字符串作为连接符
  4. console.log(typeof arr); // 打印结果:object
  5. console.log(typeof result1); // 打印结果:string
  6. console.log('arr =' + JSON.stringify(arr));
  7. console.log('result1 =' + JSON.stringify(result1));
  8. console.log('result2 =' + JSON.stringify(result2));

上方代码中,最后三行的打印结果是:

  1. arr =["a","b","c"]
  2. result1 =a,b,c
  3. result2 =a-b-c

split()

注意,split()是字符串的方法,不是数组的方法。

语法:

  1. 新的数组 = str.split(分隔符);

解释:通过指定的分隔符,将一个字符串拆分成一个数组。不会改变原字符串。

备注:split()这个方法在实际开发中用得非常多。一般来说,从接口拿到的 json 数据中,经常会收到类似于"q, i, a, n"这样的字符串,前端需要将这个字符串拆分成['q', 'i', 'a', 'n']数组,这个时候split()方法就派上用场了。

reverse()

reverse():反转数组,返回结果为反转后的数组(会改变原来的数组)。

语法:

  1. 反转后的数组 = 数组.reverse();

举例:

  1. var arr = ['a', 'b', 'c', 'd', 'e', 'f'];
  2. var result = arr.reverse(); // 将数组 arr 进行反转
  3. console.log('arr =' + JSON.stringify(arr));
  4. console.log('result =' + JSON.stringify(result));

打印结果:

  1. arr =["f","e","d","c","b","a"]
  2. result =["f","e","d","c","b","a"]

从打印结果可以看出,原来的数组已经被改变了。

sort()方法

sort()方法需要好好理解。

sort():对数组的元素进行从小到大来排序(会改变原来的数组)。

sort()方法:无参时

如果在使用 sort() 方法时不带参,则默认按照Unicode 编码,从小到大进行排序。

举例 1:(当数组中的元素为字符串时)

  1. let arr1 = ['e', 'b', 'd', 'a', 'f', 'c'];
  2. let result = arr1.sort(); // 将数组 arr1 进行排序
  3. console.log('arr1 =' + JSON.stringify(arr1));
  4. console.log('result =' + JSON.stringify(result));

打印结果:

  1. arr1 =["a","b","c","d","e","f"]
  2. result =["a","b","c","d","e","f"]

从上方的打印结果中,我们可以看到,sort 方法会改变原数组,而且方法的返回值也是同样的结果。

举例 2:(当数组中的元素为数字时)

  1. let arr2 = [5, 2, 11, 3, 4, 1];
  2. let result = arr2.sort(); // 将数组 arr2 进行排序
  3. console.log('arr2 =' + JSON.stringify(arr2));
  4. console.log('result =' + JSON.stringify(result));

打印结果:

  1. arr2 =[1,11,2,3,4,5]
  2. result =[1,11,2,3,4,5]

上方的打印结果中,你会发现,使用 sort() 排序后,数字11竟然在数字2的前面。这是为啥呢?因为上面讲到了,sort()方法是按照Unicode 编码进行排序的。

那如果我想让 arr2 里的数字,完全按照从小到大排序,怎么操作呢?继续往下看。

sort()方法:带参时,自定义排序规则

如果在 sort()方法中带参,我们就可以自定义排序规则。具体做法如下:

我们可以在 sort()添加一个回调函数,来指定排序规则。回调函数中需要定义两个形参,浏览器将会分别使用数组中的元素作为实参去调用回调函数。

浏览器根据回调函数的返回值来决定元素的排序:(重要)

  • 如果返回一个大于 0 的值,则元素会交换位置
  • 如果返回一个小于 0 的值,则元素位置不变
  • 如果返回一个等于 0 的值,则认为两个元素相等,则不交换位置

如果只是看上面的文字,可能不太好理解,我们来看看下面的例子,你肯定就能明白。

举例:将数组中的数字按照从小到大排序

写法 1

  1. var arr = [5, 2, 11, 3, 4, 1];
  2. // 自定义排序规则
  3. var result = arr.sort(function (a, b) {
  4. if (a > b) {
  5. // 如果 a 大于 b,则交换 a 和 b 的位置
  6. return 1;
  7. } else if (a < b) {
  8. // 如果 a 小于 b,则位置不变
  9. return -1;
  10. } else {
  11. // 如果 a 等于 b,则位置不变
  12. return 0;
  13. }
  14. });
  15. console.log('arr =' + JSON.stringify(arr));
  16. console.log('result =' + JSON.stringify(result));

打印结果:

  1. arr = [1, 2, 3, 4, 5, 11];
  2. result = [1, 2, 3, 4, 5, 11];

上方代码的写法太啰嗦了,其实也可以简化为如下写法:

写法 2:(冒泡排序)

  1. let arr = [5, 2, 11, 3, 4, 1];
  2. // 自定义排序规则
  3. let result = arr.sort(function (a, b) {
  4. return a - b; // 升序排列
  5. // return b - a; // 降序排列
  6. });
  7. console.log('arr =' + JSON.stringify(arr));
  8. console.log('result =' + JSON.stringify(result));

打印结果不变。

上方代码还可以写成 ES6 的形式,也就是将 function 改为箭头函数,其写法如下。

写法 3:(箭头函数)

  1. let arr = [5, 2, 11, 3, 4, 1];
  2. // 自定义排序规则
  3. let result = arr.sort((a, b) => {
  4. return a - b; // 升序排列
  5. });
  6. console.log('arr =' + JSON.stringify(arr));
  7. console.log('result =' + JSON.stringify(result));

上方代码,因为函数体内只有一句话,所以可以去掉 return 语句,继续简化为如下写法。

写法 4:(推荐)

  1. let arr = [5, 2, 11, 3, 4, 1];
  2. // 自定义排序规则:升序排列
  3. let result = arr.sort((a, b) => a - b);
  4. console.log('arr =' + JSON.stringify(arr));
  5. console.log('result =' + JSON.stringify(result));

上面的各种写法中,写法 4 是我们在实战开发中用得最多的。

为了确保代码的简洁优雅,接下来的代码中,凡是涉及到函数,我们将尽量采用 ES6 中的箭头函数来写。

sort 方法举例:将数组从小到大排序

将数组从小到大排序,这个例子很常见。但在实际开发中,总会有一些花样。

下面这段代码,在实际开发中,经常用到,一定要掌握。完整代码如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <script>
  10. let dataList = [
  11. {
  12. title: '品牌鞋子,高品质低价入手',
  13. publishTime: 200,
  14. },
  15. {
  16. title: '不是很贵,但是很暖',
  17. publishTime: 100,
  18. },
  19. {
  20. title: '无法拒绝的美食,跟我一起吃吃',
  21. publishTime: 300,
  22. },
  23. ];
  24. console.log('qianguyihao 排序前的数组:' + JSON.stringify(dataList));
  25. // 将dataList 数组,按照 publishTime 字段,从小到大排序。(会改变原数组)
  26. dataList.sort((a, b) => parseInt(a.publishTime) - parseInt(b.publishTime));
  27. console.log('qianguyihao 排序后的数组:' + JSON.stringify(dataList));
  28. </script>
  29. </body>
  30. </html>

打印结果:

  1. qianguyihao 排序前的数组:[
  2. {"title":"品牌鞋子,高品质低价入手","publishTime":200},
  3. {"title":"不是很贵,但是很暖","publishTime":100},
  4. {"title":"无法拒绝的美食,跟我一起吃吃","publishTime":300}
  5. ]
  6. qianguyihao 排序后的数组:[
  7. {"title":"不是很贵,但是很暖","publishTime":100},
  8. {"title":"品牌鞋子,高品质低价入手","publishTime":200},
  9. {"title":"无法拒绝的美食,跟我一起吃吃","publishTime":300}
  10. ]

上方代码中,有人可能会问: publishTime 字段已经是 int 类型了,为啥在排序前还要做一次 parseInt() 转换?这是因为,这种数据,一般是后台接口返回给前端的,数据可能是 int 类型、也可能是字符串类型,所以还是统一先做一次 partInt() 比较保险。这是一种良好的工作习惯。

indexOf() 和 lastIndexOf():获取数据的索引

语法 1

  1. 索引值 = 数组.indexOf(想要查询的元素);
  2. 索引值 = 数组.lastIndexOf(想要查询的元素);

备注:indexOf() 是从前向后查找元素的位置。同理,lastIndexOf()是从后向前寻找。

解释:可以检索一个数组中是否含有指定的元素。如果数组中含有该元素,则会返回其第一次出现的索引;如果没有找到指定的内容,则返回 -1。

这个方法的作用:

  • 如果获取的索引值为 0,说明数组是以查询的元素为开头的
  • 如果获取的索引值为-1,说明这个数组中没有指定的元素。

注意indexOf()在检索时,是严格类型约束,类似于===

举例 1

  1. var arr = ['a', 'b', 'c', 'd', 'e', 'd', 'c'];
  2. console.log(arr.indexOf('c')); //从前往后,找第一个"c"在哪个位置
  3. console.log(arr.lastIndexOf('d')); //从后往前,找第一个"d"在哪个位置

打印结果:

  1. 2
  2. 5

举例 2

  1. let arr = ['1', '2', '3', '4', '5'];
  2. console.log(arr.indexOf(2));

打印结果:

  1. -1

语法 2

这个方法还可以指定第二个参数,用来指定查找的起始位置。语法如下:

  1. 索引值 = 数组.indexOf(想要查询的元素, [起始位置]);

举例 3:(两个参数时,需要特别注意)

  1. let arr = ['q', 'i', 'a', 'n', 'g', 'u', 'y', 'i', 'h', 'a', 'o'];
  2. result = str.indexOf('a', 3); // 从下标为3的位置开始查找 'a'这个元素 【重要】
  3. console.log(result); // 打印结果:9

上方代码中,indexOf()方法中携带了两个参数,具体解释请看注释。

includes()

语法

  1. 布尔值 = arr.includes(想要查找的元素, [position]);

解释:判断一个数组中是否包含指定的元素。如果是,则会返回 true;否则返回 false。

参数中的 position:如果不指定,则默认为0;如果指定,则规定了检索的起始位置。

  1. const arr = [11, 12, 13, 14, 15];
  2. console.log(arr.includes(12)); // 打印结果:true
  3. console.log(name.includes(20)); // 打印结果:false
  4. console.log(name.includes(11, 1)); // 打印结果:false

find()

语法

  1. find((item, index, arr) => {
  2. return true;
  3. });

作用:找出第一个满足「指定条件返回 true」的元素;如果没找到,则返回 undefined。

备注:一旦找到符合条件的第一个元素,将不再继续往下遍历。

举例:

  1. let arr = [2, 3, 2, 5, 7, 6];
  2. let result = arr.find((item, index) => {
  3. return item > 4; //遍历数组arr,一旦发现有第一个元素大于4,就把这个元素返回
  4. });
  5. console.log(result); //打印结果:5

findIndex()

语法

  1. findIndex((item, index, arr) => {
  2. return true;
  3. });

作用:找出第一个满足「指定条件返回 true」的元素的 index。

举例:

我们直接把上面的代码中的 find 方法改成 findIndex,来看看效果。

  1. let arr = [2, 3, 2, 5, 7, 6];
  2. let result = arr.findIndex((item, index) => {
  3. return item > 4; //遍历数组arr,一旦发现有第一个元素大于4,就把这个元素的index返回
  4. });
  5. console.log(result); //打印结果:3

every()

every():对数组中每一项运行回调函数,如果都返回 true,every 就返回 true;如果有一项返回 false,则停止遍历,此方法返回 false。

注意:every()方法的返回值是 boolean 值,参数是回调函数。

举例:

  1. var arr1 = ['千古', '宿敌', '南山忆', '素颜'];
  2. var bool1 = arr1.every(function (element, index, array) {
  3. if (element.length > 2) {
  4. return false;
  5. }
  6. return true;
  7. });
  8. console.log(bool1); //输出结果:false。只要有一个元素的长度是超过两个字符的,就返回false
  9. var arr2 = ['千古', '宿敌', '南山', '素颜'];
  10. var bool2 = arr2.every(function (element, index, array) {
  11. if (element.length > 2) {
  12. return false;
  13. }
  14. return true;
  15. });
  16. console.log(bool2); //输出结果:true。因为每个元素的长度都是两个字符。

some()

some():对数组中每一个元素运行回调函数,只要有一个元素返回 true,则停止遍历,此方法返回 true。

注意:some()方法的返回值是 boolean 值。

every() 和 some() 的使用场景

every() 和 some() 这两个方法,初学者很容易搞混。要怎么区分呢?你可以这样记:

  • every():全部真,才为真。当你需要让数组中的每一个元素都满足指定条件时,那就使用 every()。
  • some():一个真,则为真,点到为止。数组中只要有一个元素满足指定条件时,就停止遍历。那就使用 some()。

valueOf():返回数组本身

  1. 数组本身 = 数组.valueOf();

这个方法的意义不大。因为我们直接写数组对象的名字,就已经是数组本身了。

概念:数组的遍历

遍历数组即:获取并操作数组中的每一个元素,然后得到想要的返回结果。在我们的实战开发中,使用得非常频繁。

遍历数组的方法包括:every()、filter()、forEach()、map()等。

PS:这几个方法不会修改原数组

语法格式:

  1. 数组/boolean/无 = 数组.every/filter/forEach/map/some(
  2. function(item, index, arr){
  3. 程序和返回值;
  4. })

有了这几种方法,就可以替代一些 for 循环了。下面依次来介绍。

for 循环遍历

举例:

  1. const arr = ['生命壹号', '许嵩', '永不止步'];
  2. for (let i = 0; i < arr.length; i++) {
  3. console.log(arr[i]); // arr[i]代表的是数组中的每一个元素i
  4. }
  5. console.log(JSON.stringify(arr));

打印结果:

  1. 生命壹号
  2. 许嵩
  3. 永不止步
  4. ["生命壹号","许嵩","永不止步"]

forEach() 遍历

forEach() 这种遍历方法只支持 IE8 以上的浏览器。IE8 及以下的浏览器均不支持该方法。所以如果需要兼容 IE8,则不要使用 forEach,改为使用 for 循环来遍历即可。

forEach()方法需要一个函数作为参数。这种函数,是由我们创建但是不由我们调用的,我们称为回调函数。

数组中有几个元素,该回调函数就会执行几次。

回调函数中传递三个参数:

  • 第一个参数,就是当前正在遍历的元素
  • 第二个参数,就是当前正在遍历的元素的索引
  • 第三个参数,就是正在遍历的数组

代码举例:

  1. let myArr = ['王一', '王二', '王三'];
  2. myArr.forEach((item, index, arr) => {
  3. console.log('item:' + item);
  4. console.log('index:' + index);
  5. console.log('arr:' + JSON.stringify(arr));
  6. console.log('----------');
  7. });

打印结果:

  1. item:王一
  2. index:0
  3. arr:["王一","王二","王三"]
  4. ----------
  5. item:王二
  6. index:1
  7. arr:["王一","王二","王三"]
  8. ----------
  9. item:王三
  10. index:2
  11. arr:["王一","王二","王三"]
  12. ----------

注意,forEach() 没有返回值。也可以理解成:forEach() 的返回值是 undefined。如果你尝试 tempArry = myArr.forEach()这种方式来接收,是达不到效果的。

forEach() 能不能改变原数组?

forEach() 能不能改变原数组?关于这个问题,大部分人会搞错。我们来看看下面的代码。

1、数组的元素是基本数据类型:(无法改变原数组)

  1. let numArr = [1, 2, 3];
  2. numArr.forEach((item) => {
  3. item = item * 2;
  4. });
  5. console.log(JSON.stringify(numArr)); // 打印结果:[1, 2, 3]

上面这段代码,你可要看仔细了,打印结果是 [1, 2, 3],不是 [2, 4, 6]

2、数组的元素是引用数据类型:(直接修改整个元素对象时,无法改变原数组)

  1. let objArr = [
  2. { name: '千古壹号', age: 20 },
  3. { name: '许嵩', age: 30 },
  4. ];
  5. objArr.forEach((item) => {
  6. item = {
  7. name: '邓紫棋',
  8. age: '29',
  9. };
  10. });
  11. console.log(JSON.stringify(objArr)); // 打印结果:[{"name":"千古壹号","age":20},{"name":"许嵩","age":30}]

3、数组的元素是引用数据类型:(修改元素对象里的某个属性时,可以改变原数组)

  1. let objArr = [
  2. { name: '千古壹号', age: 28 },
  3. { name: '许嵩', age: 30 },
  4. ];
  5. objArr.forEach((item) => {
  6. item.name = '邓紫棋';
  7. });
  8. console.log(JSON.stringify(objArr)); // 打印结果:[{"name":"邓紫棋","age":28},{"name":"邓紫棋","age":30}]

如果你需要通过 forEach 修改原数组,建议用 forEach 里面的参数 2 和参数 3 来做,具体请看下面的标准做法。

4、forEach() 通过参数 2、参数 3 修改原数组:(标准做法)

  1. // 1、数组的元素是基本数据类型
  2. let numArr = [1, 2, 3];
  3. numArr.forEach((item, index, arr) => {
  4. arr[index] = arr[index] * 2;
  5. });
  6. console.log(JSON.stringify(numArr)); // 打印结果:[2,4,6]
  7. // 2、数组的元素是引用数据类型时,直接修改对象
  8. let objArr = [
  9. { name: '千古壹号', age: 28 },
  10. { name: '许嵩', age: 34 },
  11. ];
  12. objArr.forEach((item, index, arr) => {
  13. arr[index] = {
  14. name: '小明',
  15. age: '10',
  16. };
  17. });
  18. console.log(JSON.stringify(objArr)); // 打印结果:[{"name":"小明","age":"10"},{"name":"小明","age":"10"}]
  19. // 3、数组的元素是引用数据类型时,修改对象的某个属性
  20. let objArr2 = [
  21. { name: '千古壹号', age: 28 },
  22. { name: '许嵩', age: 34 },
  23. ];
  24. objArr2.forEach((item, index, arr) => {
  25. arr[index].name = '小明';
  26. });
  27. console.log(JSON.stringify(objArr2)); // 打印结果:[{"name":"小明","age":28},{"name":"小明","age":34}]

总结

如果纯粹只是遍历数组,那么,可以用 forEach() 方法。但是,如果你想在遍历数组的同时,去改变数组里的元素内容,那么,最好是用 map() 方法来做,不要用 forEach()方法,避免出现一些低级错误。

参考链接:

map()方法

语法:

  1. arr.map(function (item, index, arr) {
  2. return newItem;
  3. });

解释:对数组中每一项运行回调函数,返回该函数的结果,组成的新数组(返回的是加工之后的新数组)。不会改变原数组。

作用:对数组中的每一项进行加工。

举例 1:(拷贝的过程中改变数组元素的值)

有一个已知的数组 arr1,我要求让 arr1 中的每个元素的值都加 10,这里就可以用到 map 方法。代码举例:

  1. var arr1 = [1, 3, 6, 2, 5, 6];
  2. var arr2 = arr1.map(function (item, index) {
  3. return item + 10; //让arr1中的每个元素加10
  4. });
  5. console.log(arr2);

打印结果:

(二)JavaScript 对象简介 - 图17

举例 2:【重要案例,实际开发中经常用到】

将 A 数组中某个属性的值,存储到 B 数组中。代码举例:

  1. const arr1 = [
  2. { name: '千古壹号', age: '28' },
  3. { name: '许嵩', age: '32' },
  4. ];
  5. // 将数组 arr1 中的 name 属性,存储到 数组 arr2 中
  6. const arr2 = arr1.map((item) => item.name);
  7. // 将数组 arr1 中的 name、age这两个属性,改一下“键”的名字,存储到 arr3中
  8. const arr3 = arr1.map((item) => ({
  9. myName: item.name,
  10. myAge: item.age,
  11. })); // 将数组 arr1 中的 name 属性,存储到 数组 arr2 中
  12. console.log('arr1:' + JSON.stringify(arr1));
  13. console.log('arr2:' + JSON.stringify(arr2));
  14. console.log('arr3:' + JSON.stringify(arr3));

打印结果:

  1. arr1:[{"name":"千古壹号","age":"28"},{"name":"许嵩","age":"32"}]
  2. arr2:["千古壹号","许嵩"]
  3. arr3:[{"myName":"千古壹号","myAge":"28"},{"myName":"许嵩","myAge":"32"}]

map 的应用场景,主要就是以上两种。

注意:map() 方法真的不会改变原数组吗?

答案:不一定。

举例:

  1. const arr = [
  2. {
  3. name: "qianguyihao1",
  4. age: 22,
  5. },
  6. {
  7. name: "qianguyihao2",
  8. age: 23,
  9. },
  10. ];
  11. arr.map((item) => {
  12. item.name = "haha"; // 修改 item 里的某个属性
  13. return item;
  14. });
  15. console.log(JSON.stringify(arr));

打印结果:

  1. [{"name":"haha","age":22},{"name":"haha","age":23}]

总结:map方法如果是修改整个item的值,则不会改变原数组。但如果是修改 item 里面的某个属性,那就会改变原数组。

filter()

语法:

  1. arr.filter(function (item, index, arr) {
  2. return true;
  3. });

解释:对数组中的每一项运行回调函数,该函数返回结果是 true 的项,将组成新的数组(返回值就是这个新的数组)。不会改变原数组。

作用:对数组进行过滤。

举例 1:找出数组 arr1 中大于 4 的元素,返回一个新的数组。代码如下:

  1. let arr1 = [1, 3, 6, 2, 5, 6];
  2. let arr2 = arr1.filter((item) => {
  3. if (item > 4) {
  4. return true; // 将arr1中大于4的元素返回,组成新的数组
  5. }
  6. return false;
  7. });
  8. console.log(JSON.stringify(arr1)); // 打印结果:[1,3,6,2,5,6]
  9. console.log(JSON.stringify(arr2)); // 打印结果:[6,5,6]

上方代码更简洁的写法如下:

  1. let arr1 = [1, 3, 6, 2, 5, 6];
  2. let arr2 = arr1.filter((item) => item > 4); // 将arr1中大于4的元素返回,组成新的数组
  3. console.log(JSON.stringify(arr1)); // 打印结果:[1,3,6,2,5,6]
  4. console.log(JSON.stringify(arr2)); // 打印结果:[6,5,6]

举例 2

获取数组 A 中指定类型的对象,放到数组 B 中。代码举例如下:

  1. const arr1 = [
  2. { name: '许嵩', type: '一线' },
  3. { name: '周杰伦', type: '过气' },
  4. { name: '邓紫棋', type: '一线' },
  5. ];
  6. const arr2 = arr1.filter((item) => item.type == '一线'); // 筛选出一线歌手
  7. console.log(JSON.stringify(arr2));

打印结果:

  1. [
  2. { name: '许嵩', type: '一线' },
  3. { name: '邓紫棋', type: '一线' },
  4. ];

reduce()方法

reduce() 语法

reduce 的发音:[rɪ’djuːs]。中文含义是减少,但这个方法跟“减少”没有任何关系。

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。返回值是回调函数累计处理的结果。

语法

  1. arr.reduce(function (previousValue, currentValue, currentIndex, arr) {}, initialValue);

参数解释:

  • previousValue:必填,上一次调用回调函数时的返回值
  • currentValue:必填,当前正在处理的数组元素
  • currentIndex:选填,当前正在处理的数组元素下标
  • arr:选填,调用 reduce()方法的数组
  • initialValue:选填,可选的初始值(作为第一次调用回调函数时传给 previousValue 的值)

在以往的数组方法中,匿名的回调函数里是传三个参数:item、index、arr。但是在 reduce() 方法中,前面多传了一个参数previousValue,这个参数的意思是上一次调用回调函数时的返回值。第一次执行回调函数时,previousValue 没有值怎么办?可以用 initialValue 参数传给它。

备注:绝大多数人在一开始接触 reduce() 的时候会很懵逼,但是没关系,有事没事多看几遍,自然就掌握了。如果能熟练使用 reduce() 的用法,将能替代很多其他的数组方法,并逐渐走上进阶之路,领先于他人。

为了方便理解 reduce(),我们先来看看下面的简单代码,过渡一下:

  1. let arr1 = [1, 2, 3, 4, 5, 6];
  2. arr1.reduce((prev, item) => {
  3. console.log(prev);
  4. console.log(item);
  5. console.log('------');
  6. return 88;
  7. }, 0);

打印结果:

  1. 0
  2. 1
  3. ------
  4. 88
  5. 2
  6. ------
  7. 88
  8. 3
  9. ------
  10. 88
  11. 4
  12. ------
  13. 88
  14. 5
  15. ------
  16. 88
  17. 6
  18. ------

上面的代码中,由于return的是固定值,所以 prev 打印的也是固定值(只有初始值是 0,剩下的遍历中,都是打印 88)。

现在来升级一下,实际开发中,prev 的值往往是动态变化的,这便是 reduce()的精妙之处。我们来看几个例子就明白了。

reduce() 的常见应用

举例 1、求和:

计算数组中所有元素项的总和。代码实现:

  1. const arr = [2, 0, 1, 9, 6];
  2. // 数组求和
  3. const total = arr.reduce((prev, item) => {
  4. return prev + item;
  5. });
  6. console.log('total:' + total); // 打印结果:18

举例 2、统计某个元素出现的次数:

代码实现:

  1. // 定义方法:统一 value 这个元素在数组 arr 中出现的次数
  2. function repeatCount(arr, value) {
  3. if (!arr || arr.length == 0) return 0;
  4. return arr.reduce((totalCount, item) => {
  5. totalCount += item == value ? 1 : 0;
  6. return totalCount;
  7. }, 0);
  8. }
  9. let arr1 = [1, 2, 6, 5, 6, 1, 6];
  10. console.log(repeatCount(arr1, 6)); // 打印结果:3

举例 3、求元素的最大值:

代码实现:

  1. const arr = [2, 0, 1, 9, 6];
  2. // 数组求最大值
  3. const maxValue = arr.reduce((prev, item) => {
  4. return prev > item ? prev : item;
  5. });
  6. console.log(maxValue); // 打印结果:9

参考链接:

数组练习

splice()练习:数组去重

代码实现:

  1. //创建一个数组
  2. var arr = [1, 2, 3, 2, 2, 1, 3, 4, 2, 5];
  3. //去除数组中重复的数字
  4. //获取数组中的每一个元素
  5. for (var i = 0; i < arr.length; i++) {
  6. //console.log(arr[i]);
  7. /*获取当前元素后的所有元素*/
  8. for (var j = i + 1; j < arr.length; j++) {
  9. //console.log("---->"+arr[j]);
  10. //判断两个元素的值是否相等
  11. if (arr[i] == arr[j]) {
  12. //如果相等则证明出现了重复的元素,则删除j对应的元素
  13. arr.splice(j, 1);
  14. //当删除了当前j所在的元素以后,后边的元素会自动补位
  15. //此时将不会在比较这个元素,我需要再比较一次j所在位置的元素
  16. //使j自减
  17. j--;
  18. }
  19. }
  20. }
  21. console.log(arr);

清空数组

清空数组,有以下几种方式:

  1. var array = [1, 2, 3, 4, 5, 6];
  2. array.splice(0); //方式1:删除数组中所有项目
  3. array.length = 0; //方式2:length属性可以赋值,在其它语言中length是只读
  4. array = []; //方式3:推荐

join() 练习

问题:将一个字符串数组输出为|分割的形式,比如“千古|宿敌|素颜”。使用两种方式实现。

答案:

方式 1:(不推荐)

  1. var arr = ['千古', '宿敌', '素颜'];
  2. var str = arr[0];
  3. var separator = '|';
  4. for (var i = 1; i < arr.length; i++) {
  5. str += separator + arr[i]; //从第1个数组元素开始,每个元素前面加上符号"|"
  6. }
  7. console.log(str);

输出结果:

(二)JavaScript 对象简介 - 图18

不推荐这种方式,因为:由于字符串的不变性,str 拼接过多的话,容易导致内存溢出(很多个 str 都堆放在栈里)。

方式 2:(推荐。通过 array 数组自带的 api 来实现)

  1. var arr = ['千古', '宿敌', '素颜'];
  2. console.log(arr.join('|'));

结果:

(二)JavaScript 对象简介 - 图19

reverse() 练习

题目:将一个字符串数组的元素的顺序进行反转,使用两种种方式实现。提示:第 i 个和第 length-i-1 个进行交换。

答案:

方式 1:

  1. function reverse(array) {
  2. var newArr = [];
  3. for (var i = array.length - 1; i >= 0; i--) {
  4. newArr[newArr.length] = array[i];
  5. }
  6. return newArr;
  7. }

方式 2:(算法里比较常见的方式)

  1. function reverse(array) {
  2. for (var i = 0; i < array.length / 2; i++) {
  3. var temp = array[i];
  4. array[i] = array[array.length - 1 - i];
  5. array[array.length - 1 - i] = temp;
  6. }
  7. return array;
  8. }

方式 3:(数组自带的 reverse 方法)

现在我们学习了数组自带的 api,我们就可以直接使用 reverse()方法。

练习:数组去重

问题:编写一个方法去掉一个数组中的重复元素。

分析:创建一个新数组,循环遍历,只要新数组中有老数组的值,就不用再添加了。

答案:

  1. // 编写一个方法 去掉一个数组的重复元素
  2. var arr = [1, 2, 3, 4, 5, 2, 3, 4];
  3. console.log(arr);
  4. var aaa = fn(arr);
  5. console.log(aaa);
  6. //思路:创建一个新数组,循环遍历,只要新数组中有老数组的值,就不用再添加了。
  7. function fn(array) {
  8. var newArr = [];
  9. for (var i = 0; i < array.length; i++) {
  10. //开闭原则
  11. var bool = true;
  12. //每次都要判断新数组中是否有旧数组中的值。
  13. for (var j = 0; j < newArr.length; j++) {
  14. if (array[i] === newArr[j]) {
  15. bool = false;
  16. }
  17. }
  18. if (bool) {
  19. newArr[newArr.length] = array[i];
  20. }
  21. }
  22. return newArr;
  23. }