JavaScript
JavaScript基础语法、DOM、BOM、事件高级、PC端网页特效、本地存储等核心内容。
1. 计算机基础
1.1 编程语言和标记语言的区别
编程语言:较强的逻辑和行为能力,if,else,while
标记语言:如html,不用向计算机发送指令,常用于格式化或链接,是用来被读取的
程序员用编程语言编写程序发出指令控制计算机来实现任务,高级语言需要翻译器转换为计算机能识别的机器语言(二进制化)
1.2 数据存储
使用 0 和 1 表示数据,本质都是数据
以二进制数据的形式存放在硬盘中
数据存储单位:
- 位 bit(可以保存一个 0 或 1)
- 字节 Byte(B)= 8b
- 千字节 KB = 1024B
- 兆字节 MB = 1024KB
- 吉字节 GB = 1024MB
- 太字节 TB = 1024GB
2. 初识 JS
2.1 JS 是什么
运行在客户端(自己的电脑)的脚本语言(script —— 脚本)
不需要编译,由 js 解释器(引擎)逐行进行解释并执行
2.2 JS 作用
- 表单动态校验(不需要经过后台直接告知输入内容是否合格,减轻服务器的压力)
- 网页特效
- 服务端开发(Node.js)
- 桌面程序
- App
- 物联网
- 游戏开发
2.3 HTML/CSS 和 JS
- HTML 决定网页结构和内容
- CSS 决定网页呈现的样子
- JS 实现业务逻辑和页面控制,决定功能
2.4 浏览器执行 JS
浏览器分成两部分:渲染引擎和 JS 引擎
- 渲染引擎:用来解析 HTML 与 CSS,俗称内核,比如 chrome 浏览器的 blink,老版本的 webkit
- JS 引擎:也称为 JS 解释器。用来读取网页中的 JavaScript 代码,对其处理后运行,比如 chrome 浏览器的 V8
浏览器本身不会执行 js 代码,是通过内置的 js 引擎解释器来执行 js 代码,js 执行代码逐行解释每一句源码(转换为机器语言),后由计算机执行
2.5 JS 组成
- ECMAScript 规定了 js 的编程语法和核心知识
- DOM 文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言的标准编程接口。通过 DOM 提供的接口可以对页面上的各种元素进行操作(大小、位置、颜色等)
- BOM 浏览器对象模型(Browser Object Model,简称 BOM)是指浏览器对象模型,它提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。通过 BOM 可以操作浏览器窗口,比如弹出框、控制浏览器跳转、获取分辨率等。
2.6 JS 代码书写位置
2.6.1 行内样式 JS
<input type="button" value="点我试试" onclick="alert('Hello World')" />- 可以将单行或少量 JS 代码写在 HTML 标签的事件属性中(以 on 开头的属性),如:onclick
- 注意单双引号的使用:在 HTML 中我们推荐使用双引号,JS 中我们推荐使用单引号
- 可读性差,在 HTML 中编入 JS 大量代码时,不方便阅读,引号容易错,多层嵌套很混乱
- 特殊情况下使用
2.6.2 内嵌 JS
<script>
alert("Hello World!");
</script>可以把多行 js 代码写到 <script> 标签里
2.6.3 外部 JS
<script src="my.js"></script>- 利于 HTML 页面代码结构化,把单独 JS 代码独立到 HTML 页面之外,既美观,又方便
- 引用外部 JS 文件的 script 标签中间不可以写代码
- 适合于 JS 代码量比较大的情况
2.7 JS 注释
2.7.1 单行注释
// 单行注释快捷键:ctrl + /
2.7.2 多行注释
/*
多行注释
*/默认快捷键:alt + shift + a
2.8 JS 输入输出语句
| 方法 | 说明 |
|---|---|
alert(msg) | 浏览器弹出警示框 |
console.log(msg) | 浏览器控制台打印输出信息 |
prompt(info) | 浏览器弹出输入框,用户可以输入 |
alert()主要用来显示消息给用户console.log()用来给程序员看自己运行时的消息
3. JS 语法基础
3.1 变量
3.1.1 变量是什么
用于存放数据的容器,我们通过变量名获取数据,数据可以修改
- 本质:变量是程序在内存中申请的一块用来存放数据的空间
3.1.2 变量的使用
1. 声明变量 var age
var 是一个 JS 关键字,用来声明变量(variable 变量的意思)。使用该关键字声明变量后,计算机会自动为变量分配内存空间。
age 是程序员定义的变量名,我们要通过变量名来访问内存中分配的空间
2. 赋值
age = 10; // 给这个变量赋值为10= 即赋值的意思,变量值是程序员保存到变量空间里的值
3. 变量初始化
// 声明变量同时赋值为18
var age = 18;3.1.3 变量语法拓展
1. 更新变量
一个变量被重新赋值,原有的值会被覆盖,以最后一次赋值为准
var age = 18;
age = 81; // 结果是812. 同时声明多个变量
同时声明多个变量时,只需要写一个 var,多个变量名之间使用英文逗号隔开。
var age = 18,
address = "火影村",
salary = 15000;3. 声明变量特殊情况
| 情况 | 说明 | 结果 |
|---|---|---|
var age; console.log(age); | 只声明,不赋值 | undefined |
console.log(age) | 不声明不赋值直接使用 | 报错 |
age = 10; console.log(age); | 不声明只赋值 | 10 |
不声明不赋值直接使用,会报错,后面的代码都会不执行
3.1.4 变量命名规范
- 由字母(A-Z,a-z),数字(0-9),下划线(_),美元符号($)组成
- 如:usrAge,num01,__name
- 严格区分大小写。
var app和var App是两个变量 - 不能以数字开头(字母或者下划线开头)
- 不能是关键字,保留字。例如:var,for,while
- 遵循驼峰命名法。首字母小写,后面单词的首字母需要大写。如 myFirstName
- 推荐翻译网站:有道 / 爱词霸
- 不能有空格,不能用 -
3.1.5 交换两个变量
使用一个临时变量,用来做中间存储
// 交换两个变量的值 核心是临时变量 temp
var temp;
var apple1 = 1;
var apple2 = 2;
temp = apple1;
apple1 = apple2;
apple2 = temp;
alert(apple1);
alert(apple2);3.2 数据类型
3.2.1 为什么需要数据类型
不同的数据所占存储空间不同,为了便于把数据分成所需内存大小不同的数据,充分利用存储空间,所以定义不同的数据类型
3.2.2 变量的数据类型
JavaScript 是一种弱类型或者说动态语言。这意味着不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。
var age = 10; // 这是一个数字型
var areYouOk = "使得"; // 这是一个字符串- 在代码运行时,变量的数据类型是由 JS 引擎根据
=右边变量值的数据类型来判断的,运行完毕之后,变量就确定了数据类型。 - JavaScript 拥有动态类型,同时也意味着相同的变量可用作不同的类型。
var x = 6; // x 为数字
var x = "Bill"; // x 为字符串3.2.3 数据类型的分类
- 基本数据类型(Number, String, Boolean, Undefined, Null)
- 复杂数据类型(Object)
3.2.4 简单数据类型
| 简单数据类型 | 说明 | 举例 |
|---|---|---|
| Number | 数字型,包含整型值和浮点型值 | 0.21 / 0 |
| Boolean | 布尔值类型,如 true,false,等价于 1 和 0 | false |
| String | 字符串类型,如 "张三" | "" |
| Null | var a = null; 声明了变量 a 为空值 | null |
| Undefined | var a; 声明了变量 a 但是没有赋值,此时 a = undefined | undefined |
注意:JS 里的字符串都带引号
3.2.4.1 数字型 Number
JavaScript 数字类型既可以用来保存整数值,也可以保存小数(浮点数)。
var age = 12; // 整数
var Age = 21.3747; // 小数数字型进制
最常见的进制有二进制、八进制、十进制、十六进制。
// 1.八进制数字序列范围:0~7 逢8进1
var num1 = 07; // 对应十进制的7
var num2 = 019; // 对应十进制的19
var num3 = 08; // 对应十进制的8
// 2.十六进制数字序列范围:0~9以及A~F
var num = 0xa;在 JS 中八进制前面加 0,十六进制前面加 0x
数字型范围
- JS 中数值的最大值:
Number.MAX_VALUE即 1.7976931348623157e+308 - JS 中数值的最小值:
Number.MIN_VALUE即 5e-32
数字型三个特殊值
alert(Infinity); // Infinity(无穷大)
alert(-Infinity); // -Infinity(无穷小)
alert(NaN); // NaN - Not a Number,代表任何一个非数值console.log("你好" - 100); // 输出为 NaNisNaN
用来判断非数字,并且返回一个值,如果是数字返回的是 false,如果不是数字返回的是 true
var userAge = 21;
var isOK = isNaN(userAge);
console.log(isOK); // false 21不是一个非数字3.2.4.2 字符串型 String
字符串型可以是引号中的任意文本,其语法为"双引号"和'单引号'
var strMsg = "我爱北京天安门~"; // 使用双引号表示字符串
var strMsg = "我爱北京"; // 使用单引号表示字符串
// '12' 'true' 是字符串不加引号会报错,被认为是 JS 代码,但是 JS 没有这些语法
HTML 标签里面的属性使用的是双引号,JS 推荐使用单引号
字符串引号嵌套
JS 可以用单引号嵌套双引号,或者用双引号嵌套单引号(外双内单,外单内双)
不能单双搭配,引号会采取就近原则
var strMsg = '我是"快乐的"郭小佳';
var strMsg = "我是'快乐的'郭小佳";字符串转义符
| 转义符 | 解释说明 |
|---|---|
\n | 换行符,n 是 newline |
\\ | 斜杠 \ |
\' | ' 单引号 |
\" | " 双引号 |
\t | tab 缩进 |
\b | 空格,b 是 blank 的意思 |
转义符本质是字符,要写到引号的里面
字符串长度
字符数量即字符串长度,length 属性获取整个字符串的长度。
var str = "my name is andy";
console.log(str.length); // 15字符串拼接
- 多个字符串之间可以使用
+进行拼接,其拼接方式为 字符串 + 任何类型 = 拼接之后的新字符串 - 拼接前会把与字符串相加的任何类型转成字符串,再拼接成一个新的字符串
// 1. 字符串相加
alert("hello" + " " + "World"); // hello World
// 2. 数值字符串相加
alert("100" + "100"); // 100100
// 3. 数值字符串 + 数值
alert("12" + 12); // 1212
// 4. 数值 + 数值
alert(12 + 12); // 24最终结果是字符串类型 —— 数值相加,字符相连
字符串拼接加强(变量)
console.log("Pink老师" + 18); // 只要有字符就会相连
var age = 18;
console.log("Pink老师" + age); // Pink老师18
console.log("Pink老师" + age + "岁啦"); // Pink老师18岁啦将字符串和变量拼接,变量可以很方便地修改里面的值
变量不能加引号,因为加了引号会变成字符串
如果变量两侧都有字符串拼接,口诀"引引加加",删掉数字,变量写加中间
console.log("Pink老师" + age + "岁啦"); // Pink老师18岁啦3.2.4.3 布尔型 Boolean
- 布尔类型有两个值:true 和 false,其中 true 表示真(对),而 false 表示假(错)。
- 布尔型和数字型相加的时候,true 的值为 1,false 的值为 0。
var flag = true;
console.log(flag + 1); // 2 true当1来看,false当0来看3.2.4.4 Undefined
一个声明后没有被赋值的变量会有一个默认值 undefined
var str;
console.log(str); // undefined
var variable = undefined;
console.log(variable + "Pink"); // undefinedPink
console.log(variable + 18); // NaN
// 未定义加上数字 = NaN3.2.4.5 Null
一个声明变量给 null 值,里面存的值为空
var space = null;
console.log(space + "pink"); // nullpink
console.log(space + 1); // 1
// 数字加空值,等于没加
console.log(true + space); // 1
// true 在加法运算里当成 1 来看3.2.5 获取变量数据类型
typeof
typeof 可用来获取检测变量的数据类型
var num = 18;
console.log(typeof num); // 结果 number| 类型 | 例 | 结果 |
|---|---|---|
| String | typeof "小白" | "string" |
| Number | typeof 18 | "number" |
| Boolean | typeof true | "boolean" |
| Undefined | typeof undefined | "undefined" |
| Null | typeof null | "object" |
字面量
字面量是在源代码中一个固定值的表示法,通俗来说,就是字面量表示如何表达这个值。
- 数字字面量:8,9,10
- 字符串字面量:"前端"
- 布尔字面量:true,false
3.2.6 数据类型转换
把一种数据类型的变量转换成另外一种数据类型
转换为字符串型
| 方式 | 说明 | 案例 |
|---|---|---|
toString() | 转成字符串 | var num = 1; alert(num.toString()); |
String() 强制转换 | 转成字符串 | var num = 1; alert(String(num)); |
| 加号拼接字符串 | 和字符串拼接的结果都是字符串(隐式转换) | var num = 1; alert(num + "我是字符串"); |
转换为数字型
| 方式 | 说明 | 案例 |
|---|---|---|
parseInt(string) 函数 | 将 string 类型转成整数数值型 | parseInt('78') |
parseFloat(string) 函数 | 将 string 类型转成浮点数数值型 | parseFloat('78.21') |
Number() 强制转换函数 | 将 string 类型转换为数值型 | Number('12') |
| js 隐式转换 (- * /) | 利用算术运算隐式转换为数值型 | '12' - 0 |
// 1.parseInt()
var age = prompt("请输入您的年龄");
console.log(parseInt(age)); // 数字型 18
console.log(parseInt("3.14")); // 3 取整
console.log(parseInt("3.94")); // 3,不会四舍五入
console.log(parseInt("120px")); // 120,会去掉单位
// 2.parseFloat()
console.log(parseFloat("3.14")); // 3.14
console.log(parseFloat("120px")); // 120,会去掉单位
// 要以数字开头,否则会输出 NaN
// 例如:parseFloat('rem120px') // NaN
// 3.利用 Number(变量)
var str = "123";
console.log(Number(str));
console.log(Number("12"));
// 4.利用了算术运算 - * / 隐式转换
console.log("12" - 0); // 12
console.log("123" - "120"); // 3
// 如果这里是加号,那就是字符串连接的意思
console.log("123" * 1); // 123简单加法器案例:
// 提示用户输入两个数字并计算和
var num1 = prompt("请输入第一个数字");
var num2 = prompt("请输入第二个数字");
var result = parseFloat(num1) + parseFloat(num2);
alert("结果是:" + result);转换为布尔型
| 方法 | 说明 | 案例 |
|---|---|---|
Boolean() 函数 | 其他类型转成布尔值 | Boolean('true') |
- 代表空、否定的值会被转换为
false,如''、0、NaN、null、undefined - 其余的值都会被转换为
true
3.3 JS 运算符
运算符的分类
运算符(operator)也被称为操作符,是用于实现赋值、比较和执行算数运算等功能的符号。
JavaScript 中常用的运算符有:
- 算数运算符
- 递增和递减运算符
- 比较运算符
- 逻辑运算符
- 赋值运算符
3.3.1 算术运算符
算术运算使用的符号,用于执行两个变量或者值的算术运算
| 运算符 | 描述 |
|---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取余(取模),返回的除法余数,如 9 % 2 = 1 |
- 取模运算(Modulus Operation),简记 mod,尽可能让商向负无穷靠近,即 a ÷ b 向下取整得到余数
- 取余运算(Remainder Operation),简记 rem,尽可能让商向0靠近,即 a ÷ b 向着 0 的方向取整得到余数
浮点数的精度问题
浮点数值的最高精度是 17 位小数,但在进行算数计算时其精确度远远不如整数
var result = 0.1 + 0.2; // 结果不是0.3,是0.30000000000000004
console.log(0.07 * 100); // 结果不是7,而是7.000000000000001所以不要直接判断两个浮点数是否相等
先乘除后加减,判断能被整除:% 取余,余数为 0
表达式和返回值
表达式:由数字,运算符,变量等以能求得数值的有意义排列方法所得的组合
最终会有一个结果,返回给开发者,叫返回值
3.3.2 递增和递减运算符
需要反复给数字变量添加或减去 1,可以用 ++ 或 -- 来完成
放在变量前面时,我们称为前置递增(递减)运算符, 放在变量后面时,我们称为后置递增(递减)运算符, 注意:递增和递减运算符必须和变量配合使用。
前置递增运算符
++num 前置递增,自加 1,类似 num = num + 1
++num 先自加,后返回值
var num = 10;
alert(++num + 10); // 21后置递增运算符
num++ 后置递增,自加 1,类似 num = num + 1
num++ 先返回原值,后自加
var num = 10;
alert(10 + num++); // 20前置和后置如果单独使用,效果一样
3.3.3 比较(关系)运算符
比较运算符是两个数据进行比较时所使用的运算符,比较运算后,会返回一个**布尔值(true / false)**作为比较运算的结果。
| 运算符名称 | 说明 |
|---|---|
< | 小于号 |
> | 大于号 |
>= | 大于等于号(大于或者等于) |
<= | 小于等于号(小于或者等于) |
== | 判等号(会转型,自动把字符转换成数值类型) |
!= | 不等号 |
=== | 全等,要求值和数据类型都一致 |
等号比较
=赋值,把右边赋值给左边==判断,判断两边值是否相等(注意有隐式转换)===全等,判断两边的值和数据类型是否完全相同
3.3.4 逻辑运算符
用来进行布尔值运算的运算符,返回值是布尔值,多用于多个条件的判断
| 逻辑运算符 | 说明 |
|---|---|
&& | 逻辑与 and |
|| | 逻辑或 or |
! | 逻辑非 not |
逻辑与 &&
两边都是 true 才返回 true,否则为 false
逻辑或 ||
两边都是 false,返回 false,否则返回 true
逻辑非 !
取反符,用来取一个布尔值相反的数
var isOK = !true;
console.log(isOK); // false短路运算(逻辑中断)
短路运算的原理:当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值
① 逻辑与
// 语法:表达式1 && 表达式2
// 如果第一个表达式的值为真,则返回表达式2
// 如果第一个表达式的值为假,则返回表达式1② 逻辑或
// 语法:表达式1 || 表达式2
// 如果第一个表达式的值为真,则返回表达式1
// 如果第一个表达式的值为假,则返回表达式2永远返回的是能决定整条表达式真假的那条表达式(前真后看,前假看前)
3.3.5 赋值运算符
用来把数据赋值给变量的运算符
| 赋值运算符 | 说明 |
|---|---|
= | 直接赋值 |
+=、-= | 加(减)一个数后再赋值 |
*=、/=、%= | 乘(除,取余)后再赋值 |
3.3.6 运算符优先级
| 优先级 | 运算符 | 顺序 |
|---|---|---|
| 1 | 小括号 | () |
| 2 | 一元运算符 | ++ -- ! |
| 3 | 算数运算符 | 先 *、/,后 +、- |
| 4 | 关系运算符 | >、>=、<、<= |
| 5 | 相等运算符 | ==、!=、===、!== |
| 6 | 逻辑运算符 | 先 &&,后 || |
| 7 | 赋值运算符 | = |
| 8 | 逗号运算符 | , |
3.4 流程控制
流程控制主要有三种结构,分别是顺序结构、分支结构和循环结构,这三种结构代表三种代码执行的顺序
3.4.1 顺序流程结构
按照代码先后顺序执行
3.4.2 分支流程控制
根据条件不同,执行不同路径的代码,得到不同结果
3.4.2.1 if 语句
// 条件成立执行代码,否则什么也不做
if (条件表达式) {
// 条件成立执行的代码语句
}如果小括号里为 true,那么执行大括号的语句
3.4.2.2 if else 双分支语句
// 条件成立,执行if里面代码,否则执行else里面的代码
if (条件表达式) {
// [如果]条件成立执行的代码
} else {
// [否则]执行的代码
}3.4.2.3 if else if 多分支语句
if (条件表达式1) {
语句1;
} else if (条件表达式2) {
语句2;
} else if (条件表达式3) {
语句3;
} else {
// 上述条件都不成立执行此处代码
}多选一,最后只有一个语句执行,else if 中间有个空格
3.4.2.4 三元表达式
// 语法结构:表达式1 ? 表达式2 : 表达式3;
// 执行思路:如果表达式1为true,则返回表达式2的值,
// 如果表达式1为false,则返回表达式3的值(双分支简写)3.4.2.5 switch 分支流程控制
也是多分支语句,基于不同条件执行不同代码
当要针对变量设置一系列的特定值的选项时使用
switch (表达式) {
case value1:
// 表达式等于 value1 时要执行的代码
break;
case value2:
// 表达式等于 value2 时要执行的代码
break;
default:
// 表达式不等于任何一个 value 时要执行的代码
}switch 表达式的值会与结构中的 case 的值做比较,如果存在匹配全等(===),则与该 case 关联的代码块会被执行,并在遇到 break 时停止,整个 switch 语句代码执行结束
如果所有的 case 的值都和表达式的值不匹配,则执行 default 里的代码
执行 case 里面的语句时,如果没有 break,则继续执行下一个 case 里面的语句
switch 后面括号里的可以是表达式或值,通常是变量
case 后面跟一个选项的表达式或值,后面跟冒号
3.4.3 循环流程控制
3.4.3.1 for 循环
在程序中,一组被重复执行的语句被称之为循环体,能否继续重复执行,取决于循环的终止条件。由循环体及循环的终止条件组成的语句,被称之为循环语句。
for (初始化变量; 条件表达式; 操作表达式) {
// 循环体
}一行打印五个星星
var star = "";
for (var i = 1; i <= 5; i++) {
star += "☆";
}
console.log(star);3.4.3.2 双重 for 循环
循环嵌套是指在一个循环语句中再定义一个循环语句的语法结构,例如在 for 循环语句中,可以再嵌套一个 for 循环,这样的 for 循环语句我们称之为双重 for 循环。
for (外循环的初始; 外循环的条件; 外循环的操作表达式) {
for (内循环的初始; 内循环的条件; 内循环的操作表达式) {
需执行的代码;
}
}外面循环一次,里面循环全部
打印五行五列星星:
var star = "";
for (var j = 1; j <= 5; j++) {
for (var i = 1; i <= 5; i++) {
star += "☆";
}
// 每次满5个星星就加一次换行
star += "\n";
}
console.log(star);3.4.3.3 while 循环
while (条件表达式) {
// 循环体代码
}执行思路:
- 先执行条件表达式,如果结果为 true,则执行循环体代码;如果为 false,则退出循环,执行后面代码
- 执行循环体代码
- 循环体代码执行完毕后,程序会继续判断执行条件表达式,如条件仍为 true,则会继续执行循环体,直到循环条件为 false 时,整个循环过程才会结束
注:
使用 while 循环时一定要注意,它必须要有退出条件,否则会成为死循环
while 循环和 for 循环的不同之处在于 while 循环可以做较为复杂的条件判断,比如判断用户名和密码
3.4.3.4 do while 循环
do {
// 循环体代码 - 条件表达式为true的时候重复执行循环体代码
} while (条件表达式);执行思路:
- 先执行一次循环体代码
- 再执行表达式,如果结果为 true,则继续执行循环体代码,如果为 false,则退出循环,继续执行后面的代码
- 先执行再判断循环体,所以 do while 循环语句至少会执行一次循环体代码
3.4.3.5 continue 关键字
continue 关键字用于立即跳出本次循环,继续下一次循环(本次循环体中 continue 之后的代码就会少执行一次)。
3.4.3.6 break 关键字
break 关键字用于立即跳出整个循环
3.5 拓展阅读
3.5.1 解释性语言和编译型语言
解释型语言和编译型语言:
程序语言翻译成机器语言,一种是编译,一种是解释
编译器在代码执行之前编译,生成中间代码文件
解释器是在运行时进行及时解释,并立即执行
3.5.2 标识符,关键字,保留字
标识符:开发人员给变量,属性,函数,参数取的名字
不能是关键字或保留字
关键字:JS 本身已使用,不能充当变量名,方法名
保留字:预留的关键字
3.5.3 JS 命名规范
- 标识符命名规范:变量、函数的命名必须要有意义,变量一般使用名词,函数一般使用动词
- 操作符命名规范:一般操作符前后都有空格
- 单行注释规范:单行注释前有个空格
- 其他规范:括号前后空格,花括号对齐
3.5.4 断点调试
在浏览器中 F12 进入开发者模式 -> source -> 找到文件 -> 单击行号打点 -> F11 下一步
3.6 数组
数组(Array)是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式。
3.6.1 创建数组
JavaScript 中创建数组有两种方式:
- 利用
new创建数组 - 利用数组字面量创建数组
利用 new 创建数组
var 数组名 = new Array(); // 创建一个新的空数组
// 注意A要大写利用数组字面量创建数组
// 1. 利用数组字面量方式创建空的数组
var 数组名 = [];
// 2. 使用数组字面量方式创建带初始值的数组
var 数组名 = ["小白", "小黑", "小黄", "瑞奇"];
// 3. 数组中可以存放任意类型的数据,例如字符串,数字,布尔值等
var arrStus = ["小白", 12, true, 28.9];3.6.2 获取数组元素
数组的索引(下标)
索引(下标):用来访问数组元素的序号(数组下标从 0 开始)
// 定义数组
var arrStus = [1, 2, 3];
// 获取数组中的第2个元素
alert(arrStus[1]); // 结果是23.6.3 遍历数组
我们可以通过 for 循环索引遍历数组中的每一个元素
// 数组索引访问数组中的元素
var arr = ["red", "green", "blue"];
console.log(arr[0]); // red
console.log(arr[1]); // green
console.log(arr[2]); // blue
// for 循环遍历数组
var arr = ["red", "green", "blue"];
for (var i = 0; i < arr.length; i++) {
console.log(arrStus[i]);
}数组的长度
使用"数组名.length"可以访问数组元素的数量(数组长度)
是元素的个数而不是索引号
var arrStus = [1, 2, 3];
alert(arrStus.length); // 33.6.4 数组中新增元素
通过修改 length 长度新增数组元素
length 可读写,可通过修改其长度实现扩容的目的
通过修改数组索引新增数组元素
var arr1 = ["red", "green", "blue"];
arr1[3] = "pink";注意:若索引号已被占用,再赋值会被替换
不要直接给数组名赋值,否则里面的数组元素都会没有
3.6.5 数组案例
求数组中的最大值:
var arr = [1, 3, 5, 7, 9, 2, 4, 6, 8];
var max = arr[0];
for (var i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
console.log("最大值为:" + max);数组转换为分隔字符串:
var arr = ["red", "green", "blue", "pink"];
var str = arr.join("|");
console.log(str); // red|green|blue|pink3.7 函数
3.7.1 函数概念
相同或功能相似的被大量重复使用的代码
函数即封装一段可被重复调用执行的代码块
function getSum(num1, num2) {
var sum = 0;
for (var i = num1; i <= num2; i++) {
sum += i;
}
return sum;
}
getSum(1, 100);3.7.2 函数使用
3.7.2.1 声明函数
function 函数名() {
// 函数体代码
}function 是关键字,必须小写
函数是为了实现某个功能而定义的,通常命名为动词
3.7.2.2 调用函数
函数名(); // 通过调用函数名来执行函数体代码调用的时候千万不要忘记添加小括号
声明函数本身并不会执行代码,只有调用函数时才会执行函数体代码
3.7.2.3 函数的封装
把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口
3.7.3 函数的参数
3.7.3.1 形参和实参
在声明函数时,可以在函数名称后面的小括号中添加的参数,被称为形参,而在调用函数时,在函数名称后面的小括号中添加的参数,被称为实参。
参数的作用是在函数内部某些值不固定,通过参数可以在调用函数时传递不同的值进去
// 声明函数,可以定义任意多的参数,用逗号分隔
function 函数名(形参1, 形参2, 形参3...) {
// 函数体
}
// 调用函数
函数名(实参1, 实参2, 实参3...);实参和形参的多个参数之间用逗号分隔,形参相当于不用声明的变量
3.7.3.2 形参与实参个数不匹配问题
| 参数个数 | 说明 |
|---|---|
| 实参个数等于形参个数 | 输出正确结果 |
| 实参个数多于形参个数 | 只取到形参的个数 |
| 实参个数小于形参个数 | 多的形参定义为 undefined,结果为 NaN |
JS 中,形参默认值即为 undefined
3.7.4 函数的返回值
3.7.4.1 return 语句
function 函数名() {
...
return 需要返回的值;
}
函数名(); // 此时调用函数就可以得到函数体内 return 后面的值3.7.4.2 return 终止函数
使用 return 语句,函数会停止执行,并返回指定的值。
3.7.4.3 return 返回值
return 只能返回一个值。如果用逗号隔开多个值,以最后一个为准
var resNum = add(21, 6); // 调用函数,传入两个实参,并通过 resNum 接收函数返回值
alert(resNum); // 6若函数没有 return,返回值是 undefined。
3.7.4.4 break、continue、return 的区别
break:结束当前循环体(如 for、while)continue:跳出本次循环,继续执行下次循环(如 for、while)return:不仅可以退出循环,还能够返回 return 语句中的值,同时还可以结束当前的函数体内的代码
3.7.5 arguments 的使用
当不确定有多少个参数时,可以用 arguments 来获取。arguments 是当前函数的一个内置对象。所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的所有实参。
arguments 展示形式是一个伪数组,因此可以进行遍历,伪数组具有以下特点:
- 具有 length 属性
- 按索引方式储存数据
- 不具有数组的 push, pop 等方法
function fn() {
console.log(arguments);
// 里面存储了所有传递过来的实参
console.log(arguments.length);
for (var i = 0; i <= arguments.length; i++) {
console.log(arguments[i]);
}
}
fn(1, 2, 3);3.7.6 函数的两种声明方式
1. 自定义函数方式(命名函数)
利用函数关键字 function 自定义函数方式
// 声明定义方式
function fn() {}
// 调用
fn();2. 函数表达式方式(匿名函数)
// 这是函数表达式写法,匿名函数后面跟分号结束
var fn = function () {};
// 调用的方式,函数调用必须写到函数体下面
fn();fn 是变量名,不是函数名,fn 里面存储的是一个函数。
3. 立即执行函数(匿名函数)
避免全局变量之间的污染
(function () {
console.log(11);
})();
// 两种写法
// (function(){})() // 第二个()是调用的意思,里面是实参
// (function(){}())
(function (x, y) {
console.log(x + y);
})(1, 2); // 这个分号一定要有
(function (x, y) {
console.log(x + y);
})(1, 2);3.8 作用域
限定名字可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
JavaScript (ES6前) 中的作用域有两种:
- 全局作用域
- 局部作用域(函数作用域)
3.8.1 全局作用域
作用于所有代码执行的环境(整个 script 标签内部)或者一个独立的 JS 文件
3.8.2 局部作用域
作用于函数内的代码环境,就是局部作用域。因为跟函数有关系,所以也称为函数作用域
3.8.3 JS 没有块级作用域
- 块作用域由 {} 包括
- 在其他编程语言中,在 if 语句、循环语句中创建的变量,仅仅只能在本 if 语句、本循环语句中使用
3.8.4 全局变量和局部变量
全局变量:在全局作用域下声明的变量(在函数外部定义的变量)
- 全局变量在代码的任何位置都可以使用
- 在全局作用域下 var 声明的变量是全局变量
局部变量:在局部作用域下声明的变量(在函数内部定义的变量)
- 局部变量只能在该函数内部使用
- 在函数内部 var 声明的变量是局部变量
- 函数的形参实际上就是局部变量
区别:
- 全局变量:优点:在任何一个地方都可以使用,只有在浏览器关闭时才会被销毁。缺点:占内存
- 局部变量:缺点:只在函数内部使用,当其所在的代码块被执行时,会被初始化;当代码块运行结束后,就会被销毁。优点:节省内存空间
3.8.5 作用域链
- 根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链
- 作用域链:采取就近原则的方式来查找变量最终的值。
var num = 10;
function fn() {
// 外部函数
var num = 20;
function fun() {
// 内部函数
console.log(num); // 20,一级一级访问
}
}3.8.6 预解析
在当前作用域下,JS 代码执行之前,浏览器会把带有 var 和 function 声明的变量在内存中进行提前声明或者定义。
预解析也叫做变量,函数提升。
3.8.6.1 变量预解析
变量的声明会被提升到当前作用域的最上面,但是赋值不会被提升
console.log(num); // 结果是undefined
var num = 10;
// 相当于执行了以下代码
// var num;
// console.log(num);
// num = 10;3.8.6.2 函数预解析
函数的声明会被提升到当前作用域的最上面,但不会调用函数。
fn();
function fn() {
console.log("打印");
} // 控制台打印出字符串-打印函数声明代表函数整体,所以函数提升后,函数名代表整个函数,但是函数并没有被调用
3.8.6.3 函数表达式声明函数
函数表达式创建函数,会执行变量提升,此时接收函数的变量名无法正确调用。
fn();
var fn = function () {
console.log("想不到吧");
};
// 报错提示"fn is not a function"变量声明提升,fn 在提升之后的值是 undefined,而 fn 调用是在 fn 被赋值为函数体之前,此时 fn 的值是 undefined,所以无法正确调用
4. 对象
4.1 对象基础
在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的:
- 属性:事物的特征,在对象中用属性来表示(常用名词)
- 方法:事物的行为,在对象中用方法来表示(常用动词)
对象使某些数据存储更清晰,比如一个人的详细信息
4.1.1 利用字面量创建对象
对象字面量:就是花括号 {} 里面包含了表达这个具体事物(对象)的属性和方法
{} 里面采取键值对的形式表示
- 键:相当于属性名
- 值:相当于属性值,可以是任意类型的值(数字类型、字符串类型、布尔类型,函数类型等)
var star = {
name: "pink",
age: 18,
sex: "男",
sayHi: function () {
alert("大家好啊~");
},
};
// 多个属性或者方法中间用逗号隔开
// 方法冒号后面跟的是一个匿名函数4.1.2 对象的使用
对象的属性:存储具体数据的"键值对"中的"键"称为对象的属性,即对象中存储具体数据的项
对象的方法:存储函数的"键值对"中的"键"称为对象的方法,即对象中存储函数的项
访问对象的属性
- 对象里面的属性调用:
对象.属性名,这个小点.就理解为"的" - 对象里面属性的另一种调用方式:
对象['属性名'],注意方括号里面的属性必须加引号
console.log(obj.name);
console.log(obj["age"]);调用对象的方法
- 对象里面的方法调用:
对象.方法名(),注意这个方法名字后面一定加括号
obj.sayHi();变量、属性、函数、方法总结
- 属性是对象的一部分,而变量不是对象的一部分,变量是单独存储数据的容器。
- 变量:单独声明赋值,单独存在
- 属性:对象里面的变量称为属性,不需要声明,用来描述该对象的特征
- 方法是对象的一部分,函数不是对象的一部分,函数是单独封装操作的容器。
- 函数:单独存在的,通过"函数名()"的方式就可以调用
- 方法:对象里面的函数称为方法,方法不需要声明,使用"对象.方法名()"的方式就可以调用,方法用来描述该对象的行为和功能。
4.1.3 利用 new Object 创建对象
创建空对象:
var andy = new Object();使用内置构造函数 Object 创建对象,此时 andy 变量已经保存了创建出来的空对象
给空对象添加属性和方法:
通过对象操作属性和方法的方式
andy.name = "pink";
andy.age = 18;
andy.sayHi = function () {
alert("大家好啊");
};注意:Object 第一个字母 O 要大写
new Object 要用到 new 关键字
使用的格式:对象.属性 = 值
4.1.4 利用构造函数创建对象
构造函数:是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
// 构造函数的语法格式
function 构造函数名() {
this.属性 = 值;
this.方法 = function () {};
}
new 构造函数名();在 JS 中,使用构造函数要时要注意以下两点:
- 构造函数用于创建某一类对象,其首字母要大写
- 构造函数要和
new一起使用才有意义
// 1. 构造函数首字母要大写
// 2. 构造函数不需要 return 就可以返回结果
// 3. 调用构造函数必须使用 new
// 4. 我们只要 new Star() 调用函数就创建了一个对象
// 5. 构造函数内的属性和方法前面必须加 this,表示当前对象的属性和方法
function Stars(uname, age, sex) {
this.name = uname;
this.age = age;
this.seex = sex;
this.sing = function (sang) {
console.log(sang);
};
}
var ldh = new Stars("刘德华", 18, "男");
console.log(typeof ldh); // object 对象,调用函数返回的是对象
console.log(ldh.name);
console.log(ldh["sex"]);
ldh.sing("冰雨");
// 把冰雨传给了 sang- 构造函数,如
Stars(),抽象了对象的公共部分,封装到了函数里面,它泛指某一大类(class) - 创建对象,如
new Stars(),特指某一个,通过new关键字创建对象的过程我们也称为对象实例化
4.1.5 new 关键字的作用
new 在执行时会做四件事:
- 在内存中创建一个新的空对象。
- 让
this指向这个新的对象。 - 执行构造函数里面的代码,给这个新对象添加属性和方法
- 返回这个新对象(所以构造函数里面不需要 return)
4.1.6 遍历对象
for...in 语句用于对数组或者对象的属性进行循环操作
for (变量 in 对象名字) {
// 在此执行代码
}语法中的变量是自定义的,它需要符合命名规范,通常我们会将这个变量写为 k 或者 key。
for (var k in obj) {
console.log(k); // 这里的 k 是属性名
console.log(obj[k]); // 这里的 obj[k] 是属性值
}会在控制台输出对象所有的信息
4.2 内置对象
JavaScript 中的对象分为 3 种:自定义对象、内置对象、浏览器对象
内置对象就是指 JS 语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能
JavaScript 提供了多个内置对象:Math、Date、Array、String 等
4.2.1 查文档
学习一个内置对象的使用,只要学会其常用成员的使用即可,我们可以通过查文档学习,可以通过 MDN/W3C 来查询
MDN:https://developer.mozilla.org/zh-CN/
如何学习对象中的方法
- 查阅该方法的功能
- 查看里面参数的意义和类型
- 查看返回值的意义和类型
- 通过 demo 进行测试
4.2.2 Math 对象
Math 对象不是构造函数,它具有数学常数和函数的属性和方法。跟数学相关的运算(求绝对值,取整、最大值等)可以使用 Math 中的成员。
// Math 数学对象,不是一个构造函数,所以我们不需要 new 来调用,而是直接使用里面的属性和方法即可
Math.PI; // 圆周率
Math.floor(); // 向下取整
Math.ceil(); // 向上取整
Math.round(); // 四舍五入版就近取整 注意 -3.5 结果是 -3
Math.abs(); // 绝对值
Math.max() / Math.min(); // 求最大和最小值
console.log(Math.max(1, 99, 3)); // 99
console.log(Math.max(q, 99, "xiao")); // NaN
console.log(Math.max()); // -Infinity
console.log(Math.abs("-1")); // 隐式转换 会把字符型'-1'转换为数字型注意:上面的方法必须带括号
封装自己的数学对象
var myMath = {
PI: 3.141592653,
max: function () {
var max = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
},
};随机数方法 random()
返回小数,取值范围为 [0, 1)
构造返回两个数之间的随机整数,并且包含这两个数,代码如下:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
} // 包含了最大值和最小值
console.log(getRandomInt(1, 10));实现随机点名:
var arr = ["张三", "李四", "张五", "李六"];
console.log(arr[getRandomInt(0, 3)]);4.2.3 Date 对象
Date() 是一个构造函数,必须用 new 调用,实例化来创建一个对象
用来处理日期和时间
使用
- 获取当前时间必须实例化
var date = new Date();
console.log(date);
// 如果无参数则返回系统当前时间- 写了参数,返回括号里面的时间
参数常用写法:
- ① 数字型 2019, 10, 01(返回 11 月不是 10 月,因为月份从 0 开始)
- ② 字符串型 '2019-10-1 8:8:8' 或者 '2019/5/1'
var date1 = new Date(2019, 10, 1); // 返回11月不是10月
var date2 = new Date("2019-10-1 8:8:8"); // 返回10月日期格式化
var date = new Date();
console.log(date.getFullYear()); // 获取当前年份
console.log(date.getMonth() + 1); // 获取当前月份(0-11,需要+1)
console.log(date.getDate()); // 获取当前日期
console.log(date.getDay()); // 获取当前星期(0-6,0代表周日)
console.log(date.getHours()); // 获取当前小时
console.log(date.getMinutes()); // 获取当前分钟
console.log(date.getSeconds()); // 获取当前秒获取日期的总的毫秒形式
// 时间戳,距离1970年1月1号过了多少毫秒数
console.log(date.valueOf());
console.log(date.getTime());
console.log(+new Date());
console.log(Date.now());倒计时案例
核心算法:规定时间减去剩余时间即倒计时,但是不能拿时分秒相减
可以用时间戳来做,用户输入时间总的毫秒数减去现在时间的总的毫秒数得到的就是剩余时间的毫秒数。再把总的毫秒数转换为天、时、分、秒
转换公式如下:
- d = parseInt(总秒数 / 60 / 60 / 24); 计算天数
- h = parseInt(总秒数 / 60 / 60 % 24); 计算小时
- m = parseInt(总秒数 / 60 % 60); 计算分
- s = parseInt(总秒数 % 60); 计算秒
function countDown(time) {
var nowTime = +new Date(); // 返回当前总毫秒数
var inputTime = +new Date(time); // 用户输入时间总的毫秒数
var times = (inputTime - nowTime) / 1000; // 剩余总时间秒数
var d = parseInt(times / 60 / 60 / 24);
d = d < 10 ? "0" + d : d;
var h = parseInt((times / 60 / 60) % 24);
h = h < 10 ? "0" + h : h;
var m = parseInt((times / 60) % 60);
m = m < 10 ? "0" + m : m;
var s = parseInt(times % 60);
s = s < 10 ? "0" + s : s;
return d + "天" + h + "时" + m + "分" + s + "秒";
}
console.log(countDown("2019-5-1 18:00:00"));4.2.4 Array 对象
利用数组字面量创建数组
var arr = [1, 2, 3];
console.log(arr[0]);利用 new Array() 创建数组
var arr1 = new Array(); // 创建空数组
var arr2 = new Array(2); // 创建长度为2的数组,有两个空元素
var arr3 = new Array(2, 3); // 创建 [2, 3] 数组,表示里面有两个数组元素,2和3检测是否为数组
instanceof运算符,可以用来判断一个对象是否属于某种类型
var arr = [];
console.log(arr instanceof Array); // trueArray.isArray(参数)H5 新增的方法
console.log(Array.isArray(arr));添加数组元素
| 方法 | 描述 |
|---|---|
push() | 给原数组末尾添加一个或多个元素,返回新数组长度 |
unshift() | 在数组开头添加一个或多个元素,返回新数组长度 |
var arr = [1, 2, 3];
console.log(arr.push(4, 5, 6));
// push() 参数直接写数组元素即可,push 完毕,返回的结果是新数组的长度,原数组也会发生变化
console.log(arr.unshift("red", "purple"));
// unshift() 参数直接写数组元素即可,unshift 完毕,返回的结果是新数组的长度,原数组也会发生变化删除数组元素
| 方法 | 描述 |
|---|---|
pop() | 删除最后一个元素,返回删除的元素 |
shift() | 删除第一个元素,返回删除的元素 |
console.log(arr.pop());
// pop 删除最后一个元素,一次只能删除一个元素
// pop() 没有参数,pop 完毕,返回的是删除的元素,原数组也会变化
console.log(arr.shift());
// shift 删除第一个元素,一次只能删除一个元素
// shift() 没有参数,shift 完毕,返回的是删除的元素,原数组也会变化数组排序
| 方法 | 描述 | 案例 |
|---|---|---|
reverse() | 数组倒序 | 改变原来数组,返回新数组 |
sort() | 冒泡排序 | 改变原来数组,返回新数组 |
// 1.反转数组
var arr = ["pink", "red", "blue"];
arr.reverse();
console.log(arr);
// 2.冒泡排序
var arr1 = [3, 4, 7, 1];
arr1.sort();
console.log(arr1);降序排序:
arr1.sort(function (a, b) {
return b - a;
});升序排序:
arr1.sort(function (a, b) {
return a - b;
});获取数组索引
| 方法 | 描述 |
|---|---|
indexOf(数组元素) | 返回对应元素值的第一个索引,若找不到该元素则返回 -1 |
lastIndexOf() | 返回在数组中的最后一个的索引,若不存在则返回 -1 |
var arr = ["red", "green", "blue"];
console.log(arr.indexOf("blue"));数组转换为字符串
| 方法 | 描述 | 返回值 |
|---|---|---|
toString() | 把数组转换成字符串,逗号分隔每一项 | 返回一个字符串 |
join('分隔符') | 把数组中的元素转换成一个字符串 | 返回一个字符串 |
var arr = [1, 2, 3];
console.log(arr.toString()); // 1,2,3
var arr1 = ["green", "red", "blue"];
console.log(arr1.join("-")); // green-red-blue
// 若分隔符为空,则默认为逗号数组其他常用方法
| 方法 | 描述 |
|---|---|
concat() | 拼接两个数组,返回新数组 |
slice(begin, end) | 截取数组,返回新数组 |
splice(start, deleteCount, ...items) | 删除/插入元素,返回被删除的元素 |
4.2.5 字符串对象
基本包装类型
就是把简单数据类型包装成为了复杂数据类型,这样一个简单数据类型就有了属性和方法
var str = "andy";
console.log(str.length);
// 上面的代码可以执行
// 1. 生成临时变量,把简单数据类型包装为复杂数据类型
var temp = new String("andy");
// 2. 赋值给我们声明的字符变量
str = temp;
// 3. 销毁临时变量
temp = null;字符串的不可变
指的是值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间
根据字符返回位置
字符串的所有方法,都不会修改字符串本身,操作完成后会返回一个新的字符串。
| 方法名 | 说明 |
|---|---|
indexOf('要查找的字符', 开始的位置) | 返回指定内容在字符串中的位置,若找不到返回 -1,开始的位置是 index 索引号 |
lastIndexOf() | 从后往前找,只找第一个匹配的 |
var str = "改革春风吹满地,春天来了";
console.log(str.indexOf("春")); // 2
console.log(str.indexOf("春", 3)); // 6根据位置返回字符(重点)
| 方法 | 说明 | 使用 |
|---|---|---|
charAt(index) | 返回指定位置的字符 | str.charAt(0) |
charCodeAt(index) | 返回指定位置字符的 ASCII 码 | str.charCodeAt(0) |
str[index] | 获取指定位置字符(H5 新增) | str[0] |
字符串操作方法(重点)
| 方法 | 说明 |
|---|---|
concat(str1, str2, ...) | 拼接字符串,等效于 + |
substr(start, length) | 从 start 开始截取 length 个字符 |
slice(begin, end) | 截取从 begin 到 end(不含)的字符 |
substring(begin, end) | 截取从 begin 到 end 的字符(不接受负值) |
replace() 方法
在字符串中用一些字符替代另一些字符
replace(被替换的字符, 要替换为的字符);应用于敏感词汇替换:
var str1 = "abcoeffooeab";
while (str1.indexOf("o") !== -1) {
str1 = str1.replace("o", "*");
}
console.log(str1);split() 方法
用于切分字符串,可以把字符串切分为数组,切分完毕后,返回的是一个新的数组
var str = "a,b,c,d";
console.log(str.split(","));
// 返回的是一个数组 [a, b, c, d]大小写转换
// toUpperCase // 转化大写
// toLowerCase // 转化小写4.2.5.1 replace 和 replaceAll 方法
它们的第一个参数都使用 'pattern',可以是一个 string 或者 regex,replaceAll 强制第一个参数为正则表达式时使用全局标注 /g,否则抛出类型错误。replace(/something/g, 'replacement') 可以起到和 replaceAll 一样的效果。
4.2.5.2 at 方法
at(index),index 从 0 开始正数,支持负数,从末尾开始数起。
要获取字符串倒数第二个字符,可以用这个方法:
str.at(-2);除此之外,还可以使用:
let char = str[str.length - 2];
// 或
let char = str.slice(-2, -1);4.3 原型对象
4.3.1 构造函数和原型的 this 指向
构造函数中的 this 指向实例对象
原型对象中的 this 也指向实例对象
4.3.2 constructor 属性
每个原型对象中都有一个 constructor 属性,指向关联的构造函数
如果通过对象赋值的形式修改了原型对象,需要手动使用 constructor 指回原来的构造函数
function Star(name, age) {
this.name = name;
this.age = age;
}
Star.prototype = {
constructor: Star, // 手动指回 Star
sing: function () {
console.log("唱歌");
},
};4.3.3 对象原型
每个实例对象都有一个 __proto__ 属性,指向其构造函数的原型对象
var star = new Star("pink", 18);
console.log(star.__proto__ === Star.prototype); // true4.3.4 原型继承
通过原型链实现继承
function Father(name) {
this.name = name;
}
Father.prototype.sayHi = function () {
console.log("Hello, I am " + this.name);
};
function Son(name, age) {
Father.call(this, name);
this.age = age;
}
// 设置 Son 的原型为 Father 的实例
Son.prototype = new Father();
Son.prototype.constructor = Son;4.3.5 原型链
- 只要是对象,就有对象原型
__proto__ - 原型对象也是对象
- 只要是原型对象,就有 constructor,指向创造我的构造函数
原型链是 JS 实现继承的重要机制,通过原型链可以实现对象之间的属性和方法的共享。
5. DOM
页面文档对象模型,是 W3C 组织推荐的处理可拓展标记语言(HTML 和 XML)的标准编程接口。
通过 DOM 接口可以改变网页的内容,结构和样式。
5.1 DOM 树
- 文档:一个页面就是一个文档,DOM 中用
document表示 - 元素:页面中所有标签都是元素,DOM 中使用
element表示 - 节点:网页中所有内容都是节点(标签,属性,文本,注释等),DOM 中用
node表示
DOM 把以上内容都看作是对象
5.2 获取元素
| 方法 | 描述 | 案例 |
|---|---|---|
document.getElementById('id') | 获取对应唯一 ID 的元素对象,返回一个获取的元素对象 | var element = document.getElementById(id); |
document.getElementsByTagName() | 根据标签名返回元素对象,返回的是存放所有标签的一个伪数组 | var element = document.getElementsByTagName('标签名'); |
document.getElementsByClassName() | 根据类名返回存放所有标签的伪数组(H5 新增) | var element = document.getElementsByClassName('类名') |
document.querySelector('选择器') | 通过选择器,返回指定选择器的第一个元素对象(H5 新增),类选择器名前要加 .,ID 选择器前要加 # | var firstBox = document.querySelector('.box'); |
document.querySelectorAll('选择器') | 根据指定选择器返回所有集合(H5 新增) | var boxs = document.querySelectorAll('.box') |
使用
console.dir()可以打印我们获取的元素对象,更好地查看对象里面的属性和方法。根据标签名获取,还可以获取某个父元素内部的所有指定标签名的子元素。
// 例如 ol 里面有四个 li
var ol = document.getElementById("ol");
console.log(ol.getElementsByTagName("li"));5.3 获取特殊元素
5.3.1 获取 body 元素
var bodyEle = document.body;5.3.2 获取 html 元素
var htmlEle = document.documentElement;5.4 事件基础
事件是可以被 JS 侦测到的行为,触发 -- 响应机制,网页中的每个元素都可以产生某些可以触发 JS 的事件。
例如,用户点击按钮产生一个事件,然后执行某些操作。
5.4.1 事件三要素
- 事件源:事件被触发的对象(谁)如:按钮
- 事件类型:怎么触发,什么事件,比如鼠标点击(干了什么)
- 事件处理程序:通过函数(怎么办)要做什么
var btn = document.getElementById("btn"); // 获取事件源(元素对象)
btn.onclick = function () {
// 给事件类型附上触发函数
alert("按钮被点击");
};5.4.2 执行事件的步骤
- 获取事件源
var div = document.querySelector("div");- 注册事件(绑定事件)
div.onclick = ...- 添加事件处理程序(采取函数赋值形式)
div.onclick = function () {};5.4.3 常见的鼠标点击事件
| 鼠标事件 | 触发条件 |
|---|---|
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
5.5 操作元素
5.5.1 改变元素内容
element.innerText;
// 例如:点击按钮改变文字
// 1. 获取元素
var btn = document.querySelector("btn");
var div = document.querySelector("div");
// 2. 注册事件
btn.onclick = function () {
div.innerText = "2019-6-6";
};从起始位置到终止位置的内容,但它不识别 HTML 标签,同时空格和换行也会去掉(不常用)
element.innerHTML;
// 例如:
// 1. 获取元素
var btn = document.querySelector("btn");
var div = document.querySelector("div");
// 2. 注册事件
btn.onclick = function () {
div.innerHTML = "<strong>2019-6-6</strong>";
};起始位置到终止位置的全部内容,识别 HTML 标签,同时保留空格和换行(W3C 推荐)
5.5.2 常用元素的属性操作
src/href/id/alt/title
var ldh = document.getElementById("ldh");
var zxy = document.getElementById("zxy");
var img = document.getElementById("img");
zxy.onclick = function () {
img.src = "images/zxy.jpg";
};总结:元素对象.属性 = 值
5.5.3 表单元素的属性操作
type、value、checked、selected、disabled
显示和隐藏密码案例:
<input type="password" id="pwd" />
<button id="btn">显示密码</button>
<script>
var pwd = document.getElementById("pwd");
var btn = document.getElementById("btn");
var flag = 0;
btn.onclick = function () {
if (flag === 0) {
pwd.type = "text";
flag = 1;
btn.innerText = "隐藏密码";
} else {
pwd.type = "password";
flag = 0;
btn.innerText = "显示密码";
}
};
</script>5.5.4 样式属性操作
1.element.style.样式名(行内样式操作,样式少时使用)
var div = document.querySelector("div");
div.onclick = function () {
this.style.backgroundColor = "purple";
this.style.width = "200px";
};2.element.className(类名样式操作,样式复杂时使用)
var div = document.querySelector("div");
div.onclick = function () {
this.className = "change";
// 把类名改成 change,之后只要在 css 里添加 change 类名对应的样式即可
};注意:
- JS 里的样式采取驼峰命名方法,比如
fontSize、backgroundColor - 修改 style 样式的操作产生的是行内样式,css 权重高
- 修改样式多时可以用修改类名的方式来更改元素样式
className会直接更改元素的类名,会覆盖原先的类名
显示和隐藏文本框内容案例:
var text = document.querySelector("input");
text.onfocus = function () {
if (text.value === "手机") {
text.value = "";
this.style.color = "gray";
}
};
text.onblur = function () {
if (text.value === "") {
text.value = "手机";
this.style.color = "#fff";
}
};5.5.5 排他思想
若有同一组元素,想要某一个元素实现某种样式,需要用到循环的排他思想:
- 所有元素全部清楚样式(干掉其他人)
- 给当前元素设置样式(留下我自己)
- 注意顺序不能颠倒
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
var btns = document.getElementsByTagName("button");
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
// 1. 把所有按钮背景色还原
for (var i = 0; i < btns.length; i++) {
btns[i].style.backgroundColor = "";
}
// 2. 把当前按钮变成背景色
this.style.backgroundColor = "pink";
};
}
</script>
</body>表单全选/取消全选案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style>
thead tr {
background-color: skyblue;
}
</style>
</head>
<body>
<table cellspacing="0px">
<thead>
<tr>
<td><input type="checkbox" id="All_c" /></td>
<td>姓名</td>
<td>性别</td>
<td>年龄</td>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" id="c" /></td>
<td>小明</td>
<td>男</td>
<td>18</td>
</tr>
<tr>
<td><input type="checkbox" id="c" /></td>
<td>小芳</td>
<td>女</td>
<td>17</td>
</tr>
</tbody>
</table>
<script>
// 全选或者取消全选的方法
var a_cbox = document.getElementById("All_c");
var cbox = document.querySelector("tbody").querySelectorAll("input");
a_cbox.onclick = function () {
for (var i = 0; i < cbox.length; i++) {
cbox[i].checked = this.checked;
}
};
// 子按钮状态影响全选框状态
for (var i = 0; i < cbox.length; i++) {
cbox[i].onclick = function () {
if (!this.checked) {
a_cbox.checked = false;
} else {
var flag = 1;
for (var i = 0; i < cbox.length; i++) {
if (cbox[i].checked == false) {
flag = 0;
break;
}
}
if (flag) {
a_cbox.checked = true;
}
}
};
}
</script>
</body>
</html>5.5.6 自定义属性的操作
1.获取元素的属性值
element.getAttribute('属性'):用于获得自定义属性(程序员自定义的属性)element.属性值:获取元素本身自带属性(内置的)
2.设置属性值
element.setAttribute('属性', '值'):设置自定义属性element.属性值 = '值':设置内置属性
3.移除属性
element.removeAttribute('属性')
tab 栏切换(重点案例):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Tab 切换</title>
<style>
.clearfix:before,
.clearfix:after {
content: "";
display: table;
}
.clearfix:after {
clear: both;
}
.clearfix {
*zoom: 1;
}
.tab_list li {
float: left;
list-style: none;
margin-left: 10px;
}
.current {
float: left;
list-style: none;
margin-left: 10px;
background-color: pink;
}
.item {
display: none;
}
</style>
</head>
<body>
<div class="tab">
<div class="tab_list clearfix">
<ul>
<li class="current">商品介绍</li>
<li>规格包装</li>
<li>售后保障</li>
<li>商品评价</li>
<li>手机社区</li>
</ul>
</div>
<div class="tab_con">
<div class="item">商品介绍模块</div>
<div class="item">规格包装模块</div>
<div class="item">售后保障模块</div>
<div class="item">商品评价模块</div>
<div class="item">手机社区模块</div>
</div>
</div>
<script>
// 1. 上面的模块选项,点击某一个,底色变红色,其余不变
var tab_list = document.querySelector(".tab_list");
var lis = tab_list.querySelectorAll("li");
var items = document.querySelector(".tab_con").querySelectorAll(".item");
items[0].style.display = "block";
for (var i = 0; i < lis.length; i++) {
// 设置索引号(自定义属性 index)
lis[i].setAttribute("index", i);
}
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
// 改变底色
for (var i = 0; i < lis.length; i++) {
lis[i].className = "";
}
this.className = "current";
// 改变内容
var index = this.getAttribute("index");
for (var i = 0; i < items.length; i++) {
items[i].style.display = "none";
}
items[index].style.display = "block";
};
}
</script>
</body>
</html>5.5.7 H5 自定义属性
目的:是为了保存数据并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
通过 getAttribute('属性') 获取
1.设置 H5 自定义属性
规定以 data 开头作为属性名并且赋值
<div data-index="1"></div>
<!-- 或者 -->
<div></div>
<script>
document.querySelector("div").setAttribute("data-index", 2);
</script>2.获取 H5 自定义属性
- 兼容性获取:
element.getAttribute('data-属性名') - H5 新增:
element.dataset.属性名 element.dataset['属性名']
这两个属性名不需要带 data-,且只有 IE11 才开始支持这两个方法
对于这样命名的自定义属性 data-list-name
必须采用驼峰命名法获取,也就是 element.dataset.listName
5.6 节点操作
5.6.1 节点概述
获取元素通常有两种方式:
- 利用 DOM 提供的方法获取,逻辑不强且繁琐
- 利用节点层级关系比如父子兄节点获取元素,逻辑性强但是兼容性差
网页中所有内容都是节点(标签,属性等),DOM 树中所有节点均可通过 JS 进行访问,可被修改,创建或者删除。
一般来说,节点至少拥有 nodeType(节点类型)、nodeName(节点名称)和 nodeValue(节点值)三个基本属性。
- 元素节点
nodeType为 1 - 属性节点
nodeType为 2 - 文本节点
nodeType为 3(包括文字,空格,换行等)
实际开发中,节点操作主要操作元素节点。
5.6.2 节点层级
常见的为父子兄节点关系
1.父节点 node.parentNode
返回某节点的最近的父节点,若找不到则返回 null
var a = document.querySelector(".son"); // 获取节点
console.log(a.parentNode); // 获取并打印最近父节点2.子节点
node.childNodes
返回指定节点的所有子节点(不仅仅是元素节点,包括了文本节点等)即时更新
var a = document.querySelector(".father");
console.log(a.childNodes); // 获取并打印所有子节点
for (var i = 0; i < ul.childNodes.length; i++) {
// 通过 for 循环通过 nodeType 来过滤出子节点中的元素节点
if (ul.childNodes[i].nodeType == 1) {
console.log(ul.childNodes[i]);
}
}由于想要获取里面的元素节点,则需要专门处理,所以我们一般不提倡使用 childNodes
parentNode.children(重点)
返回所有子元素节点,虽然它是非标准的获取方式,但是浏览器都支持,所以可以大量使用这种方式,也是实际开发常用的
console.log(ul.children);parentNode.firstChild / parentNode.lastChild
获取第一个子节点 / 最后一个子节点(不管是文本还是元素节点等),找不到则返回 null
parentNode.firstElementChild / lastElementChild
获取第一个 / 最后子元素节点,找不到则返回 null
注意这两种方法有兼容性问题,ie9 以上才支持
实际开发中获得第一个子元素节点或者最后一个子元素节点的办法:
通过 parentNode.children[0] 获取第一个子元素节点
通过 parentNode.children[node.children.length-1] 获取最后一个元素节点
下拉菜单案例
通过父子节点关系实现下拉菜单的展开收起。
3.兄弟节点
node.nextSibling:获取下一个兄弟节点(包括文本节点,元素节点等)node.previousSibling:获取上一个兄弟节点(包括文本节点,元素节点等)node.nextElementSibling:获取下一个元素节点(ie9 以上才支持)node.previousElementSibling:获取上一个元素节点(ie9 以上才支持)
如何避免兼容性问题?自己封装函数:
function getNextElementSibling(element) {
var el = element;
while ((el = el.nextSibling)) {
if (el.nodeType === 1) {
return el;
}
}
return null;
}5.6.3 创建节点
var li = document.createElement("li");这些元素原先不存在,是根据我们的需求动态生成,也称为动态创建元素节点。
5.6.4 添加节点
node.appendChild(child):添加到节点的最后子节点末尾
var ul = document.querySelector("ul");
ul.appendChild(li);node.insertBefore(child, 指定元素):将一个节点添加到父节点的指定子节点前面
var li = document.createElement("li");
ul.insertBefore(li, ul.children[0]);简单版发布留言案例:
通过 createElement 创建新元素,添加到 ul 末尾,并清空输入框。
5.6.5 删除节点
node.removeChild(child):删除子节点
// 例:ul.removeChild(ul.children[0]); 删除 ul 中第一个子元素节点5.6.6 复制节点
node.cloneNode():返回调用该方法的节点的一个副本- 若括号里为空或者
false则为浅拷贝,即只复制标签不复制内容 - 若为
true则为深拷贝,复制标签和内容
var ul = document.querySelector("ul");
var ul_copy = ul.children[0].cloneNode(true);
ul.appendChild(ul_copy);5.6.7 替换节点
parentNode.replaceChild(newChild, oldChild);
用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。
5.6.8 总结
DOM 节点操作的常用方法:
- 创建:
createElement - 添加:
appendChild、insertBefore - 删除:
removeChild - 替换:
replaceChild - 复制:
cloneNode
5.7 DOM 重点核心
对于 DOM 操作,我们主要针对子元素的操作,主要有:
创建,增,删,改,查,属性操作,时间操作
1.创建
document.write、innerHTML、createElement
2.增
appendChild、insertBefore
3.删
removeChild
4.改
主要修改 DOM 的元素属性,DOM 元素的内容、属性、表单的值等
- 修改元素属性:
src、href、title等 - 修改普通元素内容:
innerHTML、innerText - 修改表单元素:
value、type、disabled - 修改元素样式:
style、className
5.查
主要获取查询 DOM 的元素
- DOM 提供的 API 方法:
getElementById、getElementsByTagName(古老用法,不推荐) - H5 提供的新方法:
querySelector、querySelectorAll(提倡) - 利用节点操作获取元素:父(
parentNode)、子(children)、兄(previousElementSibling、nextElementSibling)(提倡)
6.属性操作
主要针对于自定义属性
setAttribute:设置 DOM 的属性值getAttribute:得到 DOM 的属性值removeAttribute:移除属性
7.事件操作
事件源.事件类型 = 事件处理程序
5.8 正则表达式
是用来匹配字符串中字符组合的模式
是对象
通常用来查找,替换那些符合正则表达式的文本
使用场景:
表单验证(匹配),过滤敏感词(替换),从字符串中(提取)
语法
1,定义正则表达式语法:
const 变量名 = /表达式/;// 是正则表达式的字面量
2.判断是否有符合规则的字符串
test() 方法用来查看正则表达式和指定的字符串是否匹配
regObj.test(被检测的字符串);3.检索(查找)符合规则的字符串
regObj.exec(被检测的字符串);元字符
是一些具有特殊含义的字符
举例:
- 普通写法:26 个英文字母 abcde...
- 元字符:
[a-z]
1.边界符
用来提示字符所在的位置,只要有两个字符:
| 符号 | 说明 |
|---|---|
^ | 表示匹配行首的文本 |
$ | 表示匹配行尾的文本 |
^ 和 $ 一起,表示精确匹配
2.量词
用来设定某个模式出现的次数
重复的是离得最近的那个
*重复零次或更多次+重复一次或更多次?重复零次或一次{n}重复 n 次{n,}重复 n 次或更多次{n,m}重复 n 到 m 次
+ 等于 {1,},* 等于 {0,},? 等于 {0,1}
3.字符类
[abc]匹配任何包含的字符[^abc]匹配任何不包含的字符[a-z]匹配 a-z 范围内的字符[A-Za-z0-9_]匹配字母数字下划线
4.预定义类
| 预定义类 | 说明 |
|---|---|
\d | 匹配数字,相当于 [0-9] |
\D | 匹配非数字 |
\w | 匹配字母数字下划线 |
\W | 匹配非字母数字下划线 |
\s | 匹配空白字符 |
\S | 匹配非空白字符 |
修饰符
| 修饰符 | 说明 |
|---|---|
i | ignore - 忽略大小写 |
g | global - 全局匹配 |
m | multiline - 多行匹配 |
// 替换字符串中所有的指定字符
var str = "abcoefoxyozzopp";
str.replace(/o/g, "*"); // "abc*ef*xy*zz*pp"案例 - 过滤敏感词
// 替换字符串中所有的指定敏感词
var str = "abcoefoxyozzopp";
str.replace(/o/g, "*"); // "abc*ef*xy*zz*pp"6. 事件高级
6.1 注册事件(绑定事件)
注册事件有两种方式:传统方式和方法监听注册方式
6.1.1 addEventListener 事件监听方式
eventTarget.addEventListener(type, listener[, userCapture]);- 该方法将指定的监听器注册到目标对象 eventTarget 上
- 当该对象触发指定的事件时,就会执行事件处理函数。
该方法接受三个参数:
type:事件类型字符串,比如click、mouseover,这里不用带onlistener:事件处理函数,事件发生时,会调用该监听函数useCapture:可选参数,是一个布尔值,默认false
var btns = document.querySelectorAll("button");
// 1. 传统方式注册事件
btns[0].onclick = function () {
alert("hi");
};
// 2. 事件监听注册事件 addEventListener
btns[1].addEventListener("click", function () {
alert(22);
});6.1.2 attachEvent 方式(了解)
eventTarget.attachEvent(eventNameWithOn, callback);指定监听器注册到 eventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行
参数:
eventNameWithOn:事件类型字符串,比如onclick、onmouseover,这里要带 oncallback:事件处理函数,当目标触发事件时回调函数被调用
注册事件兼容性处理方案:
function addEventListener(element, eventName, fn) {
if (element.addEventListener) {
element.addEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.attachEvent) {
element.attachEvent("on" + eventName, fn);
} else {
// 相当于 element.onclick = fn;
element["on" + eventName] = fn;
}
}6.2 删除事件(解绑事件)
6.2.1 removeEventListener 方法
eventTarget.removeEventListener(type, listener[, useCapture]);divs[1].addEventListener("click", fn); // 注册事件函数
function fn() {
alert(22);
divs[1].removeEventListener("click", fn); // 移除fn事件函数
}
// 这里的fn不需要调用加小括号6.2.2 detachEvent 删除事件方式
eventTarget.detachEvent(eventNameWithOn, callback);参数:
eventNameWithOn:事件类型字符串,比如onclick、onmouseover,这里要带oncallback:事件处理函数
ie9 以前的版本支持
6.2.3 传统事件删除方式
eventTarget.onclick = null;6.2.4 删除事件兼容性处理方案
function removeEventListener(element, eventName, fn) {
if (element.removeEventListener) {
element.removeEventListener(eventName, fn);
} else if (element.detachEvent) {
element.detachEvent("on" + eventName, fn);
} else {
element["on" + eventName] = null;
}
}6.3 DOM 事件流
事件流描述的是页面中接收事件的顺序
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流
- 事件冒泡:IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到 DOM 最顶层节点的过程。
- 事件捕获:网景最早提出,由 DOM 最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程。
DOM 事件流分为 3 个阶段:
捕获阶段
当前目标阶段
冒泡阶段
JS 代码只能执行捕获或者冒泡中的一个阶段
onclick和attachEvent只能执行冒泡阶段addEventListener(type, listener[, useCapture])第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false(默认),表示在事件冒泡阶段调用事件处理程序实际开发一般用事件冒泡
有些事件没有冒泡,比如
onblur、onfocus、onmouseenter、onmouseleave
6.4 事件对象
eventTarget.onclick = function (event) {};
eventTarget.addEventListener("click", function (event) {});
// 这个 event 就是事件对象,常写成 e 或者 evt
// event 是个形参,系统帮我们设定为事件对象,不需要传递实参进去。
// 当注册事件时,event 对象会被系统自动创建,并依次传递给事件监听器。事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象 event,它有很多属性和方法。
比如:
- 谁绑定了这个事件
- 鼠标触发事件,会得到鼠标的相关信息,比如位置
- 键盘触发事件,会得到键盘的相关信息,比如按了哪个键
解决兼容性问题:
var div = document.querySelector("div");
div.onclick = function (e) {
console.log(e);
console.log(window.event);
e = e || window.event;
};事件对象常见的属性和方法:
| 属性/方法 | 说明 |
|---|---|
e.target | 返回触发事件的对象(标准) |
e.srcElement | 返回触发事件的对象(非标准,ie6-8 使用) |
e.type | 返回事件的类型,比如 click,不带 on |
e.cancelBubble | 该属性阻止冒泡(ie6-8 使用) |
e.returnValue | 该属性阻止默认事件(ie6-8 使用),比如不让链接跳转 |
e.preventDefault() | 该方法阻止默认事件(标准),比如不让链接跳转 |
e.stopPropagation() | 阻止冒泡(标准) |
注:e.target 和 this 的区别
- this 是事件绑定的元素,这个函数的调用者(绑定这个事件的元素)
- e.target 是事件触发的元素
阻止默认事件:
<body>
<div>123</div>
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
<input type="submit" value="提交" name="sub" />
</form>
<script>
// 常见事件对象的属性和方法
// 1. 返回事件类型
var div = document.querySelector("div");
div.addEventListener("click", fn);
div.addEventListener("mouseover", fn);
div.addEventListener("mouseout", fn);
function fn(e) {
console.log(e.type);
}
// 2. 阻止默认行为(事件)让链接不跳转 或者让提交按钮不提交
var a = document.querySelector("a");
a.addEventListener("click", function (e) {
e.preventDefault(); // dom 标准写法
});
// 3. 传统的注册方式
a.onclick = function (e) {
// 普通浏览器 e.preventDefault(); 方法
// 低版本浏览器 ie678 returnValue 属性
// 我们可以利用 return false 也能阻止默认行为 没有兼容性问题
return false;
alert(11);
};
</script>
</body>6.5 阻止事件冒泡
开始时由最具体的元素接收,然后逐级向上传播到 DOM 最顶层节点。
阻止事件冒泡标准写法:
利用事件对象里的 stopPropagation() 方法
e.stopPropagation();非标准:
利用事件对象 cancelBubble 属性:
e.cancelBubble = true;兼容性解决方案:
if (e && e.stopPropagation) {
e.stopPropagation();
} else {
window.event.cancelBubble = true;
}6.6 事件委托(代理,委派)
不是每个子节点都单独设置事件监听器,而是把事件监听器设置在父节点上,然后利用冒泡原理影响设置每个子节点。
只操作一次,可以提高 DOM 的性能。
<body>
<ul>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
</ul>
<script>
// 事件委托的核心原理:给父节点添加侦听器,利用事件冒泡影响每一个子节点
var ul = document.querySelector("ul");
ul.addEventListener("click", function (e) {
e.target.style.backgroundColor = "pink";
});
</script>
</body>6.7 常用的鼠标事件
| 鼠标事件 | 触发条件 |
|---|---|
click | 鼠标点击左键触发 |
dblclick | 鼠标双击触发 |
mouseover | 鼠标经过触发 |
mouseout | 鼠标离开触发 |
mouseenter | 鼠标进入触发(不冒泡) |
mouseleave | 鼠标离开触发(不冒泡) |
mousemove | 鼠标移动触发 |
mousedown | 鼠标按下触发 |
mouseup | 鼠标弹起触发 |
contextmenu | 鼠标右键菜单 |
6.7.1 禁止鼠标右键菜单
contextmenu 主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单
document.addEventListener("contextmenu", function (e) {
e.preventDefault();
});6.7.2 禁止鼠标选中(selectstart 开始选择)
document.addEventListener("selectstart", function (e) {
e.preventDefault();
});6.7.3 获取鼠标在页面的坐标
document.addEventListener("click", function (e) {
// 1. client 鼠标在可视区的 x 和 y 坐标
console.log(e.clientX);
console.log(e.clientY);
// 2. page 鼠标在页面文档的 x 和 y 坐标
console.log(e.pageX);
console.log(e.pageY);
// 3. screen 鼠标在电脑屏幕的 x 和 y 坐标
console.log(e.screenX);
console.log(e.screenY);
});6.8 常用的键盘事件
| 键盘事件 | 触发条件 |
|---|---|
onkeyup | 某个键盘按键被松开时触发 |
onkeydown | 某个键盘按键被按下时触发 |
onkeypress | 某个键盘按键被按下时触发,但是它不识别功能键,比如 ctrl shift 箭头等 |
- 如果使用
addEventListener不需要加on onkeypress和前面 2 个的区别是,它不识别功能键,比如左右箭头,shift 等- 三个事件的执行顺序是:
keydown–keypress–keyup
键盘对象属性:keyCode
返回该键值的 ASCII 值
onkeydown和onkeyup不区分字母大小写,onkeypress区分字母大小写- 在我们实际开发中,我们更多的使用
keydown和keyup,它们能识别所有的键(包括功能键) keypress不识别功能键,但是keyCode属性能区分大小写,返回不同的 ASCII 值
document.addEventListener("keyup", function (e) {
console.log("up:" + e.keyCode);
if (e.keyCode === 65) {
alert("您按下的a键");
}
});7. BOM
BOM 即浏览器对象模型,提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。
BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。
BOM 包含了 DOM。
7.1 BOM 的构成
window 对象是浏览器的顶级对象,具有双重角色
- 是 JS 访问浏览器窗口的一个接口
- 是一个全局对象,定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法。在调用时可以省略 window,前面学习的对话框属于 window 对象方法,如
alert()、prompt()等其完整写法是window.alert()。
注意:window 下有个特殊属性 window.name,所以不要声明 name 变量
7.2 window 对象的常见事件
7.2.1 窗口加载事件
window.onload = function () {};
// 或者
window.addEventListener("load", function () {});window.onload 是窗口加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、css 文件等),就调用的处理函数
一般需要标签在 js 段之上才能触发事件,因为 html 是从上往下执行,而加上 window.onload = function(){},将 js 代码写入 {} 内就可以将 js 放置在任何地方,因为它会等待页面全部加载完毕才会执行该 js
- 有了
window.onload就可以把 js 代码写到页面元素的上方 window.onload传统注册事件方式只能写一次,如果有多个,会以最后一个window.onload为准window.addEventListener('load', function(){})可以写任意次,推荐用这种方法。
document.addEventListener("DOMContentLoaded", function () {});DOMContentLoaded 事件触发时,仅当 DOM 加载完成,不包括样式表、图片、flash 等等
ie9 以上才支持
如果页面图片很多的话,从用户访问到 onload 触发可能需要较长的时间,交互效果就会延迟实现,必然影响用户的体验,此时用 DOMContentLoaded 事件比较合适
7.2.2 调整窗口大小事件
window.onresize = function () {};
// 或者
window.addEventListener("resize", function () {});window.onresize 是调整窗口大小加载事件,当触发时就调用的处理函数。
注意:
- 只要窗口尺寸变化就会触发这个事件
window.innerWidth可以获取当前屏幕的宽度。我们经常利用这个事件完成响应式布局。
7.2.3 定时器
setTimeout() 定时器
window.setTimeout(调用函数, [延迟的毫秒数]);setTimeout() 方法用于设置一个定时器,该定时器在定时器到期后执行调用函数。
window可以省略- 调用函数可以直接写函数,或者写函数名
- 延迟的毫秒数省略默认是 0,如果写,必须是毫秒
- 定时器有很多,可给经常使用的定时器赋值一个标识符(timeoutID),给定时器取名字,以作区分
setTimeout() 这个调用函数也称为回调函数 callback,意思就是回头调用函数,上一件事情干完(时间或者条件到了)再回头调用这个函数
而 element.onclick = function(){} 或者 element.addEventListener('click', fn); 里面的函数也是回调函数
停止 setTimeout() 定时器
window.clearTimeout(timeoutID);取消了先前通过调用 setTimeout() 建立的定时器。
注意:
window可省略- 里面的参数就是定时器的标识符(变量)
setInterval() 定时器
window.setInterval(调用函数, [间隔时间 ms]);重复调用一个函数,每隔相同的时间就调用一次回调函数。
停止 setInterval 定时器
window.clearInterval(timeoutID);取消了先前通过调用 setInterval() 建立的定时器
setTimeout 与 setInterval 的区别:
setTimeout方法用于在一定的延迟之后执行一次指定的函数。setInterval方法用于在一定的时间间隔内重复执行指定的函数
7.2.4 this
this 的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定 this 到底指的是谁,一般情况下 this 的最终指向是调用它的对象
- 全局作用域或者普通函数中
this指向的是window - 定时器里的
this指向window - 方法调用中谁调用
this就指向谁 - 构造函数中
this指向构造函数的实例
7.3 JS 执行队列
7.3.1 JS 是单线程
即同一时间只做一件事
所有任务都要排队,一个一个完成,所以如果 JS 执行时间过长,就会造成页面的渲染不连贯,导致页面渲染加载阻塞
7.3.2 同步和异步
同步:
前一个任务结束后再执行后一个任务,程序执行结果和排列顺序一致
异步:
在做一件事情的同时去处理其他事情,同一时间宏观上做多个任务
本质区别:这条流水线上各个流程的执行顺序不同
7.3.3 同步任务和异步任务
- 同步任务:在主线程上执行,形成一个执行栈
- 异步任务:JS 的异步通过回调函数实现
- 一般而言,异步任务有以下三种类型:
- 普通事件:
click、resize等 - 资源加载,如
load、error等 - 定时器,包括
setInterval等
- 普通事件:
异步任务相关回调函数会放入任务队列(消息队列)中
7.3.4 JS 执行机制
- 先执行执行栈中的同步任务
- 异步任务(回调函数)放入任务(消息)队列中
- 再执行下一个同步任务
- 一旦执行栈中所有同步任务完成,系统就会依次读取任务队列中的异步任务,被读取的异步任务结束等待状态,进入执行栈,开始执行。
事件循环:
主线程不断从任务队列中读取异步任务,这个过程是循环不断的,所以整个的这种运行机制又称为 Event Loop(事件循环)。
7.4 location 对象
location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL。因为这个属性返回的是一个对象,所以我们将这个属性称为 location 对象。
7.4.1 URL
统一资源定位符,每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器该怎么处理它。
7.4.2 location 对象的属性
| 属性 | 返回值 |
|---|---|
location.href | 获取或者设置(跳转)整个 URL |
location.host | 返回主机(域名),如 www.baidu.com |
location.port | 返回端口号,如果未写返回空字符串 |
location.pathname | 返回路径 |
location.search | 返回参数 |
location.hash | 返回片段,#后面内容,常见于链接锚点 |
重点记住:href 和 search
7.4.3 location 对象的方法
location.assign('URL'):跳转网页到指定 URL,会记录浏览器历史,可以后退页面,也称为重定向页面location.replace('URL'):替换网页,但不记录历史,所以无法后退location.reload():刷新页面,重新加载。参数为true则强制刷新
7.5 navigator 对象
包含浏览器的相关信息,有很多属性,常用 userAgent 可以返回由客户机发送服务器的 user-agent 头部的值
下面前端代码可以判断用户哪个终端打开页面,如果是用 PC 打开的,我们就跳转到 PC 端的页面,如果是用手机打开的,就跳转到手机端页面:
if (
navigator.userAgent.match(
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i,
)
) {
window.location.href = ""; // 手机
} else {
window.location.href = ""; // 电脑
}7.6 history 对象
与浏览器历史记录进行交互
包含用户访问过的 URL
| history 对象方法 | 作用 |
|---|---|
back() | 可以后退功能 |
forward() | 前进功能 |
go(参数) | 前进后退功能,参数如果是 1 前进 1 个页面,如果是 -1 后退 1 个页面 |
8. PC 端网页特效
8.1 元素偏移量 offset 系列
offset 翻译就是偏移量,使用 offset 相关属性可以动态的得到元素的位置、大小等
- 获得元素距离带有定位父元素的位置(父级必须带定位,否则以 body 为准)
- 获得元素自身的大小
- 返回的数值都不带单位
8.1.1 offset 常用属性
| 属性 | 作用 |
|---|---|
element.offsetParent | 返回作为该元素带有定位的父级元素,如果父级都没用定位则返回 body |
element.offsetTop | 返回元素相对带有定位父元素上方的偏移 |
element.offsetLeft | 返回元素相对带有定位父元素的左边框的偏移 |
element.offsetWidth | 返回自身包括padding、边框、内容区的宽度,返回数值不带单位 |
element.offsetHeight | 返回自身包括padding、边框、内容区的高度,返回数值不带单位 |
8.1.2 offset 和 style 的区别
| offset | style |
|---|---|
| 可以获取任意样式表中的样式值 | 只能获取行内样式表中的样式值 |
| 获取的值是数字,没有单位 | 获取的值带单位 |
| 获取的是布局相关属性(只读) | 获取的是渲染相关属性(可读写) |
| 不包含 padding | 包含 padding |
8.2 元素可视区 client 系列
client 翻译过来就是客户端,我们使用 client 的相关属性来获取元素可视区的相关信息。通过 client 系列的相关属性可以动态地得到元素的边框大小、元素大小
| 属性 | 作用 |
|---|---|
element.clientTop | 返回元素上边框的大小 |
element.clientLeft | 返回元素左边框的大小 |
element.clientWidth | 返回自身包括 padding、内容区的宽度,不含边框,返回数值不带单位 |
element.clientHeight | 返回自身包括 padding、内容区的高度,不含边框,返回数值不带单位 |
8.3 元素滚动 scroll 系列
scroll 翻译过来就是滚动的,可以获得元素的大小和滚动距离
| 属性 | 作用 |
|---|---|
element.scrollTop | 返回被卷去的上侧距离,返回数值不带单位 |
element.scrollLeft | 返回被卷去的左侧距离,返回数值不带单位 |
element.scrollWidth | 返回自身实际的宽度不含边框(包含内容超出容器的部分),返回数值不带单位 |
element.scrollHeight | 返回自身实际的高度(包含内容超出容器的部分),不含边框,返回数值不带单位 |
div.addEventListener("scroll", function () {
console.log(div.scrollTop);
});8.3.1 页面被卷去的头部
如果浏览器的高或宽不足以显示整个页面,会自动出现滚动条,当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部,滚动条在滚动时会触发 onscroll 事件。
// 兼容性写法
var scrollTop =
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;8.4 三大系列总结
offset经常用于获取元素位置offsetLeft、offsetTopclient经常用于获取元素大小clientWidth、clientHeightscroll经常用于获取滚动距离scrollTop、scrollLeft- 注意页面滚动的距离通过
window.pageXOffset获得
8.5 mouseenter 和 mouseover 区别
当鼠标移动到元素上就会触发
mouseover经过自身盒子和子盒子都会触发一次(会冒泡)mouseenter只有经过自身盒子才会触发,不会冒泡,和鼠标离开mouseleave搭配也不会冒泡
8.6 动画函数封装
核心原理:通过定时器 setInterval() 不断移动盒子位置
实现步骤:
- 获得盒子当前位置(盒子记得加定位)
- 让盒子在当前位置加上一个移动距离
- 利用定时器不断重复这个操作
- 加一个结束定时器的条件
- 注意此元素需要添加定位,才能使用
element.style.left - 注意需要两个参数:动画对象和移动距离
<script>
var div = document.querySelector('div');
function animate(obj, target) { // 封装
var timer = setInterval(function () {
if (obj.offsetLeft >= target) {
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 1 + 'px';
}, 30);
}
animate(div, 400); // 调用
</script>给不同元素记录不同定时器
给封装的对象创建一个属性值 timer 令其等于一个定时器
obj.timer = setInterval(function () {});代码优化:
<script>
var div = document.querySelector('div');
function animate(obj, target) { // 封装
clearInterval(obj.timer); // 先清除定时器防止一个对象的多个定时器同时运行
obj.timer = setInterval(function () {
if (obj.offsetLeft >= target) {
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 1 + 'px';
}, 30);
}
animate(div, 400); // 调用
</script>8.7 常见网页特效案例
8.7.1 缓动动画
缓动动画就是让元素运动速度有所变化,最常见的就是让速度慢慢停下来
思路:
- 让盒子每次移动距离变小
- 核心算法:步长 = (目标值 - 现在的位置) / 10 作为每次移动的距离
- 步长尽量取整数
- 盒子位置到目标位置就停止定时器
function animate(obj, target) {
// 目的是让元素只能有一个定时器,防止定时器效果累加
clearInterval(obj.timer);
obj.timer = setInterval(function () {
// 步长值写在定时器里面
// 把步长值改为整数,尽量避免小数的出现
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + step + "px";
}, 30);
}8.7.2 动画函数添加回调函数
function animate(obj, target, callback) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
// 如果有回调函数传进来
if (callback) {
callback();
}
}
obj.style.left = obj.offsetLeft + step + "px";
}, 30);
}8.7.3 网页轮播图
见二轮考核代码
9. 本地存储
特性:
- 数据存储在用户浏览器中
- 设置、读取方便、甚至刷新不丢失数据
- 容量大,
sessionStorage约 5M,localStorage约 20M - 只能存储字符串,可以将对象
JSON.stringify()编码后存储
9.1 sessionStorage
- 生命周期为关闭浏览器窗口消失
- 在同一窗口(页面)下数据可以共享
- 以键值对的形式存储使用
// 存储数据
sessionStorage.setItem("key", value);
// 获取数据
sessionStorage.getItem("key");
// 删除数据
sessionStorage.removeItem("Key");
// 删除所有数据
sessionStorage.clear();var ipt = document.querySelector("input");
var set = document.querySelector(".set");
set.addEventListener("click", function () {
var val = ipt.value;
sessionStorage.setItem("uname", val);
});9.2 localStorage
- 生命周期永久生效,除非手动删除否则关闭页面也存在
- 可以多窗口共享(同一浏览器共享)
- 以键值对形式使用
// 存储数据
localStorage.setItem("Key", value);
// 获取数据
localStorage.getItem("key");
// 删除数据
localStorage.removeItem("key");
// 删除所有数据
localStorage.clear();10. 附录:常见实用技巧
10.1 模板字符串
模板字符串(Template literals),是 ES6 中新增的一种字符串表示方法,使用反引号(`)包围,允许以更灵活和功能丰富的方式创建字符串。
const name = "Bob";
const age = 30;
const introduction = `你好,我叫${name},今年${age}岁`;模板字符串的主要特点:
- 多行字符串:模板字符串可以包含多行文本,不需要使用
+来连接字符串。 - 插值表达式:可以在模板字符串中直接嵌入变量或表达式,只需在
${}中添加 JavaScript 表达式即可。 - 表达式嵌入:
${}可以包含任意的 JavaScript 表达式,包括函数调用。 - 标签模板:模板字符串可以与函数一起使用,形成标签模板。
const text = `这是一个
多行字符串`;
const name = "Alice";
const greeting = `你好,${name}!`;
function highlight(strings, ...values) {
return strings.reduce((acc, str, i) => {
return `${acc}${str}<em>${values[i] || ""}</em>`;
}, "");
}10.2 JSON
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于 JavaScript 的一个子集,但是它是语言无关的。
JSON 数据格式:
- 对象:使用花括号
{}包围,包含一系列键值对 - 数组:使用方括号
[]包围,可以包含任意数量的值 - 字符串:使用双引号
"包围 - 数字:表示整数或浮点数
- 布尔值:
true或false - null:表示空值
// 解析 JSON
var jsonString = '{"name": "Alice", "age": 25}';
var obj = JSON.parse(jsonString);
// 序列化对象为 JSON
var obj = { name: "Alice", age: 25 };
var jsonString = JSON.stringify(obj);注意事项:
- JSON 格式要求使用双引号
"来包围键名和字符串 - JSON 格式不支持注释
10.3 classList
在 JavaScript 中,classList 属性返回一个 DOMTokenList 对象,它表示元素的类属性的集合。classList.add 方法用于添加一个类到元素的类属性中。如果类已经存在,那么它不会重复添加。
// 向元素添加单个类
var element = document.getElementById("myElement");
element.classList.add("new-class");
// 向元素添加多个类
element.classList.add("new-class", "another-class");
// 检查类是否存在
if (!element.classList.contains("existing-class")) {
element.classList.add("existing-class");
}
// 响应用户操作添加类
button.addEventListener("click", function () {
element.classList.add("active");
});注意事项:
- 如果尝试添加的类已经存在于元素上,
classList.add方法不会重复添加它 classList.add可以一次性添加多个类,只需传入多个参数即可
10.4 防抖(Debounce)
Debounce(防抖)是一种限制函数被频繁调用的技术,确保函数在指定的时间间隔后执行。这种技术通常用于处理诸如窗口调整大小、滚动等频繁触发的事件。
function debounce(func, wait) {
let timeout;
return function executedFunction() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}
// 使用示例
window.addEventListener(
"resize",
debounce(function (event) {
console.log("窗口尺寸改变:", event);
}, 250),
);10.5 存储对象到 localStorage
// 创建一个对象
var myObject = {
name: "Alice",
age: 25,
hobbies: ["reading", "gaming", "hiking"],
};
// 将对象转换为 JSON 字符串
var storedObject = JSON.stringify(myObject);
// 存储 JSON 字符串到 localStorage
localStorage.setItem("myKey", storedObject);10.6 for...in
let key in data 是一种使用 for...in 循环的语法,用于遍历一个对象的所有可枚举属性。
let data = {
name: "Alice",
age: 25,
country: "Wonderland",
};
for (let key in data) {
console.log(key + ": " + data[key]);
}注意事项:
for...in循环只会遍历对象自身的可枚举属性- 属性遍历的顺序是不确定的
for...in循环不会遍历 Symbol 类型的属性- 替代方法:
Object.keys()、Object.values()、Object.entries()
