JS学习笔记
第01节:JavaScript概述
一、JavaScript概述
1995年,JavaScript问世,主要目的是处理表单验证。起初命名为LiveScript,后来因为java语言盛行,更名为JavaScript,目的是希望借着Java的火爆流行起来(JavaScript的开发者一定想不到JavaScript在20多年后的今天会如此盛行)。
1997年,欧洲计算机制造商协会发布了ECMAScript,在接下来的几年里,web浏览器厂商就开始将ECMAScript作为JavaScript实现的标准。
2009年,Node.js问世,JavaScript这门语言逐步在后台占据一席之地,目前,前端开发的大量工具都基于node.js。
2015年,ECMAScript2015(ES6)正式发布,使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
后续版本统称ES2015+,ES2015是完全向下兼容的
二、JavaScript是做什么的
在前端,通过javascript可以实现更多的页面交互,与后台的数据交互,以及更为丰富的网页效果。
在后台,借助node的运行环境,使用javascript一门语言,即可完成服务器端开发,我们会在后续的章节中讲解如何使用JavaScript做服务端开发,本章主要内容仍然面向前端开发。。
三、hello world
JavaScript放在<script></script>
这个标签里
四、控制台
chrome浏览器中的console
选项,他是JavaScript的控制台工具,我们可以在其中输出JavaScript程序,也可以在其中看到程序运行结果。例如我们在网页中编写如下代码:
可以看到控制台中会输出hello world
。
第02节:变量与数据类型
一、变量的基本概念
变量可以理解为是一个存储数据的容器
代码如下所示:
通过var声明一个变量,var
后面的英文字母就是变量名,变量名是自定义的,在一定的规则下我们可以随意命名(下一部分我们来讲命名规则)。
每行结尾的分号并不是必须写的,但是为了代码更加规范,我们要求每行代码的结尾都要写分号,用以表示本行结束(注意必须是英文半角的分号)。
二、变量的命名规范
代码如下所示:
- 变量名要见名知意
- 变量名可以是字母、下划线、$,还有数字;但是不能以数字开头
- 小写字母开头,多个单词,第二个单词首字母大写(驼峰命名)
- 不可以与关键字、保留字重复
三、数据类型
JavaScript有六种数据类型,如下表所示:
类型名称 | 值 | 说明 |
---|---|---|
数值 | 100;3.14 | 不管是整数还是小数,都是数值型。 |
字符串 | "hello";"100" | 双引号或单引号中的值是字符串。 |
布尔 | true;false | 布尔值只有两个值,代表真和假。 |
空 | null | 空值只有null,后续讲解。 |
未定义 | undefined | 未定义值只有undefined,后续讲解 |
对象 | {} | 未初始化时可以暂时赋值为null |
关于undefined
第03节:表达式与运算符
一、表达式概述
字面量
赋值符号
=
右边某种数据类型的值,就是字面量例如字符串
"hello world"
或是数字100
都是字面量
表达式
通过运算符将变量、字面量组合起来,就是表达式。
每一个表达式都有一个固定返回值(表达式的结果)
二、运算符
算数运算符
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
+ | 加法 | 20 + 10 | 7 |
- | 减法 | 20 - 10 | 10 |
* | 乘法 | 20 * 10 | 200 |
/ | 除法 | 20 / 10 | 2 |
% | 求于 | 11 % 2 | 1 |
++ | 自增1 | ++7 | 8 |
-- | 自减 | --7 | 6 |
自增和自减运算符如果写在变量后面,那么表达式的返回值是变量本身,然后变量自增或自减,运算符写在变量前面,那么变大时的返回值直接就是变量自增或自减后的结果。
比较运算符
比较运算符的返回值是布尔值
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
> | 大于 | 20 > 10 | true |
< | 小于 | 20 < 10 | false |
>= | 大于等于 | 20 >= 10 | true |
<= | 小于等于 | 20 <= 10 | false |
== | 等于(会进行类型转换,不比较数据类型,效率低) | 20 == "20" | true |
!= | 不等(会进行类型转换,不比较数据类型,效率低) | 20 != 10 | false |
=== | 恒等于(不进行类型转换,会比较类型,效率高) | 20 === "20" | false |
!== | 非恒等于(不进行类型转换,会比较类型,效率高) | 20 !== "20" | true |
逻辑运运算符
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
&& | 逻辑与 | true && false | false |
|| | 逻辑或 | true || false | true |
! | 逻辑非 | !true | false |
赋值运算符
运算符 | 描述 | 示例 | 等同于 |
---|---|---|---|
= | 赋值 | x = 10 | 无 |
+= | 加并赋值 | x += y | x = x + y |
-= | 减并赋值 | x -= y | x = x - y |
*= | 乘并赋值 | x *= y | x = x * y |
/= | 除并赋值 | x /= y | x = x / y |
第04节:条件语句
一、概述
语句执行流程有三种:顺序执行、条件执行、循环执行。
条件语句表示的就是按照条件判断执行哪些代码(或不执行哪些代码)。
二、if语句
if语句是最基本的条件控制语句,它让JavaScript程序可以选择执行顺序,我们可以通过一个布尔值来控制一行语句是否执行,if语句有多种形式,下面我们一一介绍: 示例代码如下:
在上面的代码中,if括号内的值如果是true,则执行第二行代码,如果是false,则不执行第二行代码。
if后面的括号内一般不会直接写一个布尔值,而是写一个表达式, 示例代码如下:
上面我们将的两个例子都是通过判断条件来执行一行代码,但是大多数情况,我们需要执行多行代码,那么我们需要在if后面加上一对花括号,并且,为了让代码块更直观,我们在以后的代码中,都会写if后面的花括号。 示例代码如下:
通过上面的例子,我们通过判断条件确定是否执行某一个代码块,下面我们通过if...else...语句实现:在两个代码块中,选择一个来执行 示例代码如下:
if...else语句可以判断两种情况下,需要执行哪些代码,如果需要判断的条件是三种情况,我们可以使用if...else if...语句 示例代码如下:
我们可以通过修改num1和num2的值来判断输出哪一行语句。
通过控制运算符来实现数学运算
三、switch语句
if语句在程序执行的过程中创建一条分支,并且可以使用if...else if...语句来处理多条分支,然而当所有的分支都依赖于同一个表达式的值时,重复计算多条if语句中的条件是非常浪费时间的做法,switch语句正合适处理这种情况
我们了解switch语句的语法,下面我们使用switch语句改写demo03,实现通过控制运算符来实现数学运算
四、条件运算符
如果是简单的判断,我们可以使用条件运算符
如果表达式为true,表达式的返回值是第一个值,如果表达式为false,那么表达式的返回值是第二个值,示例代码如下所示示例示例代码如下:
第05节:循环语句
条件语句的代码可以被想象成是一条条分支的路径,而循环语句的代码则是程序路径的一个回路,可以让一部分代码重复执行。JavaScript中的循环语句有for语句和while语句。
一、for语句
for语句的语法如下:
在for语句中,如果布尔值是true,就会一直执行语句块中的内容,为了防止死循环,需要有一个计数器,当数值达到指定值,布尔值就会变成false,循环便停止了,下面的示例代码使用for循环输出0~9九个数字 示例代码如下:
通过上面的例子我们进一步理解了for语句的用法,下面我们来做一个联系,利用for循环语句输出100以内所有正整数的加和 示例代码如下:
二、while语句
while语句语法如下所示:
当bool为true的时候,花括号中的内容会循环执行。为了防止死循环,需要在循环的过程实现类似for计数器的功能,让循环在有限的次数内定制,下面我们使用while语句输出0~9是个数字 示例代码如下:
在每次循环的过程中都会让n的值加1,这样当n的值等于10,循环便停止,下面我来使用while语句输出100以内所有正整数的加和 示例代码如下:
三、continue
continue可以结束本次循环,直接进入到下一次循环,例如我们用for循环语句来实现输出0 ~ 5,7 ~ 9九个数字(跳过6) 示例代码如下:
上面的代码通过判断,实现当i的值为6的时候,跳过本次循环,直接接入下一次循环。下面我们使用continue来实现计算100以内所有不能被7整除的正整数加和 示例代码如下:
四、break
在学switch语句中,我们已经接触到了break,它可以让分支语句在结束一个case之后,跳出switch语句,break同样可以用在循环语句当中,当代码执行到break时,直接结束循环 示例代码如下:
如上面的代码所示,当控制带输出5之后,循环结束。
第06节:函数基础
一、函数的基本概念
函数是一个可执行的语句块,定义的时候不执行,调用的时候执行,使用"函数名()"的形式可以调用函数
语法如下:
我们先来编写第一个最简单的函数,当这个函数执行的时候会在控制台输出"hello function" 示例代码如下:
在上面的代码中我们定义了一个函数,并调用了一次,这样就会在控制台输出一次“hello function”。我们在编程的过程中,很多代码是需要多次使用的,我们可以把它们写在一个函数中,这样我们每次希望执行这些代码的时候,只需要调用这个函数,而不是复制-粘贴多次代码。
二、参数
下面编写一个函数sum,输出10和20两个数之和 示例代码如下:
在上面例子中我们调用sum函数,可以成功在控制台输出计算结果,但是函数内部的代码是固定的,虽然可以多次使用,但是每次使用输出的都是10和20的加和,为了让函数更加灵活,我们希望实现一个函数可以计算任意两个数的加和,那么我们就需要了解函数是如何传递参数的。
下面一个简单的例子演示函数如何传递参数 示例代码如下:
在定义函数的括号中,我们添加了一个参数str,这个参数叫做形参。它在函数内部像一个变量一样。但是在函数调用之前他是没有值的。当调用函数的时候,调用函数的括号中我们也添加了一个参数"world",这个参数叫做实参,他可以是任意数据类型的值。函数被调用后,形参str被赋予了实参"world"的值,然后执行console.log便会在控制台输出"helloworld"
上面的函数有一个形参和一个实参,函数可以传递多个参数,用逗号间隔 示例代码如下:
当我们调用函数的时候,实参和形参是一一对应的,10对应的是num1,20对应的是num2,函数执行后会在控制台输出10和20的加和。
下面我们来编写一个函数,它有一个正整数参数n,当我们调用函数时,函数会输出包括n在内,1~n所有正整数的加和 示例代码如下:
我们通过一个函数规定了一种计算方式,我们使用函数的只要输入一个值,函数就会计算出一个准确的结果。
三、返回值
在上面的例子中,我们输入了一个参数,函数就可以在控制台输出我们希望得到的结果,但是在实际开发中,很多情况我们要的不是在控制台输出得到的结果,而单纯的只是为了获取这个值,那么我们就需要用到函数的返回值。
在函数中,我们可以通过return关键字指定一个返回值,函数有了return,当函数被调用的时候就可以把调用的结果赋值给另一个变量了 示例代码如下:
在上面的例子中,函数fun1没有返回值,所有将fun1调用的结果赋值给str1,str1的值为undefined,函数fun2有返回值,返回值是"hello fun",所以当fun2被调用后,将函数运行的结果赋值给str2,str2的值就是"hello fun"
下面我们来编写一个函数,让函数来计算四则运算的结果
四、函数表达式
这种形式看起来好像是常规的变量赋值语句,即创建一个函数并将它赋值给变量functionName。这种情况下创建的函数叫做匿名函数。因为function关键字后面没有标识符。
函数表达式与其他表达式一样,在使用之前必须先赋值;如下面代码就会导致错误;
有了函数表达式,我们就可以给函数表达式赋值了;如下面代码:
五、函数声明提升
上例不会报错,正是因为 ‘函数声明提升’,即将函数声明提升(整体)到作用域顶部(注意是函数声明,不包括函数表达式),实际提升后结果同下:
==函数表达式没有函数声明提升==
六、作用域
在 JavaScript 中, 对象和函数同样也是变量。 在 JavaScript 中, 作用域为可访问变量,对象,函数的集合。 JavaScript 函数作用域: 作用域在函数内修改。
局部作用域
变量在函数内声明,变量为局部作用域。 局部变量:只能在函数内部访问。
因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。 局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。
全局变量
变量在函数外定义,即为全局变量。 全局变量有 全局作用域: 网页中所有脚本和函数均可使用。
如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。 以下实例中 carName 在函数内,但是为全局变量。
第07节:对象
一、对象的基本概念
对象是属性的集合,是六种数据类型之一。
二、自定义对象
我们可以通过一对花括号来创建一个对象 如下所示:
在花括号中,我们可以为对象定义属性,下面我们来写一个猫的对象 示例代码如下:
创建一个空的对象
三、方法
如果对象的属性值是函数,那么我们叫这个属性为这个对象的方法
上面的代码可以在控制台输出“我是喵喵”,其中sayName是cat的方法。
方法简写
四、this关键字
在上面的例子中,我们可以给cat的那么属性重新赋值,代码如下 示例代码如下:
因为猫的名字已经改变,但是sayName方法里面的名字并没有一同变化,我们可以通过this关键字实现修改了名字,方法里面的名字也会改变。
在对象的方法中使用this,可以指向这个对象本身, 示例代码如下:
五、方法传参
给对象的方法传递参数和给函数传递参数是一样的,下面我们来定义一个会算数的猫 示例代码如下:
我们在之前代码的基础上,有添加了一个count方法可以让猫可以计算两个数的加和,我们只要传入实参,猫就能计算结果。
六、对象展开运算符
七、对象的分类
- 自定义对象:封装
- 内置对象:(例如Date,获取当前时间)
- 宿主对象(document)
- 第三方库对象(jQuery,Vue)
第08节:数组
一、数组的基本概念
数组是一个特殊的对象,对象的概念是属性的集合,而数组是元素的有序集合。我们可以通过一个中括号来定义一个数组
在上面的代码中我们定义了一个数组,数组有5个元素,五个元素都是数值型。我们可以通过数组的变量名配合中括号来获取数组的元素
中括号中的数字叫做数组的下标,我们可以通过下标获取数组的元素,要注意的是下标是从零开始的。
数组是特殊的对象,它有自己的属性和方法,其中最常用的属性就是length,它可以获取数组元素的个数。
数组中的元素可以是任意类型的,但是我们一般将数组中的元素设置成相同数据类型,下面是一个字符串类型的数组,我们通过下标分别输出数组的所有元素。 示例代码如下:
二、遍历数组
- while
- for
- for in(i是下标)
- for of(i是元素)
- map方法
三、数组的常用方法
map方法
map()方法创建一个新的数组,元素为使用此数组中的每个元素调用参数所提供的函数的结果
push方法
push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。示例代码如下:
sort方法(排序)
join方法
用于把数组中的所有元素放入一个字符串,参数为间隔符,默认为","
split方法
是string的方法,可以把一个字符串拆分成数组,参数是间隔符,参数为空时会把字符串转换为只有一个元素的数组,数组元素是一个字符串
filter方法(过滤器)
创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
find方法
find()方法返回数组中第一个满足回调函数测试的第一个元素的值。否则返回undefined;示例代码如下:
第9节:内置对象
一、内置对象概述
javascript为我们提供了很多内置对象,这些内置对象为我们提供了很多语言的基本功能。我们之前学过的数组就是JavaScript的内置对象,除了数组我们还应该了解的内置对象有:Math、Date、RegExp。
需要说明的是document对象是DOM提供的对象,不属于JavaScript内置对象,window对象是BOM中的对象,同样不属于JavaScript内置对象。
二、Math
Math对象不像数组那样需要我们手动去创建,我们在JavaScript程序中直接写Math代表的就是Math对象。我们可以通过Math对象直接获取圆周率
Math对象提供了很多方法来简化我们的数学运算,下面简单列举几个方法
在Math对象的方法中,floor和random两个方法比较常用,我们可以通过这两个方法获取我们想要的随机数范围。例如我们想要1~10的随机数,代码如下
下面我们来实现一个猜数字的游戏,JavaScript随机生成一个1~100之间的数字,我们通过文本输入框输入我们所猜的数字,猜的数字不管是大于结果,还是小于结果,还是等于结果,都会有相应的提示 代码如下
三、Date
Date对象是JavaScript用于处理日期和时间的对象,我们可以通过Date对象获取当前的时间,需要说明的是Date对象获取的时间是本机的时间
我们可以通过JavaScript将当前的时间显示在网页上
上面的例子我们成功将当前的日期显示在h1标签中,但是我们显示的时间是获取的那个时间点,显示的时间是静止不动的,我们可以通过计时器方法让我们显示的时间与实际时间同步
我们将过去时间的代码放到了一个函数中,然后通过计时器方法每秒执行一次这个函数,这样我们显示出来的日期就想电子钟一样每秒与真实事件同步一次。
我们还可以通过参数创建一个指定时间的日期对象,我们修改一下
我们在编写程序的时候,有的时候会希望获取一个唯一的时间点,我们可以使用getTime方法或得。 示例代码如下
通过getTime方法可以获取时间戳,时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数,我们可以用时间戳表示一个不会重复的时间点。
课后练习
制作一个倒计时的功能,要求如下:
- 计算距离指定日期还有多少天,多少小时,多少分钟,多少秒。
- 在控制台输出这个时间。
第10节:正则表达式
一、正则表达式概述
正则表达式用于匹配字符串,例如我们想验证某一个字符串是否为邮箱格式,可以使用正则表达式判断;我们希望特换一片文章中的所有英文字母,可以使用正则表达式;我们想截取一片文章中的某些内容,也可以使用正则表达式。
正则表达式对象RegExp是JavaScript中的内置对象,我们可以像创建数组一样创建它。
在开发中,我们一般用简写的方法创建正则表达式,同样和数组比较,代码如下
二、正则表达式语法
正则表达式可以用来匹配字符串,我们可以把正则表达式看做是一种规则,如果字符串中的内容符合这种规则,就会匹配,如果不符合这个规则,就不会匹配
我们可以看到程序在控制台输出了true,因为reg定义的时候就是为了匹配字符串123的,我们再来看下面的例子
我们将字符串改成了"12345",返回结果仍然返回true,这是因为我们定义的正则表达式可以匹配任何包含"123"的字符串,为了证实这个说法,我们可以使用exec方法来输出匹配的内容
这段代码在控制台输出了一个数组,数组的第一个元素是匹配的内容,大家可以看到匹配的内容是"123",数组还带有两个属性,index表示从字符串中第几个字母开始匹配,input表示被匹配的字符串的值。
有的时候,我们希望我们写的正则表达式只能匹配"123",如果是字符串包含"123"页不匹配,如果希望这样的话,我们需要改进我们的正则表达式
这样就可以只能匹配字符串"123",但是如果正则表达式是直接把要匹配的内容写在正则表达式里,那意义也不是很大,接下来我们用正则表达式强大的语法来匹配各种字符串。
设定匹配范围
正则表达式可以通过[]设定匹配的范围,代码如下
通过指定范围,正则表达式成功匹配了字符串中的数字2。
匹配数字
正则表达式可以在匹配范围中定义[0-9]来设定匹配数字
虽然字符串中都是数字,但是一个[]只能表示匹配一位数字,所以这里匹配的是0
匹配字母
正则表达式可以在匹配范围中定义[a-z]来设定匹配字母
与匹配数字类似,上面的正则表达式可以匹配一位字母。
匹配多位
上面的匹配内容匹配的都是一位数字或字母,我们可以通过+来指定匹配多位
上面的代码表示匹配多位字母,所有abcde都成功的被匹配
匹配指定位数
有的时候我们需要匹配指定位数的字符,可以通过{}指定匹配的位数
下面我们来定义一个正则表达式来匹配一个邮箱格式的字符串,我们先来确定一下邮箱的格式:
- 5~12位的数字字母下划线开头
- 后面接@
- 后面接2~5位的数字和字母
- 后面接.
- 后面接com
很多特殊符号在正则表达式都有特殊含义,为了取消它的特殊含义,我们需要在特殊符号之前加“\”将其转义。、
三、表单验证
通过上面的学习,我们已经对正则表达式有了初步的了解,下面我们来实现一个表单验证邮箱的功能,验证通过或者不通过,都要在文本框后面输出结果
四、总结
在实际项目中,我们通常会用插件去实现字符串验证的功能。所以,大家了解了正则表达式基本语法之后,再去学习那些深度的东西
课后练习
- 使用正则表达式,完成手机号验证;
- 使用正则表达式,完成邮箱验证;
第11节:常量变量与解构赋值
一、ECMAscript概述
ECMAscript简称ES,是JavaScript的标准,我们经常说的ES5,ES6等等,可以称作JavaScript的版本,我们在之前学过的所有JavaScript特性,都是基于ES5版本的,今天我们开始讲解的是ES6标准的特性。ES6已更名为ES2015,ES7等后续的版本,我们都可以统称为ES2015+
二、变量和常量
ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。我们之前一直使用var定义变量,在ES6版本中,我们可以使用let定义变量,下面我们来说说var与let的区别
块级作用域
ES5只有全局作用域和函数作用域,没有块级作用域的概念,这带来了很多不合理的场景。 实例代码如下:
因为没有块级作用域,所以我们在for语句的外面仍然能获取i的值,在实际开发中,这是一个不可理喻的场景,我们希望的是这个i只在for语句内有效,所以再ES6中添加了块级作用域的概念,我们可以用let声明变量,问题就解决了 实例代码如下:
因为用let声明变量,变量只在块级作用域下有效,所以再for语句之外输出i会报错。另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
不存在变量提升 定义:函数声明和变量声明总是被JavaScript解释器隐式地提升(hoist)到包含他们的作用域的最顶端。 通过定义我们可以知道,只有变量的声明和函数的声明存在变量提升这一说,那么在ES2015+出现之前,JavaScript声明变量的方式是通过关键字var实现的,声明函数自然通过function啦,ES6中我们用let,const来声明变量和常量。 实例代码如下:
如果let存在变量提升,那么上述代码就相当于下面这样
由此可见,let并不存在变量提升。
不允许重复声明
实例代码如下:
多次声明是没有意义的,在ES6中,我们使用let声明变量限制了不能多次声明,如果多次声明同一个变量会报错。
常量
在ES6中,不仅有变量,还增加了常量的概念,我们用const声明常量,一旦声明,它的值就不能再改变 实例代码如下:
我们说常量不能再改变,说的是不能重新为这个常量赋值,但是如果常量存储的是一个对象,那我们是可以改变这个对象的属性的 实例代码如下:
三、模板字符串
之前我们也可以使用JavaScript输出模版字符串,通常是下面这样的:
但是我们可以看到:这样的传统做法需要使用大量的“”(双引号)和 + 来拼接才能得到我们需要的模版。但是这样是十分不方便的。
于是ES6中提供了模版字符串,用`(反引号)标识,用${}将变量括起来。上面的例子可以用模版字符串写成下面这样:
这样的做法就简洁了很多,我们不需要再使用大量的""和+来拼接字符串和变量。
上面代码中,模板字符串都是用反引号表示,如果在模板字符串中需要使用反引号,则需要在反引号前面用反斜杠转义。
模板字符串是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
多行字符串
用单引号或是双引号表示的字符串在编辑的过程中只能在一行显示,若要在多行显示需要在每一行结尾添加一个斜杠,这样的编辑方式对开发者显然不够友好,我们可以使用模板字符串的功能换行编辑字符串 代码如下所示:
因为使用了模板字符串,所以hello world如上所示并没有报错,如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
字符串中嵌入变量
我们在开发的过程中经常会遇到在字符串中嵌入变量的情况,以往我们都是使用字符串连接的方式。有了模板字符串,我们可以在字符串中添加变量或对象的属性,需要将变量名写在${}之中 代码如下所示:
这样的表达方式可以更友好地将代码呈现给开发者。
模板字符串调用函数
我们不仅可以将变量和对象的属性嵌入模板字符串,还可以将函数嵌入模板字符串,并显示出函数的返回值 代码如下所示:
如果模板字符串中嵌入的值是需要通过计算而得到的,我们就可以使用上面的方法来完成这个功能。
四、解构赋值
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值,解构赋值主要包括数组的解构赋值、对象的解构赋值、字符串的解构赋值、函数参数的解构赋值。
数组的结构赋值
实例代码如下:
对象的解构赋值 解构不仅可以用于数组,还可以用于对象。 实例代码如下:
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
字符串的结构赋值 字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象 示例代码如下:
函数参数的结构赋值 函数的参数也可以使用解构赋值。 实例代码如下:
上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量x和y。对于函数内部的代码来说,它们能感受到的参数就是x和y。
五、课后练习
- 熟练使用本节讲解的基础语法;
第12节:函数进阶
一、概述
在此前的内容中,我们已经学习了函数的基本用法,例如如何定义函数,如何调用函数,以及函数的形参、实参、返回值等内容。本节内容会继续讲解ES2015为函数赋予的新特性。
- 立即执行函数
- 闭包
- 函数默认值
- 箭头函数
- async函数
二、立即执行函数
立即执行函数顾名思义,就是声明之后立刻执行的函数,实例代码如下所示:
函数体被一个小括号包裹起来,然后后面紧跟着一个小括号,这样当函数被声明之后,就立刻被调用。
立即执行函数通常用来封装代码,例如下面的例子:
这段代码可以输出a + b
的结果,但是立即执行函数外部是不能访问a和b的,因此成功达到了封装的目的。
在比较古老的js代码中,大家都是这种方法来封装代码的,但是ES2015自带模块化语法,因此这种封装的写法就不太常见了,大家只做了解即可。
三、闭包
闭包是定义在函数内部的函数,我们用立即执行函数编写一个闭包的例子
立即函数内部的add函数就是闭包,闭包的特点是具有全局性,闭包内部的this关键字会指向全局对象window。
闭包的特性:内部函数未执行完,外部函数即使执行完成,外部函数中的变量也不会被销毁。
四、函数默认值
在ES2015版本之前,我们可以通过下面的方法设置函数参数的默认值 代码如下所示:
上面的实例代码,利用逻辑或运算符的能力重新给参数赋值,也就是说如果有参数传入,则赋值为传入的参数,如果没有,则为或运算的第二个值。
这种设置默认值的方式看起来很不友好,ES2015中新增了更直观的设置函数默认值的方法,实例代码如下所示:
五、箭头函数
在此前的课程中,我们学习了两种方法定义函数,
- 函数声明
- 函数表达式
在ES2015中增加了箭头函数的语法,可以使用=>
来定义函数,我们来对比一下三种写法,实例代码如下所示:
=>
左侧的括号存放参数=>
右侧的花括号存放函数体。
箭头函数简写
以上展示了一个完整的箭头函数的语法,这其实还不能体现箭头函数的优势。
箭头函数第一个优势是,简写的箭头函数可以让代码更简洁
- 如果函数只有一个形参,那么可以省略参数外面的括号。
- 如果函数体只有一个表达式作为返回值,可以省略花括号和
return
关键字
实例代码如下所示
箭头函数中的this
箭头函数不仅简化了函数的写法,而且让函数中的this指针变得更人性化。
此前我们学习过的,函数中的this指向谁,取决于是哪个对象调用了这个函数。
例如下面中的示例代码
在实例代码中
- sayMe方法输出的this是person这个对象,因为是person直接调用的sayMe。
- sayMeDelay方法因为加入了一个延迟函数,导致setTimeout中的函数由windows对象调用,因此this指向windows对象。
但是在开发的过程中,我们通常希望sayMedelay内部
在上面的代码中,我们定义了一个变量self用来存储指向person的this,这样我们就可以正确地输出lee。但是这样的写法看起来有给我们增加了一些复杂度,我们可以用箭头函数来解决这个问题,
在上面的代码中,成功地输出了两个lee,这是因为箭头函数中的this,不是调用函数时的this指向的对象,而是定义函数时this指向的对象,定义函数的时候,this指向的是person而不是window,所以这里可以正确地输出结果。
六、课后练习
- 说出箭头函数与普通函数的区别。
- 使用箭头函数延迟调用cat.sayName,输出cat对象的name属性。
第13节:面相对象
一、面相对象概述
首先面向对象是一种编程思想,是一种通过多个对象互相协作完成处理流程的编程思路【是对现实世界中一类事物的抽象,在编程中可以理解为是一种建立现实世界事物的模型】 推及到广义上,面向对象已经越了程序设计和软件开发,我认为面向对象又是一种思维方式,不局限于编程语言,甚至不局限编程本身,它把复杂的需求、业务逻辑抽丝剥茧、逐个分析。 主要分为:类的声明定义、对象的创建使用、面向对象拥有的特征【三大特征:封装、继承、多态】 封装:体现了对象对于敏感数据的保护特征 继承:体现了代码的复用和功能的扩展 多态:体现了不同操作环境中代码的多样性【程序的健壮性】 思路:大量的程序开发—软件的开发—解决问题—处理数据—CRUD【增删改查】
二、基于原型的面向对象
在ES2015版本之前,JavaScript是没有类的概念的,我们可以使用构造函数来模拟一个类,这在我们之前的课程中已经讲解过了,这里我们简单复习一下。
创建一个猫的构造函数
在上面的代码中,我们定义了一个构造函数Cat,通过var关键字创建了一个变量cat,cat有两个属性,name和age。我们可以直接输出cat.name;
我们可以通过原型属性为构造函数添加方法,例如我们给这个Cat添加一个shout方法,让这只猫可以叫 代码如下所示:
这样猫就有了shout方法,可以调用shout方法让猫叫。
通过上面的代码,我们可以模拟一个类的概念,但是这样的写法与真正面相对象语言的写法相比,确实更难以理解。所以ES2015中增加了类的概念。
三、ES2015中的面向对象
基于原型的继承方式,虽然实现了代码复用,但是行文松散且不够流畅,可阅读性差,不利于实现扩展和对源代码进行有效的组织管理。不得不承认,基于类的继承方式在语言实现上更健壮,且在构建可服用代码和组织架构程序方面具有明显的优势。所以,ES2015+中提供了基于类class的语法。但class本质上是ES2015+提供的一颗语法糖,正如我们前面提到的,JavaScript是一门基于原型的面向对象语言。
我们可以用class来定义一个类,然后可以在这个类中定义构造函数,方法和属性。 代码如下所示:
上面的这段代码与第二个案例的代码功能是完全一样的,但是通过class关键之定义一个猫的类,让这个类更像是一个整体,而非一个个零散的prototype组合起来的一个功能。
继承
- 使用prototype属性(ES5的方案,了解即可)
- ES6以后使用extends关键字(掌握)
课后练习
按要求实现功能:
- 拓展Date对象实现如下功能:实现dateFomate方法,返回值是 "xxxx年xx月xx日"。
- 定义一个Person类,让Student类和Teacher类继承Person,为Person类添加getlnfotmation方法,是student类和Teacher类都可以通过此方法获取个人信息
第14节:DOM(文档对象模型)
一、DOM的基本概念
本章节可谓是相当重要了,是让JavaScript灵动起来必不可少的一步
- 文档对象模型
- 定义了树状结构
- 定义了接口,可以用来操作树状结构
二、样式操作
docunment.getElementById()
:返回值是一个DOM节点docunment.getElementByClassName()
:返回值是一个DOM节点的集合- dom节点的属性innerHTML可以获取、设置元素内的所有内容
我们可以通过DOM提供的querySelector方法来获取元素,参数为CSS选择器,然后进一步操作它的样式 示例代码如下:
若需要通过js设置多个元素的样式,可以使用querySelectorAll方法, 示例代码如下:
三、绑定事件
事件就是文档或者浏览器窗口发生的一些特定的交互瞬间,例如:用户点击网页会触发点击事件(onclick),用户在元素上移动会触发鼠标移动事件(onmouseover/onmouseenter),鼠标移出(onmouseout/onmouseleave)又恢复原本模样等。
我们将一个函数赋值给一个事件,当这个事件被触发的时候,这个函数就会被执行。
四、操作属性
我们可以通过JavaScript获取和设置元素属性,例如input的value属性值,或者img的src属性。
首先我们来实现一个效果,在文本框中输入字符串,然后点击按钮用在控制台输出我们输入的字符串 示例代码如下:
我们还可以通过赋值的方式为一个元素设置属性,可以切换所示图片 示例代码如下:
当点击按钮的时候,通过赋值的方式把另一张图片的地址赋值给img标签的src属性,就实现了图片切换的效果
五、数学计算案例
下面我们来实现一个能完成数学计算的程序,页面有四个文本框和一个按钮,我们在第一个文本框输入一个数字,在第二个文本框输入一个操作符,第三个文本框再输入一个数字,然后当我们点击计算按钮的时候,会在第四个文本框计算出结果,这个例子和我们学习switch语句的时候写的例子很像,但是那时候我们没有可操作的页面,现在我们把计算功能写在一个函数中。 示例代码如下:
第15节:DOM操作
一、节点的分类
- 元素节点(querySelector;querySelectorAll)
- 属性节点(element.src;element.id)
- 文本节点(innerHTML) 上一节课我们讲解了获取元素节点,操作属性节点。本节课我们讲解添加和删除元素节点和编辑文本节点。
二、文本节点
在html中我们有一个h1标签和一个按钮,h1标签内已经有了一段文本。当我们点击按钮的时候,在h1标签中插入“hello world”
在DOM中还有另一个属性可以更方便地获取和设置文本节点,这个属性是innerHTML,我们写一个简单的例子来测试innerHTML属性 示例代码如下:示例连接
对比着两种方法,第一种方法需要创建文本节点,然后通过appendChild方法将节点追加到之前文本内容的后面,第二种方法则是直接用innerHTML覆盖之前文本节点的内容。如果要实现demo01的功能,需要改写一下事件内的代码示例连接
三、创建和添加元素节点
上一节我们讲解了如何获取和设置文本节点,本节讲解如何创建和添加元素节点。创建原始节点可以使用createElement方法,添加元素节点仍然可以用appendChild方法。接下来我们来一步一步完成一个任务列表的功能,html代码如下所示:
我们要是先一个功能,当点击按钮的时候,在列表中添加一个li元素
在这个案例中,我们已经成功地在ul标签中添加了li元素,但是li元素并没有文本节点,我们进一步改进点击事件中的内容
通过上面的代码,我们已经可以在ul中添加带有文本节点的li元素了,但是文本节点是固定的“鸭梨”,我们还可以进一步通过一个文本框,让用户自己填写要插入的内容
四、删除元素节点
我们可以通过removeChild方法删除元素,下面的例子我们来实现点击按钮,删除h1标签的效果
通过上面的代码可以知道,删除一个元素需要知道他的父级元素,然后通过父级元素的removeChild方法删除子级元素,那么如果不确定删除的元素的父级是哪个元素,我们该如何获取元素的父级元素呢,可以使用parentNode方法,我们直接来改写上面的代码
下面我们来实现一个删除水果列表中水果的功能
我们要实现点击删除按钮的时候,删除span父级的li元素
第16节:事件流
一、绑定事件
想要给一个元素绑定事件,我们有两种方法:使用内联事件或事件监听器。在之前的课程中,我们一直使用的是内联事件来为元素绑定事件,例如一个按钮的点击事件,代码如下
我们还可以用使用事件监听器为元素绑定事件,代码如下
下面我们用两种方法为按钮绑定事件
两种方法都能实现相同的效果,能成功的为按钮绑定了点击事件,但区别是使用addEventLitener可以无限制的为元素绑定事件,而内联事件后面的会覆盖前面的
第一个按钮第二次绑定的事件覆盖了第一次绑定的事件,第二个按钮两次绑定的事件都能被触发。
二、事件冒泡与事件捕获
接下来我们用事件监听器为三个div元素绑定点击事件,最外层的div宽高是300px,中间的div宽高都是200px,最内层的div宽高都是100px,那么思考一下,点击最内层的div,事件会如何触发,是只触发最内层的div,还是从内到外依次触发,还是从外到内依次触发
通过上面的例子我们可以看到,事件是从最内层开始触发,然后依次向外,输出的顺序是box3-box2-box1。导致这种顺序的原因是因为:事件流有事件捕获阶段和事件冒泡阶段,事件捕获阶段是从最外层元素开始一层一层进入到事件目标(也就是我们点击的那个元素),到达事件目标后,进入事件冒泡阶段,事件从最内层流向最外层,事件默认情况下在冒泡阶段触发,所以我们看到的是先输出box3,最后输出box1。
我们也可以将事件设置为捕获阶段触发
只要在添加事件方法中添加第三个参数为true,事件就会在捕获阶段被触发,这样输出的顺序就变成了box1-box2-box3。但是在日常开发中,我们几乎不用做此修改,让事件在冒泡阶段触发就可以了。
阻止事件冒泡
使用e.stopPropagation();可以阻止事件冒泡,即内层元素的事件触发不会导致外层元素的事件触发
阻止事件的默认行为
可以使用e.preventDefault()或者return fasle;
例如阻止a标签默认点击跳转到新页面
三、事件委托
利用事件流的原理,通过e.target()我们可以实现事件委托,事件委托可以简单的理解为将子级的事件委托给父级来处理,我们先来看一个简单的例子
网页中有两个按钮,他们的父级是一个div标签,现在我们希望给这两个按钮绑定事件,当我们点击按钮的时候输出按钮的文本内容,按照我们之前学过的知识,可以有如下写法
这种方法简单易懂,但是存在重复,两个按钮触发事件执行的代码完全一样,我们可以获取到所有按钮,再通过遍历绑定事件
通过遍历我们优化了代码,但是仍然存在问题,首先,如果按钮的数量特别多,每一个按钮都绑定依次事件会非常影响程序的性能,其次,就算不考虑性能,通过这种方法绑定事件,如果使用js新增了一个按钮,这个按钮因为初始化的时候没有绑定事件,所以无法点击。为了解决上述问题,我们可以使用事件委托的方式来实现上面的功能
在事件监听函数中,我们可以在形参的位置获取到事件对象event,事件对象中包含了事件相关的信息,通过event.target可以获取到我们的事件目标,在这个例子中事件目标就是我们点击的按钮,而我们事件绑定的是按钮的容器,这样就可以将自己元素的事件委托给父级来处理。
四、事件类型
鼠标事件、键盘事件、触屏事件
键盘事件
document.onkeydown
和document.onkeyup
通过e.keyCode获取键盘按键
通过键盘上下左右实现方块的移动
触屏事件
element.ontouchstart
:在元素上按下element.ontouchend
:在元素上按下后放开element.ontouchmove
:在元素上滑动
四、课后练习
一、实现如下功能(阻止事件冒泡)
- 点击一个按钮,显示一个容器的盒子;
- 点击容器,容器背景颜色改变;
- 点击容器按钮 容器关闭;
二、实现水果列表,让后添加的元素也可以删除(事件委托);
三、通过上下左右按键控制元素移动;
第17节:计时器方法
一、计时器方法概述
计时器方法可以实现在指定的时间过后,单次或重复调用函数的功能,setTimeout可以实现函数在指定毫秒数后单次执行,setInterval可以实现函数在指定毫秒数后重复执行,语法如下所示:
二、setTimeout(只执行一次)
下面我们来实现一个效果,页面加载3秒后在控制台输出hello world
当计时器开始计时后,我们可以使用clearTimeout方法让计时器停下来,下面我们来定义一个按钮,当页面加载后,如果我们在3秒钟之内点击按钮,计时器会停止,不会输出hello world,如果不点击按钮,3秒钟之后就会输出hello world
setTimeout方法会返回一个整数类型的值,通过这个值,我们可以停止计时器,我们将setTimeout方法的返回值赋值给一个变量,当点击按钮的时,使用clearTimeout()方法
,传入t,这样计时器就会停止,hello world就不会在控制台输出。
三、setInterval(不停执行)
setInterval的用法与setTimeout的用法非常类似,都是传入两个参数,第一个参数是计时器执行的函数,第二个参数是毫秒数。下面我们来实现一个效果,每3秒钟在控制台输出依次hello world
从代码可以看出,setInterval与setTimeout完全相同,区别在于setInterval参数中的函数没个指定毫秒数后都会重复执行,当我们不希望计时器重复执行的时候,就可以使用clearInterval
方法来停止计时器
下面我们来实现一个效果,让控制台输出每隔1秒按顺序输出正整数,从数字1开始输出
上面的代码与之前有一点区别,我们并没有直接给setInterval传递一个匿名函数,而是先定义了一个函数showNumber,然后将showNumber传递给setInterval,这两种写法效果是一样的,但是如果将匿名函数传入setInterval,这个函数将不能被调用。
在上面代码的结尾,我们在页面加载之后调用了一次showNumber,目的是为了让页面加载的时候就输出1,否则我们将要等待一秒之后才能看到控制台输出1。
这个案例会一直输出数字,下面我们来改进这个例子,当数字为10的时候就停止,效果看起来有些想之前讲过的for循环输出数字,但用计时器输出可以实现每个1秒输出一个数字,而不是连续的输出
我们通过一个if语句判断n的值,当n到达10的时候,就停止计时器,这样计时器就不会再继续输出数字了。
我们还可以继续用按钮控制计时器,这次我们定义一个h1标签存放数字,再用两个按钮来实现“开始计数”和“停止计数”功能
在网页中我们经常会看到指定秒数跳转到其他网页,location.href
可以实现页面跳转,我们可以用计时器方法来实现这个功能
四、防抖(debounce)与节流(throttle)
解决性能问题,短时间内多次触发事件会造成性能问题,尤其是事件内部有大量业务逻辑时。例如
widows.onscroll
窗口滚轮事件,一旦窗口滚动,就会在短时间内大量触发。还有input输入搜索内容,当停下输入时才执行搜索。
防抖
对于短时间内多次触发事件的情况,可以使用防抖停止事件持续触发
节流
防止短时间内多次触发事件,但是在间隔一定时间后还是需要不断触发
使用闭包封装防抖和节流
第18节:BOM概述
一、概述
《javaScript高级程序设计》这本书讲过: javaScript = ECMAScript + DOM + BOM
- DOM: 文档对象模型 Document Object Model
- BOM:浏览器对象模型 Browser Object Model
二、Window对象(全局对象)
windows对象是全局对象,所有在浏览器可以直接使用的方法都是window对象的方法,其中document对象和alert()方法就是window的属性,使用时window.document和window.alert() 一般可省略window。
首先写一个打开vscode写一个案例计时器方法 示例代码如下:
如上代码时一个计时器方法使用setTimeout方法,一秒钟输出一次hello word;
计时器方法上节已经讲过了,本节主要讲的还是弹出框方法,弹出框的方法主要分为三种:
- alert()
- prompt()
- confirm()
在开发应用中,一般不适用系统自带的弹出框,移动端可能会被屏蔽。
三、location对象
- location.href - 属性返回当前页面的URL - "https://www.baidu.com"
- location.hostname - 主机的域名 - "wwww.baidu.com"
- location.pathname - 当前页面的路径和文件名 "/s"
- location.port - 端口 - "8080"
- loaction.protocol - 协议 - "https:"
四、navigator对象
获取访问者浏览器的信息
navigator.userAgent 检查当前设备,并在控制台输出
判断访问者的浏览器类型
第19节:原始类型与引用类型
一、原始类型与引用类型的基本概念
原始类型赋值给变量,遍历存储的是这个值本身,而你用类型赋值给变量,变量存储的是一个引用,这个引用会指向内存中的这个对象。
二、原始类型与引用类型的差异
接下来,我们在实际案例中展示一下原始类型与引用类型的区别:
原始类型与引用类型赋值的区别
实例代码(demo01.html)
字符串是原始类型,str1和str2两个变量存储的都是字符串类型的数据,所以对其中一个变量赋值不会影响第二个变量。
下面我们将程序中的字符串换成对象,再来看看这个例子(demo02.html)。
程序最后输出的两个值是相同的,但是我们只是将obj1的name属性赋值为xiaohong,并没有将obj2的name属性赋值为xiaohong,为什么他们都变成了同一个值?这就是原始类型与引用类型的区别:变量存储的原始类型,仅仅是存储它的值,所以我们将存储原始类型的变量重新赋值,不会影响其他变量,但是变量存储引用类型的时候,情况有所改变,变量并不是存储这个对象本身,而是存储这个对象的引用,而这个引用可以指向这个变量本身,所以当我们将obj1赋值给obj2的时候,其实是让这两个变量的引用同时指向一个对象,这样当我们改变这个对象的时候,两个变量都会有变化。
原始类型与引用类型比较的区别
接下来我们来看一下原始类型与引用类型比较的时候有什么来区别(demo03.html)。
在上面的代码中,两个字符串都是'hello world'他们比较后的返回值是true,但是两个对象name属性都是'xiaoming',比较后的返回值却是false,这也是原始类型与引用类型本质上的却别导致的,原始类型存的是值,比较的也是值,如果值相等,则返回true,如果值不等,则返回false,引用类型存的是应用,比较的也是引用,如果两个引用指向同一个对象,返回true,指向不同对象,则返回false,上面的例子中,两个对象虽然属性相同,但明显是不同的两个对象,他们就像两个重名的学生一样,即使有相同的名字,也不会是同一个人,所以返回值是false。
原始类型与引用类型传参的区别
下面我们分别将原始类型和引用类型当做参数传给一个函数,看看有什么样的区别(demo04.html)
在上面的代码中我们将变量str传给函数fun,在fun内部将参数将另一个字符串赋值给参数,然后在函数外面输出str,发现str的值并没有变,然后我们再来看下面这个例子(demo05.html)
将参数换成引用类型后,我们发现再次输出obj的时候,它的值已经变成了函数中赋的值,这是因为原始类型传参的时候,实参是形参的副本,改变形参的时候不会影响实参,而应用类型传参的时候,形参和实参的对象都指向一个引用,这样当我们修改形参的值的时候,其实是操作了内存中的对象,所以函数外部变量的值也就跟着变化了。
原始类型与引用类型的类型检测
- 原始类型:typeof(值)
- 引用类型:值 instanceof 类型
三、对象的浅度克隆
上面我们说了原始类型与引用类型的区别,在实际开发的过程中,有一种情况,我们需要得到一个对象的备份。例如我们有一个对象:
现在希望或得一个student对象的克隆对象,这两个对象所有属性都相同,我们修改其中一个对象的时候不会影响另一个对象。我们来编写一个函数,参数是一个对象,返回值是这个对象的克隆对象。
我们首先需要了解一下for...in语句(demo06.html)
我们通过for...in语句可以遍历对象中的属性和属性值,这样我们就可以很容易地实现对象克隆的功能(demo07.html)。
在上面的代码中,我们成功复制了student1,当我们修改student1的时候,student2不会改变。但是在上面的例子中,对象的所有属性值都是原始类型,如果将对象换成下面的对象
然后再用我们的clone方法克隆这个对象,就会发现,当我们改变friends属性的时候,两个对象都会改变。所以这样的clone方法只能叫“浅度克隆”,如果希望对象中所有属性都能被克隆,那么需要“深度克隆”,“深度克隆”我们会在下一章讲解。
第20节:异步编程
一、同步与异步
异步: 可以多条任务线去执行程序,一条任务卡顿不影响其他任务。
二、异步数据场景
二、回调函数
回调函数是将函数作为参数传递给另一个函数,通过回调函数可以获取异步数据,实例代码如下所示:
接下来制作一个案例巩固这个例子:定义两个异步获取数据的函数,一个是getFirstName,另一个是getLastName,将两个函数获取到的数据连接成为一个名字,实例代码如下所示:
虽然成功获取到了数据,但是通过上面的例子,我们发现,多个异步数据使用回调函数来嵌套,代码的维护成本会越来越高,接下来,我们使用es2015的语法来逐步优化这个案例。
三、promise对象
在ES2015中新加入了Promise对象,Promise对象用来解决异步问题,关于异步问题,我们会在第8章详细讲解,本章只要概括性地了解Promise对象的语法即可,创建一个Promise对象的语法如下所示:
Promise构造函数的参数是一个函数,而函数的形参resolve同样也是一个函数,调用resolve可以将数据从promise对象中传递出来。
使用Promise对象,模拟刚才的异步获取数据,实例代码如下所示:
使用Promise对象,定义刚才案例中的两个函数,一个是getFirstName,另一个是getLastName,将两个函数获取到的值链接到一起。
四、async函数
async函数
async函数与Promise对象结合使用,可以优雅地处理异步问题,声明async函数的语法如下所示:
我们用一个实际的案例来讲解async函数与Promise对象的用法。
在Promise对象的示例中,我们用resolve函数将数据传递出来之后,然后可以用async函数中的await关键字接收数据,完整的实例代码如下所示。
await后面跟着一个Promise对象,可以获取到Promise对象内部resolve传递出来的数据,这这里需要注意的是:
await关键字必须写在async函数内部。
第21节:倒计时效果
一、倒计时效果需求分析
在电商网站中我们经常会遇到倒计时抢购的网页效果,在页面中会有一个倒计时的始终,当时间距离目标时间越近,时钟的时间就越小。需要统计日、时、分、秒。当最后一秒结束后,一个抢购按钮从不可点击变成可点击。
二、需求分析
通过需求的描述,我们知道程序中有两个时间,一个是当前时间,一个是目标时间,当前时间是不断变化的,目标时间不会变化。倒计时始终记录的是当前时间和目标时间的时间差。那么如何计算两个时间的时间差呢,我们可以通过时间戳来实现
我们已经输出了距离目标时间的时间戳,现在我们需要一个函数将毫秒数转换成天、小时、分钟和秒。
下面我们可以通过这个行数,将我们得到的距离目标时间的毫秒数,计算出距离目标时间的天、小时、分钟和秒,并将时间更新到页面上。 示例代码如下
- 感谢你赐予我前进的力量