文章目录
- JavaScript基础
- WebAPIs
- DOM
- BOM
JavaScript基础
1.了解JavaScript
- JavaScript是什么?
JavaScript 是一种运行在客户端(浏览器)的编程语言
- JavaScript组成是什么?
ECMAScript( 基础语法 )、web APIs (DOM、BOM)
2.js的书写顺序
1.内嵌式
内嵌式:直接在html页面中创建script标签来书写js代码
注意点:
建议把script放在页面尾部,能够正确获取页面标签
浏览器解析标签是按照自上而下的顺序,script放在底部能够提高页面加载效率
2.外链式
单独创建.js后缀文件来书写js代码,html通过script标签的src属性引入该文件
注意点
用于引入外联文件的script标签,中间不可以书写代码因为不会被执行
外部js文件更好管理和维护,是开发中主要使用的方式
3.行内式
行内式:在标签上直接写事件名和js代码
<div onclick="alert('你点我干嘛!')">点我看看</div>
3.JS的注释
- 单行注释
符号://
作用://右边这一行的代码会被忽略
快捷键:ctrl + /
- 块注释
符号:/* */
作用:在/* 和 */ 之间的所有内容都会被忽略
快捷键:shift + alt + A
4.JavaScript输入输出语法
1.输出
语法1:
document.write(‘要输出的内容’)
作用:向body内输出内容
注意:如果输出的内容写的是标签,也会被解析成网页元素
document.write('hello world')
document.write('<h1>我是h1标题</h1>')
语法2:
alert(’ ')
作用:页面弹出警告对话框
语法3:
console.log(’ ')
作用:控制台输出语法,程序员调试使用
2.输入
语法:
prompt(‘请输入您的姓名’)
作用:显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字
JavaScript 代码执行顺序:
按HTML文档流顺序执行JavaScript代码
alert() 和 prompt() 它们会跳过页面渲染先被执行(同步异步知识点)
5.字面量
目标:能说出什么是字面量
在计算机科学中,字面量(literal)是在计算机中描述 事/物
可以表示一个固定值的表示法
当给变量赋值时,等号右边都可以认为是字面量
比如:
我们工资是: 1000 此时 1000 就是 数字字面量
‘哈哈哈哈哈’ 字符串字面量
还有 [] 数组字面量 {} 对象字面量 true false等等
6.变量
目标:理解变量是计算机存储数据的“容器”
1.变量
白话:变量就是一个装东西的盒子。
通俗:变量是计算机中用来存储数据的“容器”,它可以让计算机变得有记忆。
注意:变量不是数据本身,它们仅仅是一个用于存储数值的容器。可以理解为是一个个用来装东西的纸箱子。
2.变量的基本使用
2-1 变量的声明
1. 声明变量:
要想使用变量,首先需要创建变量(也称为声明变量或者定义变量)
语法:
let 变量名
- 声明变量有两部分构成:声明关键字、变量名(标识)
- let 即关键字 (let: 允许、许可、让、要),所谓关键字是系统提供的专门用来声明(定义)变量的词语
举例:
我们声明了一个age变量
age 即变量的名称,也叫标识符
2-2 变量的赋值
1.定义了一个变量后,你就能够初始化它(赋值)。在变量名之后跟上一个“=”,然后是数值。
方式一:
// 1.声明变量
let age
// 2.使用赋值号(=)给变量赋值
age = 18
方式二:
// 声明的同时赋值
let age = 18
方式三:
//声明多个变量 了解
let year = 20, money = 50
console.log(year)
console.log(money)
注意:是通过变量名来获得变量里面的数据
1.变量赋值
简单点,也可以声明变量的时候直接完成赋值操作,这种操作也称为 变量初始化
2.变量更新
变量的含义:可变的量,标识符不变,值可以更改
变量的更新:
- 为一个已有值的变量重新赋值,即可更新变量
- 已声明的变量不能重复声明,否则会报错
注意: let 不允许多次声明一个变量
2-3 变量的本质
**内存:**计算机中存储数据的地方,相当于一个空间
**变量本质:**是程序在内存中申请的一块用来存放数据的小空间
2-4 变量命名规则与规范
1. 规则:
不能用关键字
关键字:有特殊含义的字符,JavaScript 内置的一些英语词汇。例如:let、var、if、for等
只能用下划线、字母、数字、$组成,且数字不能开头
字母严格区分大小写,如 Age 和 age 是不同的变量
2. 规范:
起名要有意义
遵守小驼峰命名法
第一个单词首字母小写,后面每个单词首字母大写。例:userName
3.拓展-变量let 和 变量var 的区别
- 在较旧的JavaScript,使用关键字 var 来声明变量 ,而不是 let。
- var 现在开发中一般不再使用它,只是我们可能再老版程序中看到它。
- let 为了解决 var 的一些问题。
var 声明:
- 可以先使用 在声明 (不合理)
- var 声明过的变量可以重复声明(不合理)
- 比如变量提升、全局变量、没有块级作用域等等
3-1 let和var的共同点
- 允许声明和赋值同时进行
- 允许同时声明多个变量并赋值
3-2 let和var的不同点
- let不允许重复声明,而var可以
- let遵循先声明后使用,而var可以先使用后声明(存在变量提升的问题)
- let拥有块级作用域、而var没有块级作用域(了解,后面讲解)
注意点:
大部分情况下使用‘let’和‘var’区别不大,但是‘let’是新版本中出现用于替换‘var’的,相较严谨,因此推荐使用
7.变量拓展-数组
数组 (Array) —— 一种将 一组数据存储在单个变量名下 的优雅方式
let arr = []
let 变量=数组字面量
1.数组的基本使用
目标:能够声明数组并且能够获取里面的数据
1. 声明语法
let 数组名 = [数据1, 数据2, ···, 数据n]
例
let names = ['晓明','小红','小兰','小刘']
- 数组是按顺序保存,所以每个数据都有自己的编号
- 计算机中的编号从0开始,所以小明的编号为0,小刚编号为1,以此类推
- 在数组中,数据的编号也叫索引或下标
- 数组可以存储任意类型的数据
consol.log(names[0])
2.使用:
数组名[索引]
扩展:
- 名词元素:数组中的每一项数据也称为数组的“元素”
- 数组长度:通过 数组名.length 方式可以获取到当前数组的长度(有多少个元素长度就有多少个)
8.常量
概念:使用 const 声明的变量称为“常量”。
使用场景:当某个变量永远不会改变的时候,就可以使用 const 来声明,而不是let。
命名规范:和变量一致
const PI = 3.14
注意:
- 常量不允许重新赋值
- 常量声明的时候必须赋值(初始化)
9.数据类型
计算机世界中的万事成物都是数据。
JS 数据类型整体分为两大类:
- 基本数据类型
- number 数值型
- string 字符串型
- boolean 布尔型
- underfined 未定义型
- null 空类型
- symbol??
- 引用数据类型
- array 数组
- function 函数
- Object 对象
计算机程序可以处理大量的数据,为了方便数据的管理,将数据分成了不同的类型:
注:通过 typeof 关键字检测数据类型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 数据类型</title>
</head>
<body>
<script>
// 检测 1 是什么类型数据,结果为 number
document.write(typeof 1)
</script>
</body>
</html>
1.基本数据类型
1.1 数字类型
即我们数学中学习到的数字,可以是整数、小数、正数、负数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 数据类型</title>
</head>
<body>
<script>
let score = 100 // 正整数
let price = 12.345 // 小数
let temperature = -40 // 负数
document.write(typeof score) // 结果为 number
document.write(typeof price) // 结果为 number
document.write(typeof temperature) // 结果为 number
</script>
</body>
</html>
JavaScript 中的数值类型与数学中的数字是一样的,分为正数、负数、小数等。
拓展-NaN
NaN:not a number 代表一个计算错误。他是一个不正确的或者一个未定义的数学操作所得到的结果
出现的场景:当数字和不带数字的字符串进行运算时会出现NaN,类似于乱码的意思
加号左右是数字或者字符串是拼接的效果
注意点:NaN与任何数运算都是NaN
let a = 10
let b = '今天天气真好'
console.log(a * b);//NaN
console.log(a / b);//NaN
console.log(a % b);//NaN
console.log(NaN + 1);//NaN
console.log(NaN / 1);//NaN
console.log('老师'-2) //NaN
console.log(NaN===NaN)//false
console.log('10'*10) //100 隐式转换
NaN是粘性的,任何对NaN的操作都会返回NaN
console.log(NaN + 2) //NaN
1.2 字符串类型
通过单引号( ''
) 、双引号( ""
)或反引号( ``)包裹的数据都叫字符串,单引号和双引号没有本质上的区别,推荐使用单引号。
注意事项:
- 无论单引号或是双引号必须成对使用
- 特殊情况下,字符串中需要显示引号,可以通过单双引号互相嵌套实现,单引号/双引号可以互相嵌套,但是不以自已嵌套自已
- 必要时可以使用转义符
\
,输出单引号或双引号
let str = '今天天气真好'
let str2 = "今天天气真好"
let str3 = `今天天气真好`
// 在字符串中显示引号
let str4 = "今天'天气'真好"
let str5 = '今天"天气"真好'
// 转义字符
let str6 = '今天\'天气\'真好'
let str7 = '今天\%天气\%真好'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 数据类型</title>
</head>
<body>
<script>
let user_name = '小明' // 使用单引号
let gender = "男" // 使用双引号
let str = '123' // 看上去是数字,但是用引号包裹了就成了字符串了
let str1 = '' // 这种情况叫空字符串
documeent.write(typeof user_name) // 结果为 string
documeent.write(typeof gender) // 结果为 string
documeent.write(typeof str) // 结果为 string
</script>
</body>
</html>
1.字符串拼接
字符串拼接:
场景: + 运算符 可以实现字符串的拼接。
如果加号两边有一个字符串,就会拼接
口诀:数字相加,字符相连
2.☆模板字符串(es6新语法)
用** ${变量名}
**来拼接字符串和变量
-
使用场景:
- 使用反引号包裹的数据
- 在模板字符串中可以使用${变量名}的方式插入变量
- 模板字符串可以换行编辑,而单引号的字符串不可以
-
语法:
**`` (反引号)**在英文输入模式下按键盘的tab键上方那个键(1左边那个键)
内容拼接变量时,用 ${ } 包住变量
document.write(`我的同学${name}今年${age}岁了`) document.write( ` <h2> <span>hello</span> </h2> ` )
1.3 布尔类型
表示肯定或否定时在计算机中对应的是布尔类型数据,它有两个固定的值 true
和 false
,表示肯定的数据用 true
,表示否定的数据用 false
。
- 代表空或者否定的值会被转换为false,比如:空字符串’ ’ 、0、NaN、null、undefined
- 其余值都会被转为true
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 数据类型</title>
</head>
<body>
<script>
// pink老师帅不帅?回答 是 或 否
let isCool = true // 是的,摔死了!
isCool = false // 不,套马杆的汉子!
document.write(typeof isCool) // 结果为 boolean
</script>
</body>
</html>
1.4 undefined
未定义是比较特殊的类型,只有一个值 undefined,只声明变量,不赋值的情况下,变量的默认值为 undefined,一般很少【直接】为某个变量赋值为 undefined。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 数据类型</title>
</head>
<body>
<script>
// 只声明了变量,并末赋值
let tmp;
document.write(typeof tmp) // 结果为 undefined
</script>
</body>
</html>
注:JavaScript 中变量的值决定了变量的数据类型。
1.5 空类型
JavaScript 中的 null 仅仅是一个代表“无”、“空”或“值未知”的特殊值
typeOf(null)是object
null 开发中的使用场景:
官方解释:把 null 作为尚未创建的对象
大白话: 将来有个变量里面存放的是一个对象,但是对象还没创建好,可以先给个null
1.6 null 和 undefined 区别:
-
undefined表示声明过, 但是没有赋值
-
null表示一个变量被认为的设置空对象, 而不是没有定义的原始状态,赋值了, 但是值为空
-
计算有区别
console.log(underfined + 1); //NaN console.log(null + 1); //1
2.通过 typeof关键字检测数据类型
typeof 运算符可以返回被检测的数据类型。它支持两种语法形式:
- 作为运算符: typeof x (常用的写法)
- 函数形式: typeof(x)
typeof null ===> object
换言之,有括号和没有括号,得到的结果是一样的,所以我们直接使用运算符的写法
let name='xiaoming'
let age = 28
let wifi
let pot = null
console.log(typeof (name)); //string
console.log(typeof age); //number
console.log(typeof wifi); //undefined
console.log(typeof pot); //object null本身就是一个对象
10.类型转换
理解弱类型语言的特征,掌握显式类型转换的方法
在 JavaScript 中数据被分成了不同的类型,如数值、字符串、布尔值、undefined,在实际编程的过程中,不同数据类型之间存在着转换的关系。
prompt 表单中输入的数字 输出其实都是字符串
1.隐式转换
某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换。
- 运算符+号只要遇到字符串,则会自动把另一个相加的也转换为字符串,变成字符串拼接效果
- 运算符除了+号,-、 *、 /、 %会自动将两边转换为数字类型,再进行运算
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 隐式转换</title>
</head>
<body>
<script>
let num = 13 // 数值
let num2 = '2' // 字符串
// 结果为 132
// 原因是将数值 num 转换成了字符串,相当于 '13'
// 然后 + 将两个字符串拼接到了一起
console.log(num + num2)
// 结果为 11
// 原因是将字符串 num2 转换成了数值,相当于 2
// 然后数值 13 减去 数值 2
console.log(num - num2)
let a = prompt('请输入一个数字')
let b = prompt('请再输入一个数字')
alert(a + b);
let info = prompt('请输入数字')
console.log(typeof info); //string
let info2 = +prompt('请输入数字')
console.log(typeof info2); //number
</script>
</body>
</html>
小技巧:
+号作为正号解析可以转换成数字型
任何数据和字符串相加结果都是字符串
注:数据类型的隐式转换是 JavaScript 的特征,后续学习中还会遇到,目前先需要理解什么是隐式转换。
补充介绍模板字符串的拼接的使用
0、空字符串、null、undefined等 触发隐式转换 结果为false
2.显式转换
编写程序时过度依靠系统内部的隐式转换是不严禁的,因为隐式转换规律并不清晰,大多是靠经验总结的规律。为了避免因隐式转换带来的问题,通常根逻辑需要对数据进行显示转换。
1.转换成数字
- Number(数据)
- 转成数字类型
- 如果字符串内容里有非数字,转换失败时结果为 NaN(Not a Number)即不是一个数字
- NaN也是number类型的数据,代表非数字
- parseInt(数据)
- 只保留整数
- parseFloat(数据)
- 可以保留小数
- let num = +prompt(‘输入一个数’)
- 前面加上一个加号 也可以转换成数字
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 隐式转换</title>
</head>
<body>
<script>
let t = '12'
let f = 8
// 显式将字符串 12 转换成数值 12
t = Number(t)
// 检测转换后的类型
// console.log(typeof t);
console.log(t + f) // 结果为 20
// 并不是所有的值都可以被转成数值类型
let str = 'hello'
// 将 hello 转成数值是不现实的,当无法转换成
// 数值时,得到的结果为 NaN (Not a Number)
console.log(Number(str))
// 1.表示数字的字符串: '1234'
// let num = '1234'
// console.log(Number(num)); //1234
// console.log(parseInt(num)); //1234
// console.log(parseFloat(num)); //1234
// 2.表示有小数的数字字符串: '2500.999'
// let num = '2500.999'
// console.log(Number(num)); //2500.999
// console.log(parseInt(num)); //2500
// console.log(parseFloat(num)); //2500.999
// 3.以数字开头的字符串: '8888abcd'
// let num = '8888abcd'
// console.log(Number(num)); //NaN
// console.log(parseInt(num)); //8888
// console.log(parseFloat(num)); //8888
// 4.全字符的字符串: 'hello 你好'
let num = 'hello 你好'
console.log(Number(num)); //NaN
console.log(parseInt(num)); //NaN
console.log(parseFloat(num)); //NaN
</script>
</body>
</html>
2.转换成字符串
1.String()
2.变量.toString 括号里面可以跟进制
3.两者区别
11.自增自减
前置和后置在单独使用的时候效果第一样的。不一样的地方是在运算的时候。
- 自增:
- i++ 先运算 后自增
- ++i 先自增 后运算
- 自减:
- i-- 先运算 后自减
- –i 先自减 后运算
12.比较运算符
- 比较运算符:
- > : 左边是否大于右边
- <: 左边是否小于右边
- >=: 左边是否大于或等于右边
- <=: 左边是否小于或等于右边
- ==: 左右两边值是否相等
- ===: 左右两边是否类型和值都相等
- !==: 左右两边是否不全等
比较结果为boolean类型,即只会得到 true 或 false
- 对比:
- = 单等是赋值
- == 是判断
- === 是全等
开发中判断是否相等,强烈推荐使用 ===
12-1 ASCII字符比较
a:97
A:65
0:48
- 字符串比较,是比较的字符对应的ASCII码
- 从左往右依次比较
- 如果第一位一样再比较第二位,以此类推
- 比较的少,了解即可
- NaN不等于任何值,包括它本身
- 涉及到"NaN“ 都是false
- 尽量不要比较小数,因为小数有精度问题
- 不同类型之间比较会发生隐式转换
- 最终把数据隐式转换转成number类型再比较
- 所以开发中,如果进行准确的比较我们更喜欢 === 或者 !==
13.逻辑运算符
- && and 逻辑与
- 两边都为true,结果才为true。只要有一侧false,就false
- || or 逻辑或
- 两边都为true,结果才为true。只要有一侧false,就false
- ! not 逻辑非
- 取反
14. 运算符优先级
优先级 | 运算符 | 顺序 |
---|---|---|
1 | 小括号 | () |
2 | 一元运算符 | ++、–、! |
3 | 算术运算符 | 先* / % 后+ - |
4 | 关系运算符 | > >= < <= |
5 | 相等运算符 | == != === !== |
6 | 逻辑运算符 | 先&&后|| |
7 | 赋值运算符 | = |
8 | 逗号运算符 | , |
- 一元运算符里面的逻辑非优先级很高
- 逻辑与比逻辑或优先级高
15.三元运算符
- 说明:三元运算符和if双分支是等价的
- 语法:判断条件 ? 条件成立时执行语句 : 条件不成立时执行的语句
16.Switch选择语句
switch (数据) {
case 值1:
值1匹配后执行的语句
break
case 值2:
值2匹配后执行的语句
break
case 值3:
值3匹配后执行的语句
break
······
default:
以上都没有被匹配到执行的语句
break
}
释义:
找到跟小括号里数据全等的case值,并执行里面对应的代码
若没有全等 === 的则执行default里的代码
例:数据若跟值2全等,则执行代码2
注意:
1. switch case语句一般用于等值判断,不适合于区间判断
2. switch case一般需要配合break关键字使用 没有break会造成case穿透
3. case后面只能跟上数值,不能写表达式
17.循环-while
while循环:
-
语法:
-
while(循环终止条件){
循环语句
}
-
-
循环的三要素:
- 循环变量初始值(用于记录循环次数)
- 循环变量的变化(每循环一次进行依次记录)
- 循环的终止条件(用于退出循环,否则死循环)
18.循环退出
目标: 能说出continue和break的区别
循环结束:
continue:结束本次循环,继续下次循环(程序执到continue的时候,后面的代码都不执行了。后面如果有需要i++的地方注意)
break:跳出所在的循环
let i = 1
while (i <= 5) {
if (i === 3) {
i++
//程序执到continue的时候,后面的代码都不执行了,所以要在continue之前添加i++,否则一直循环i=3
continue
//结束本次循环 下面的代码都不在循环
}
console.log(`我要吃第${i}个包子`);
i++
}
19.循环-for
1.for循环-基本使用
for循环语法
**作用:**重复执行代码
**好处:**把声明起始值、循环条件、变化值写到一起,让人一目了然,它是最常使用的循环形式
- 实现循环的 3 要素
<script>
// 1. 语法格式
// for(起始值; 终止条件; 变化量) {
// // 要重复执行的代码
// }
// 2. 示例:在网页中输入标题标签
// 起始值为 1
// 变化量 i++
// 终止条件 i <= 6
for(let i = 1; i <= 6; i++) {
document.write(`<h${i}>循环控制,即重复执行<h${i}>`)
}
</script>
-
变化量和死循环,
for
循环和while
一样,如果不合理设置增量和终止条件,便会产生死循环。while(true){}为死循环 for(;;){}为死循环
-
跳出和终止循环
<script>
// 1. continue
for (let i = 1; i <= 5; i++) {
if (i === 3) {
continue // 结束本次循环,继续下一次循环
}
console.log(i)
}
// 2. break
for (let i = 1; i <= 5; i++) {
if (i === 3) {
break // 退出结束整个循环
}
console.log(i)
}
</script>
结论:
JavaScript
提供了多种语句来实现循环控制,但无论使用哪种语句都离不开循环的3个特征,即起始值、变化量、终止条件,做为初学者应着重体会这3个特征,不必过多纠结三种语句的区别。- 起始值、变化量、终止条件,由开发者根据逻辑需要进行设计,规避死循环的发生。
- 当如果明确了循环的次数的时候推荐使用
for
循环,当不明确循环的次数的时候推荐使用while
循环
注意:
for
的语法结构更简洁,故for
循环的使用频次会更多。
2.退出循环
- continue 退出本次循环执行下一次,位于continue后面的代码就不执行了,一般用于排除或者跳过某一个选项的时候, 可以使用continue
- break 退出整个for循环,一般用于结果已经得到, 后续的循环不需要的时候可以使用
了解:
- while(true) 来构造“无限”循环,需要使用break退出循环。
- for(;😉 也可以来构造“无限”循环,同样需要使用break退出循环。
总结:
for循环和while循环有什么区别呢:
- 当如果明确了循环的次数的时候推荐使用for循环
- 当不明确循环的次数的时候推荐使用while循环
3.循环嵌套
实际上 JavaScript 中任何一种循环语句都支持循环的嵌套,如下代码所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kt91fqlA-1675651923813)(D:/feiqiu/AppData/Roaming/feiq/Recv%20Files/%E5%B0%B1%E4%B8%9A%E7%8F%AD%E7%B4%A0%E6%9D%90/JavaScript%E5%9F%BA%E7%A1%80/JavaScript%E5%9F%BA%E7%A1%80v8.0%20%E9%85%8D%E5%A5%97%E8%B5%84%E6%96%99/JavaScript%E5%9F%BA%E7%A1%80%E7%AC%AC%E4%B8%89%E5%A4%A9/02-%E7%AC%94%E8%AE%B0/assets/1647918261399.png)]
// 1. 外面的循环 记录第n天
for (let i = 1; i < 4; i++) {
document.write(`第${i}天 <br>`)
// 2. 里层的循环记录 几个单词
for (let j = 1; j < 6; j++) {
document.write(`记住第${j}个单词<br>`)
}
}
记住,外层循环循环一次,里层循环循环全部
案例-倒三角
// 外层打印几行
for (let i = 1; i <= 5; i++) {
// 里层打印几个星星
for (let j = 1; j <= i; j++) {
document.write('★')
}
document.write('<br>')
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VGd4o7Wu-1675651923817)(D:/feiqiu/AppData/Roaming/feiq/Recv%20Files/%E5%B0%B1%E4%B8%9A%E7%8F%AD%E7%B4%A0%E6%9D%90/JavaScript%E5%9F%BA%E7%A1%80/JavaScript%E5%9F%BA%E7%A1%80v8.0%20%E9%85%8D%E5%A5%97%E8%B5%84%E6%96%99/JavaScript%E5%9F%BA%E7%A1%80%E7%AC%AC%E4%B8%89%E5%A4%A9/02-%E7%AC%94%E8%AE%B0/assets/1647918678956.png)]
案例-九九乘法表
样式css
span {
display: inline-block;
width: 100px;
padding: 5px 10px;
border: 1px solid pink;
margin: 2px;
border-radius: 5px;
box-shadow: 2px 2px 2px rgba(255, 192, 203, .4);
background-color: rgba(255, 192, 203, .1);
text-align: center;
color: hotpink;
}
javascript
// 外层打印几行
for (let i = 1; i <= 9; i++) {
// 里层打印几个星星
for (let j = 1; j <= i; j++) {
// 只需要吧 ★ 换成 1 x 1 = 1
document.write(`
<div> ${j} x ${i} = ${j * i} </div>
`)
}
document.write('<br>')
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tXvKit8B-1675651923818)(D:/feiqiu/AppData/Roaming/feiq/Recv%20Files/%E5%B0%B1%E4%B8%9A%E7%8F%AD%E7%B4%A0%E6%9D%90/JavaScript%E5%9F%BA%E7%A1%80/JavaScript%E5%9F%BA%E7%A1%80v8.0%20%E9%85%8D%E5%A5%97%E8%B5%84%E6%96%99/JavaScript%E5%9F%BA%E7%A1%80%E7%AC%AC%E4%B8%89%E5%A4%A9/02-%E7%AC%94%E8%AE%B0/assets/1647918734677.png)]
20.数组
知道什么是数组及其应用的场景,掌握数组声明及访问的语法。
1.数组是什么?
数组:(Array)是一种可以按顺序保存数据的数据类型
**使用场景:**如果有多个数据可以用数组保存起来,然后放到一个变量中,管理非常方便
2.数组的基本使用
1.数组的声明方式
1.字面量声明方式:let arr = [数据1,数据2,数据3];
2.构造函数(实例化)方式:let arr = new Array(数据1,数据2,数据3)
2.定义数组和数组单元
<script>
// 1. 语法,使用 [] 来定义一个空数组
// 定义一个空数组,然后赋值给变量 classes
// let classes = [];
// 2. 定义非空数组
let classes = ['小明', '小刚', '小红', '小丽', '小米']
</script>
通过 []
定义数组,数据中可以存放真正的数据,如小明、小刚、小红等这些都是数组中的数据,我们这些数据称为数组单元,数组单元之间使用英文逗号分隔。
3.访问数组和数组索引
使用数组存放数据并不是最终目的,关键是能够随时的访问到数组中的数据(单元)。其实 JavaScript 为数组中的每一个数据单元都编了号,通过数据单元在数组中的编号便可以轻松访问到数组中的数据单元了。
我们将数据单元在数组中的编号称为索引值,也有人称其为下标。
索引值实际是按着数据单元在数组中的位置依次排列的,注意是从 0
开始的,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8DxtsBfH-1675651923819)(D:/feiqiu/AppData/Roaming/feiq/Recv%20Files/%E5%B0%B1%E4%B8%9A%E7%8F%AD%E7%B4%A0%E6%9D%90/JavaScript%E5%9F%BA%E7%A1%80/JavaScript%E5%9F%BA%E7%A1%80v8.0%20%E9%85%8D%E5%A5%97%E8%B5%84%E6%96%99/JavaScript%E5%9F%BA%E7%A1%80%E7%AC%AC%E4%B8%89%E5%A4%A9/02-%E7%AC%94%E8%AE%B0/assets/array.jpg)]
观察上图可以数据单元【小明】对应的索引值为【0】,数据单元【小红】对应的索引值为【2】
<script>
let classes = ['小明', '小刚', '小红', '小丽', '小米']
// 1. 访问数组,语法格式为:变量名[索引值]
document.write(classes[0]) // 结果为:小明
document.write(classes[1]) // 结果为:小刚
document.write(classes[4]) // 结果为:小米
// 2. 通过索引值还可以为数组单重新赋值
document.write(classes[3]) // 结果为:小丽
// 重新为索引值为 3 的单元赋值
classes[3] = '小小丽'
document.wirte(classes[3]); // 结果为: 小小丽
</script>
3.数据单元值类型
数组做为数据的集合,它的单元值可以是任意数据类型
<script>
// 6. 数组单值类型可以是任意数据类型
// a) 数组单元值的类型为字符类型
let list = ['HTML', 'CSS', 'JavaScript']
// b) 数组单元值的类型为数值类型
let scores = [78, 84, 70, 62, 75]
// c) 混合多种类型
let mixin = [true, 1, false, 'hello']
</script>
4.数组长度属性
重申一次,数组在 JavaScript 中并不是新的数据类型,它属于对象类型。
<script>
// 定义一个数组
let arr = ['html', 'css', 'javascript']
// 数组对应着一个 length 属性,它的含义是获取数组的长度
console.log(arr.length) // 3
</script>
5.操作数组
数组做为对象数据类型,不但有 length
属性可以使用,还提供了许多方法:
- 数组.push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度 (重点)
- arr.unshift(新增的内容) 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度
- pop 删除最后一个元素,并返回该元素的值。例如arr.pop()
- shift 删除第一个元素,并返回该元素的值。例如arr.shift()
- splice 删除指定元素,数组名.splice(起始位置,删除的个数),返回的是删除的那个元素。例如arr.splice(2,1)
- arr.splice(起始位置(索引号),删除几个元素),删除的起始位置指的是索引值!从0开始计算
- splice中删除的个数是可选的,如果省略则代表从指定位置开始删除到最后
- splice 添加元素,splice(起始位置,删除个数,添加数组元素)
使用以上4个方法时,都是直接在原数组上进行操作,即成功调任何一个方法,原数组都跟着发生相应的改变。并且在添加或删除单元时 length
并不会发生错乱。
<script>
// 定义一个数组
let arr = ['html', 'css', 'javascript']
// 1. push 动态向数组的尾部添加一个单元
arr.push('Nodejs')
console.log(arr)
arr.push('Vue')
// 2. unshit 动态向数组头部添加一个单元
arr.unshift('VS Code')
console.log(arr)
// 3. splice 动态删除任意单元
arr.splice(2, 1) // 从索引值为2的位置开始删除1个单元
console.log(arr)
let arr = ['red', 'green', 'blue']
//arr.splice(1, 0, 'pink') // 在索引号是1的位置添加 pink
//console.log(arr) // ['red', 'pink', 'green', 'blue']
arr.splice(1, 0, 'pink', 'hotpink') // 在索引号是1的位置添加 pink hotpink
console.log(arr) // ['red', 'pink', 'hotpink', 'green', 'blue']
// 4. pop 删除最后一个单元
arr.pop()
console.log(arr)
let arr = ['red','green','blue']
arr.pop()//blue
console.log(arr)// ['red','green']
// 5. shift 删除第一个单元
arr.shift()
console.log(arr)
</script>
6.冒泡排序
核心思想:小的数往上冒,大的数往下沉
双重for循环
let arr = [7, 6, 4, 5, 3, 8, 2, 9, 1]
for (let j = 0; j < arr.length - 1; j++) {
for (let i = 0; i < arr.length-j; i++) {
if (arr[i] > arr[i + 1]) {
let temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
}
}
}
console.log(arr);
数组排序方法:数组.sort()
arr.sort() 数组排序
注意:
- 默认情况下sort方法是以元素字符串的形式进行比较,可能存在数字排序问题,解决方式需要传入一个排序函数
- 函数中 return a-b 表示从小到大排序;反之 return b-a 表示从大到小排序
// let arr = [7, 6, 4, 5, 3, 8, 2, 9, 1]
// arr.sort()
// console.log(arr);
// 出现问题
// let arr = [7, 6, 4, 5, 30, 8, 2, 9, 1]
// arr.sort()
// console.log(arr);
let arr = [7, 6, 4, 5, 30, 8, 2, 9, 1]
arr.sort(function (a, b) {
return a - b
})
console.log(arr);
21.函数
1.函数的命名
函数名命名规范 :
- 和变量命名基本一致
- 尽量小驼峰式命名法
- 前缀应该为动词
- 命名建议:常用动词约定
2.声明(定义)
声明(定义)一个完整函数包括关键字、函数名、形式参数、函数体、返回值5个部分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-67BRHRhU-1675651923820)(D:/feiqiu/AppData/Roaming/feiq/Recv%20Files/%E5%B0%B1%E4%B8%9A%E7%8F%AD%E7%B4%A0%E6%9D%90/JavaScript%E5%9F%BA%E7%A1%80/JavaScript%E5%9F%BA%E7%A1%80v8.0%20%E9%85%8D%E5%A5%97%E8%B5%84%E6%96%99/JavaScript%E5%9F%BA%E7%A1%80%E7%AC%AC%E5%9B%9B%E5%A4%A9/03-%E7%AC%94%E8%AE%B0/assets/function.jpg)]
3.调用
声明(定义)的函数必须调用才会真正被执行,使用 ()
调用函数。
函数一次声明可以多次调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 声明和调用</title>
</head>
<body>
<script>
// 声明(定义)了最简单的函数,既没有形式参数,也没有返回值
function sayHi() {
console.log('嗨~')
}
// 函数调用,这些函数体内的代码逻辑会被执行
// 函数名()
sayHi()
// 可以重复被调用,多少次都可以
sayHi()
</script>
</body>
</html>
注:函数名的命名规则与变量是一致的,并且尽量保证函数名的语义。
小案例: 小星星
<script>
// 函数声明
function sayHi() {
// document.write('hai~')
document.write(`*<br>`)
document.write(`**<br>`)
document.write(`***<br>`)
document.write(`****<br>`)
document.write(`*****<br>`)
document.write(`******<br>`)
document.write(`*******<br>`)
document.write(`********<br>`)
document.write(`*********<br>`)
}
// 函数调用
sayHi()
sayHi()
sayHi()
sayHi()
sayHi()
</script>
4.参数
通过向函数传递参数,可以让函数更加灵活多变,参数可以理解成是一个变量。
声明(定义)一个功能为打招呼的函数
- 传入数据列表
- 声明这个函数需要传入几个数据
- 多个数据用逗号隔开
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 函数参数</title>
</head>
<body>
<script>
// 声明(定义)一个功能为打招呼的函数
// function sayHi() {
// console.log('嗨~')
// }
// 调用函数
// sayHi()
// 这个函数似乎没有什么价值,除非能够向不同的人打招呼
// 这就需要借助参数来实现了
function sayHi(name) {
// 参数 name 可以被理解成是一个变量
console.log(name)
console.log('嗨~' + name)
}
// 调用 sayHi 函数,括号中多了 '小明'
// 这时相当于为参数 name 赋值了
sayHi('小明')// 结果为 小明
// 再次调用 sayHi 函数,括号中多了 '小红'
// 这时相当于为参数 name 赋值了
sayHi('小红') // 结果为 小红
</script>
</body>
</html>
总结:
- 声明(定义)函数时的形参没有数量限制,当有多个形参时使用
,
分隔 - 调用函数传递的实参要与形参的顺序一致
参数默认值:
场景:当有参函数在调用时,并没有传递参数,这时候函数中的参数为underfined,可能会导致出错
优化方式:给参数添加默认值,来将函数进行优化,使函数更完整
语法:在函数声明的参数后面,使用=赋值符号来设置默认值,如果调用时传入参数则按照传入的为准,反之使用默认值
举例:
function getSum(x,y){
console.log(x+y)
}
getSum()
function getSum(x = 0, y = 0) {
document.write(x + y)
}
getSum()
5.形参和实参
- 形参:声明函数时写在函数名右边小括号里的叫形参(形式上的参数)
- 实参:调用函数时写在函数名右边小括号里的叫实参(实际上的参数)
- 形参可以理解为是在这个函数内声明的变量(比如 num1 = 10)实参可以理解为是给这个变量赋值
- 开发中尽量保持形参和实参个数一致
- 我们曾经使用过的 alert(‘打印’), parseInt(‘11’), Number(‘11’) 本质上都是函数调用的传参
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 函数参数</title>
</head>
<body>
<script>
// 声明(定义)一个计算任意两数字和的函数
// 形参 x 和 y 分别表示任意两个数字,它们是两个变量
function count(x, y) {
console.log(x + y);
}
// 调用函数,传入两个具体的数字做为实参
// 此时 10 赋值给了形参 x
// 此时 5 赋值给了形参 y
count(10, 5); // 结果为 15
</script>
</body>
</html>
6.返回值
函数的本质是封装(包裹),函数体内的逻辑执行完毕后,函数外部如何获得函数内部的执行结果呢?要想获得函数内部逻辑的执行结果,需要通过 return
这个关键字,将内部执行结果传递到函数外部,这个被传递到外部的结果就是返回值。
函数返回结果的关键字:return 结果
- 例如:
function fn(x){
return x
}
- 注意点:
如果函数没有return,则默认返回undefined
- return后面的值返回给函数的调用者fn()!!
1.函数的返回值执行过程:
函数的返回值执行过程!
function fn(){
return 20
}
let re = fn()
console.log(re)
//fn()是调用者,return的值给调用者,相当于 fn()=20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 基础 - 函数返回值</title>
</head>
<body>
<script>
// 定义求和函数
function count(a, b) {
let s = a + b
// s 即为 a + b 的结果
// 通过 return 将 s 传递到外部
return s
}
// 调用函数,如果一个函数有返回值
// 那么可将这个返回值赋值给外部的任意变量
let total = count(5, 12)
// console.log(alert('你好啊')); //undefined
// console.log(prompt('请输入任意内容')); //会返回用户输入的内容
// 函数的返回值
function fn() {
return 20;
}
fn()
// 相当于执行了fn()=20 fn()是调用者
console.log(fn());
// let num=prompt('...')
let re = fn()
console.log(re);
</script>
</body>
</html>
总结:
- 在函数体中使用return 关键字能将内部的执行结果交给函数外部使用
- 函数内部只能出现1 次 return,并且 return 下一行代码不会再被执行,所以return 后面的数据不要换行写
- return会立即结束当前函数
- 函数可以没有return,这种情况默认返回值为 undefined
2.函数返回值得细节
<!--
函数需要返回值的目的:方便含实质性的结果在程序其他地方使用
注意点:
return关键字除了能够返回函数结果,还有结束函数的作用,函数中代码不要写在return的下面
没有return的函数,返回的结果是undefined
-->
<script>
function getNumber() {
let a = 10
let b = 20
return a + b
console.log('这是写在return后面的打印语句');
}
console.log(getNumber());
</script>
22.作用域
通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
作用域的作用:
限制了变量的适用范围,提高了程序逻辑的局部性,增强了程序的可靠性,避免变量的命名冲突。
1.全局作用域
作用于所有代码执行的环境(被 script 标签包裹)或者一个独立的 js 文件
处于全局作用域内的变量,称为全局变量
2.局部作用域
作用于函数内的代码环境,就是局部作用域。 因为跟函数有关系,所以也称为函数作用域。
处于局部作用域内的变量称为局部变量
如果函数内部,变量没有声明,直接赋值,也当全局变量看,但是强烈不推荐
但是有一种情况,函数内部的形参可以看做是局部变量。
3.变量的查找规则(作用域链)
js引擎查找变量是从当前作用域出发查找,如果当前作用域没找到,则一级一级向外访问,直至查找到全局作用域;如果最终没有找到该变量,则报错
4. 变量使用的两种特殊情况
- 函数内部的变量,没有使用关键字声明,则会看做全局变量,可能会造成命名冲突(避免情况产生)
- 函数中的形参相当于局部变量,外部访问不到
// 情况1
// num = 20
// function fn() {
// let num=10;
// }
// fn()
// console.log(num);
// 情况2
function fn(x, y) {
console.log(x, y);
}
fn(1, 2)
console.log(x, y);
23.匿名函数
函数可以分为具名函数和匿名函数
匿名函数:没有名字的函数,无法直接使用。
使用方式:
1.函数表达式
// 声明
let fn = function() {
console.log('函数表达式')
}
// 调用
fn()
2.立即执行函数(自调用函数)
一定要加分号!!!!
函数的特殊用法,声明时立刻执行,主要用来创造局部作用域,解决变量名冲突的问题
写法1:(function(){ xxx })();
写法2:(function(){xxxx}());
;(function () {
let num = 10
})();
(function () {
let num = 20
})();
(function(x,y){
console.log(x+y);
})(1,2)
注意点:
无需调用,立即执行,其实本质已经调用了
多个立即执行函数之间用分号隔开
作用:
避免全局变量之间的污染
3.函数表达式和具名函数的不同
1.具名函数的调用可以写到任何位置
2.函数表达式必须先声明函数表达式,后调用
区别:功能上两者一致,使用函数表达式声明实际上是给变量赋值,所以要遵循变量的先声明后使用的原则
24.逻辑中断-短路运算符
短路运算符:指的是逻辑&&和逻辑||,在某种情况下可以让右边的代码不执行达到短路的效果
短路场景:
- &&左边为false的时候,右边代码短路不执行
- ||左边为false的时候,右边代码短路不执行
短路原理:
- 当js引擎能够根据第一个值得到逻辑判断结果时,右边的代码就会跳过不执行
- 逻辑运算执行完后,会把最后执行的表达式返回
应用场景:
- 常见于ES5中,给形参设置默认值操作
// 逻辑中断的应用-为变量设置默认值
function func(x = 0, y = 0) {
console.log(x + y);
}
func()
// 旧版本设置默认值的方式
function fn(x, y) {
x = x || 0
y = y || 0
console.log(x + y)
}
fn(1, 2)
25.转换为Boolean类型
强制转化Boolean类型:Boolean(数据)
1.显示转换
1.Boolean(内容)
记忆: ‘’ 、0、undefined、null、false、NaN 转换为布尔值后都是false, 其余则为 true
console.log(Boolean('pink'))
console.log(Boolean(''))
console.log(Boolean(0))
console.log(Boolean(90))
console.log(Boolean(-1))
console.log(Boolean(undefined))
console.log(Boolean(null))
console.log(Boolean(NaN))
2.隐式转换:
- 字符串与其他数据类型相加,结果都为字符串。有字符串的加法 “” + 1 ,结果是 “1”
- 减法 - (像大多数数学运算一样)只能用于数字,当其他数据类型参与时会进行转换
- 空字符串会转换为0,其他字符串会转为NaN
- null 经过数字转换之后会变为 0
- undefined 经过数字转换之后会变为 NaN
26.对象
1. 对象声明语法
let 对象名 = {}
let 对象名 = new Object()
实际开发中,我们多用花括号。{}是对象字面量
2.对象由属性和方法组成
let 对象名={
属性名:属性值,
方法名:函数
}
属性:
数据描述性的信息称为属性,如人的姓名、身高、年龄、性别等,一般是名词性的。
- 属性都是成对出现的,包括属性名和值,它们之间使用英文 : 分隔
- 多个属性之间使用英文 , 分隔
- 属性就是依附在对象上的变量(外面是变量,对象内是属性)
- 属性名可以使用 “” 或 ‘’,一般情况下省略,除非名称遇到特殊符号如空格、中横线等
方法:
let person={
name:'andy',
sayHi:function(){
document.write('hi~')
}
}
let star = {
name: '刘德华',
age: 20,
'action-skill': function () {
console.log('唱歌');
}
}
// 调用方法
star['action-skill']()
- 方法是由方法名和函数两部分构成,它们之间使用 : 分隔
- 多个属性之间使用英文 , 分隔
- 方法是依附在对象中的函数
- 方法名可以使用 “” 或 ‘’,一般情况下省略,除非名称遇到特殊符号如空格、中横线等
- 声明对象,并添加了若干方法后,可以使用 . 调用对象中函数,我称之为方法调用。
- 也可以添加形参和实参
3.对象的使用
3-1增
对象名.新属性名=新值
3-2删(了解)
delete 对象名.属性名
3-3改
对象名.属性名=新值
3-4查
-
对象名.属性名
-
对象名[‘属性名’]
- 对于多词属性或则 - 等属性,点操作就不能用了。
- 我们可以采取: 对象[‘属性’] 方式, 单引号和双引号都可以。一定要加引号
对象名['属性名'] 举例: let obj = { 'goods-name': '手机', name: 'iphone', num: '00001', weight: '340g' } // console.log(obj.goods-name); //无法找到 转换成减法运算了 console.log(obj['goods-name']); //手机 这种查找方法适用于属性名是字符串'goods-name'的形式
4.遍历对象
语法:
遍历对象:
for (let k in obj){
console.log(k); //打印属性名,是字符串类型的属性名!
console.log(obj[k]); //表示通过k访问属性值
}
注意点:
for···in主要用于遍历对象,一般不会用于遍历数组
k是循环体中的一个变量,用来获取每次进入循环中对象的属性名
k拿到的数据是字符串,对象通过字符串访问属性值可以使用 obj[字符串]的方式
一定记住: k 是获得对象的属性名, 对象名[k] 是获得 属性值
注意点:
for···in主要用于遍历对象,一般不会用于遍历数组
k是循环体中的一个变量,用来获取每次进入循环中对象的属性名
k拿到的数据是字符串,对象通过字符串访问属性值可以使用 obj[字符串]的方式
5.内置对象-Math
Math对象包含的方法有:
- random:生成0-1之间的随机小数(包含0不包括1) [0, 1)
- ceil:天花板 取大值 向上取整 舍弃小数部分,整数部分加1
- floor:地板 取小值 向下取整 舍弃小数部分,整数部分不变
- round: 取整,四舍五入原则 负数情况下,看接近谁
- max:找最大数
- min:找最小数
- sqrt:平方根
- pow:幂运算
- abs:绝对值
坐标轴上查看:
----------------------------------------------------------------------------------->
-3 -2.51 -2.5 -2
ceil:向右取
floor:向左取
round:看接近谁,负数的话-2.5算接近-2 ,-2.51算-3
5-1 内置对象-生成任意范围随机数
Math.random() 随机数函数, 返回一个0-1之间,并且包括0不包括1的随机小数 [0, 1)
- 如何生成0-10的随机数呢?
Math.floor(Math.random() * (10 + 1))
- 如何生成5-10的随机数?
Math.floor(Math.random() * (5 + 1)) + 5
- 如何生成N-M之间的随机数
Math.floor(Math.random() * (M - N + 1)) + N
5-2 封装一个函数-求随机数
Math.random()随机函数:
会返回一个0-1之间的,包括0但是不包括1的随机小数[0,1)
取N~M范围内的随机数公式:[N,M]
Math.floor(Math.random()*(M-N+1))+N
公式推导过程:
1.Math.random()*N,返回的结果范围从0-N但不包括N [0,N)
2.Math.floor(Math.random()*N),将结果向下取整后结果范围从0到N-1 [0,N-1]
3.Math.floor(Math.random()*(N+1)),
······
// 封装一个随机数函数,将随机结果返回出来 N-M任意一位整数
function getRandom(N, M) {
return Math.floor(Math.random() * (M - N + 1)) + N
}
console.log(getRandom(1, 100));
27.拓展-术语解释
术语 | 解释 | 举例 |
---|---|---|
关键字 | 在JavaScript中有特殊意义的词汇 | let、var、function、if、else、switch、case、break |
保留字 | 在目前的JavaScript中没有意义,但未来可能会具有特殊意义的词汇 | int、short、long、char |
标识(标识符) | 变量名、函数名的另一种叫法 | 无 |
表达式 | 能产生值得代码,一般配合运算符出现 | 10+3、age>=18 |
语句 | 一段可执行的代码 | if() for() |
28.拓展- 基本数据类型和引用数据类型
目标:了解基本数据类型和引用数据类型的存储方式
简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型。
-
值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型
string ,number,boolean,undefined,null
-
引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型
通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date、函数等
堆栈空间分配区别:
1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;
简单数据类型存放到栈里面
2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。
引用数据类型存放到堆里面
WebAPIs
DOM
1.const
变量声明:
变量声明有三个: var let 和 const
建议: const 优先,尽量使用const,
原因: const 语义化更好
const声明关键字:
- 用于声明常量,常量指在程序中一直不变的量/数据
- const在声明时就需要赋值,否则报错
- 引用类型可以使用const进行声明,并且在改变引用类型的值时不会报错,引用地址不发生变化即可
- 推荐优先使用const进行声明
注意:
- const 声明的值不能更改,而且const声明变量的时候需要里面进行初始化
- 但是对于引用数据类型,const声明的变量,里面存的不是值,不是值,不是值,是 地址,所以,引用数据类型使用const可以更改!
总结:
-
以后声明变量我们优先使用哪个?
const
有了变量先给const,如果发现它后面是要被修改的,再改为let
-
为什么const声明的对象可以修改里面的属性?
因为对象是引用类型,里面存储的是地址,只要地址不变,就不会报错
建议数组和对象使用 const 来声明
-
什么时候使用let声明变量?
如果基本数据类型的值或者引用类型的地址发生变化的时候,需要用let
比如 一个变量进行加减运算,比如 for循环中的 i++
2.概念 DOM
DOM (文档对象模型)----DOM是浏览器提供的一套专门用来 操作网页内容 的功能
DOM作用:开发网页内容特效和实现用户交互
1-1 DOM树:
概念:
- DOM树:将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树。是描述网页内容关系的名词,作用就是直观的体现了标签与标签之间的关系
- DOM对象:浏览器根据html标签生成的 JS对象
+ 每一个标签都由一个对应的js对象,所有的标签属性都可以在这个对象上面找到
+ 修改这个对象的属性会自动映射到标签身上
- document文档对象:
+ document是 DOM 里提供的一个对象,表示整个页面文档
+ document提供了大量操作页面内容的方法和属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标题</title>
</head>
<body>
文本
<a href="">链接名</a>
<div id="" class="">文本</div>
</body>
</html>
如下图所示,将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树,文档树直观的体现了标签与标签之间的关系。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-urZbHFgz-1675651923821)(D:/feiqiu/AppData/Roaming/feiq/Recv%20Files/%E5%B0%B1%E4%B8%9A%E7%8F%AD%E7%B4%A0%E6%9D%90/WebAPI/web%20APIs%20v8.0%E9%85%8D%E5%A5%97%E8%B5%84%E6%96%99/web%20APIs%20%E7%AC%AC%E4%B8%80%E5%A4%A9/02-%E7%AC%94%E8%AE%B0/assets/web-api.jpg)]
1.2 DOM 节点
节点是文档树的组成部分,每一个节点都是一个 DOM 对象,主要分为元素节点、属性节点、文本节点等。
- 【元素节点】其实就是 HTML 标签,如上图中
head
、div
、body
等都属于元素节点。 - 【属性节点】是指 HTML 标签中的属性,如上图中
a
标签的href
属性、div
标签的class
属性。 - 【文本节点】是指 HTML 标签的文字内容,如
title
标签中的文字。 - 【根节点】特指
html
标签。 - 其它…
1.3 document
document
是 JavaScript 内置的专门用于 DOM 的对象,该对象包含了若干的属性和方法,document
是学习 DOM 的核心。
<script>
// document 是内置的对象
// console.log(typeof document);
// 1. 通过 document 获取根节点
console.log(document.documentElement); // 对应 html 标签
// 2. 通过 document 节取 body 节点
console.log(document.body); // 对应 body 标签
// 3. 通过 document.write 方法向网页输出内容
document.write('Hello World!');
</script>
上述列举了 document
对象的部分属性和方法,我们先对 document
有一个整体的认识。
3.获取DOM元素
3-1 根据CSS选择器来获取DOM元素 (重点)
查找元素DOM元素就是利用 JS 选择页面中标签元素
1.document.querySelector(‘CSS选择器’)
document.querySelector(‘CSS选择器’) 返回指定选择器的第一个元素对象 切记里面的选择器需要加符号(./#)
- 能够选中第一个符合条件的DOM元素对象
- 返回的是一个DOM对象,如果没有匹配到,则返回null。
2.document.querySelectorAll(‘CSS选择器’)
document.querySelectorAll(‘CSS选择器’) 返回指定选择器中所有元素对象集合 返回一个伪数组(伪数组:有长度和索引号,但是没有push等方法)
- 能够选中所有符合条件的DOM元素对象
- 返回的是一个NodeList对象集合,里面有多个DOM对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM - 查找节点</title>
</head>
<body>
<h3>查找元素类型节点</h3>
<p>从整个 DOM 树中查找 DOM 节点是学习 DOM 的第一个步骤。</p>
<ul>
<li>元素</li>
<li>元素</li>
<li>元素</li>
<li>元素</li>
</ul>
<script>
const p = document.querySelector('p') // 获取第一个p元素
const lis = document.querySelectorAll('li')
</script>
</body>
</html>
3-2 伪数组以及注意事项
获取DOM元素的方式:
- document.querySelector(‘CSS选择器’)
- 能够选中第一个符合条件的DOM元素对象
- 返回的是一个DOM对象
- document.querySelectorAll(‘CSS选择器’)
- 能够选中所有符合条件的DOM元素对象
- 返回的是一个NodeList对象集合,里面有多个DOM对象
伪数组(类数组):
- 具有真实数组的length和元素下标特点
- 不具备数组的方法,例如shift、push、unshift、pop等
3-3 了解其他方式
1. 通过id名称获取标签,返回符合条件的DOM元素对象
const oP = document.getElementById('target')
console.log(oP)
2. 通过标签名称获取,返回一个伪数组
const oDiv = document.getElementsByTagName('div')
console.log(oDiv)
3. 通过类名获取标签,返回一个伪数组
const oDiv = document.getElementsByClassName('box')
console.log(oDiv)
-
getElementById:返回一个匹配到id的DOM Element对象
document.getElementById('nav') //根据id获取一个元素
-
getElementsByTagName:返回带有指定标签名的对象的集合,以伪数组形式存储
document.getElementByTagName('div')//根据标签获取一类元素 获取页面所有 div
-
document.getElementsByClassName(‘类名’):根据类名返回元素对象集合
document.getElementByClassName('w')//根据类名获取元素 获取页面所有类名为 w 的
4.操作元素内容
通过修改 DOM 的文本内容,动态改变网页的内容。
1 .innerText
.innerText
- 获取DOM对象中的文本字符串
- 赋值时作为普通文本替换标签内容
innerText
将文本内容添加/更新到任意标签位置,文本中包含的标签不会被解析。
<script>
// innerText 将文本内容添加/更新到任意标签位置
const intro = document.querySelector('.intro')
// intro.innerText = '嗨~ 我叫李雷!'
// intro.innerText = '<h4>嗨~ 我叫李雷!</h4>'
</script>
2 .innerHTML
.innerHTML
- 获取DOM对象中的HTML标签字符串
- 赋值时作为HTML替换标签内容
innerHTML
将文本内容添加/更新到任意标签位置,文本中包含的标签会被解析。
<script>
// innerHTML 将文本内容添加/更新到任意标签位置
const intro = document.querySelector('.intro')
intro.innerHTML = '嗨~ 我叫韩梅梅!'
intro.innerHTML = '<h4>嗨~ 我叫韩梅梅!</h4>'
</script>
总结:如果文本内容中包含 html
标签时推荐使用 innerHTML
,否则建议使用 innerText
属性。
- 元素.innerText 属性 只识别文本,不能解析标签
- 元素.innerHTML 属性 能识别文本,能够解析标签
5.操作元素属性
1.操作元素常用属性
- 直接能过属性名修改,最简洁的语法
修改网页元素常见属性:
1. 获取元素对象
2. 对象.属性 = 值
举例:
img的src、title、alt属性
a标签的href、target属性
<script>
// 1. 获取 img 对应的 DOM 元素
const pic = document.querySelector('.pic')
// 2. 修改属性
pic.src = './images/lion.webp'
pic.width = 400;
pic.alt = '图片不见了...'
</script>
2.操作元素样式属性
2-1 通过style属性控制样式
更改元素的样式属性:
1. 获取页面元素对象
2. 对象.style.样式属性 = 值
注意点:
- 当样式属性是由多个单词组成时,使用小驼峰写法即可,css 属性的 - 连接符与 JavaScript 的 减运算符冲突,所以要改成驼峰法。例如:
background-color
要写成box.style.backgroundColor
还有fontSize等 - 通过style修改的样式是行内样式,注意权重问题
// 1. 获取标签对象
const div = document.querySelector('div')
// 2. 更改标签的样式
div.style.border = "10px solid green"
div.style.backgroundColor = "orange"
console.dir(div)
2-2.通过类名(className) 修改样式
更改标签的类名:
语法: 元素.className = “类名”
注意点:
class是js中的关键字,所以类名属性使用className代替
class是用新值替换旧值,**会覆盖!**所以在添加新类名时需要保留旧类名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.big {
font-size: 50px;
}
.weight {
font-weight: bold;
}
</style>
</head>
<body>
<div>
我是div里面的文字
</div>
</body>
</html>
<script>
// 1. 获取页面元素的对象
const div = document.querySelector('div')
// 2. 更改标签的类名
// div.className = 'big'
// div.className = 'weight'
div.className = 'big weight'
</script>
2-3.通过 classList 操作类控制CSS
为了解决className 容易覆盖以前的类名,我们可以通过classList方式追加和删除类名
元素的类名列表: 元素.classList
对类名列表的操作方法:
- 添加类名:元素.classList.add(“类名”)
- 删除类名:元素.classList.remove(“类名”)
- 切换类名:元素.classList.toggle(“类名”) 有就删,无就加
// 1. 获取元素的类名列表
const div = document.querySelector('div')
console.log(div.classList)
// 2. 对类名列表的操作
// div.className='weight'
// div.classList.add('weight')
// div.classList.remove('big')
// div.classList.toggle('red')//没有就添加,有就删除
注意:
- 是classList有add()、remove()、toggle()方法!是方法就要注意加小括号!!!
- classList不会替换以前的类名
3.操作表单元素属性
表单很多情况,也需要修改属性,比如点击眼睛,可以看到密码,本质是把表单类型转换为文本框
正常的有属性有取值的跟其他的标签属性没有任何区别
3-1获取设置表单元素的值:
- 获取:
- 表单元素.value
- 表单元素.type
- 设置:
- 表单元素.value = “值”
- 表单元素.type = “值”
提示:标签默认具有的属性,可以直接通过 元素.属性名 来访问
3-2 修改表单元素选中和禁用
- 获取:
- 表单元素.checked
- 表单元素.disabled
- 设置:
- 表单元素.checked = 布尔值
- 表单元素.disabled = 布尔值
提示:标签默认具有的属性,可以直接通过 元素.属性名 来访问
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" value="请输入">
<button disabled>按钮</button>
<input type="checkbox" name="" id="" class="agree">
<script>
// 1. 获取元素
let input = document.querySelector('input')
// 2. 取值或者设置值 得到input里面的值可以用 value
// console.log(input.value)
input.value = '小米手机'
input.type = 'password'
// 2. 启用按钮
let btn = document.querySelector('button')
// disabled 不可用 = false 这样可以让按钮启用
btn.disabled = false
// 3. 勾选复选框
let checkbox = document.querySelector('.agree')
checkbox.checked = false
</script>
</body>
</html>
4.自定义属性
- 标签属性的分类:
- 标准属性:标签默认具有的属性 比如class id title等, 可以直接使用点语法操作比如: disabled、checked、selected
- 自定义属性:开发者后期给标签自定义添加的属性
- 自定义属性的写法:
- html5中推出来了专门的data-自定义属性 ,在标签上使用 data-属性名 的方式添加自定义属性 ,在标签上一律以data-开头
- 在程序中使用 元素.dataset 方式来获取自定义属性的集合
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div data-id="1"> 自定义属性 </div>
<script>
// 1. 获取元素
let div = document.querySelector('div')
// 2. 获取自定义属性值
console.log(div.dataset.id)
</script>
</body>
</html>
6.定时器-间歇函数
间歇函数:setInterval(函数名,间隔时间毫秒)
作用:让一段代码按一定的时间间隔连续执行,也叫定时器函数。
可以先调用一次函数,使页面一刷新就有,不用间隔一秒后才显示
<script>
// 书写方式一:
/* function func() {
document.write('hahahah你好~')
}
// 为了使页面一刷新就有,我们可以先调用一次
func()
setInterval(func,1000)
// func后不加小括号 因为func()是调用的意思,func()会立马调用 里面写函数名
// 如果要加小括号,需要加引号
// setInterval('func()',1000)
*/
// 书写方式二:
setInterval(function(){
document.write('睡醒不愁~')
},1000)
</script>
注意:
- 函数名字不需要加括号
- 定时器返回的是一个id数字
关闭间歇函数
关闭间歇函数定时器方法:
let 变量名 = setInterval(函数,间隔时间)
clearInterval(变量名)
- 接收定时器返回值: let timer = setInterval(函数,间歇时间)
- 清空定时器:clearInterval(timer)
=========================================
7.事件监听
语法:
- 元素对象.addEventListener(‘事件类型’,要执行的函数)
事件监听三要素:
- 事件源: 谁被触发了?触发事件的dom元素。
- 事件类型:用什么方式触发,比如鼠标单击 click、鼠标经过 mouseover 等
- 事件调用的函数:要做什么事?事件触发后对应执行的函数
注意:
- 事件类型要加引号
- 函数是点击之后再去执行,每次点击都会执行一次
旧版本:
事件监听版本:
- 早期L0:元素.onclick = function(){}
- 主流L2:元素.addEventListener(‘click’,function(){})
区别:
- 早期的事件监听是通过赋值的形式添加,多个相同的事件监听会出现覆盖问题;现代主流的监听方式则多个相同的事件监听都能够正常执行
<script>
const btn = document.querySelector('button')
//早期做法
btn.onclick=function(){
alert('ok')
}
btn.onclick=function(){
alert('ok2')
}
</script>
<script>
const btn = document.querySelector('button')
// 主流做法
btn.addEventListener('click',function(){
alert('你点击了我')
})
btn.addEventListener('click',function(){
alert('你点击了我2')
})
console.dir(btn)
</script>
拓展 特殊的id
id可以直接通过addEventListener添加事件,不用再获取id元素,直接id名.addEventListener
垃圾回收机制:
js垃圾回收机制:
Javascript 中存在两种变量——全局变量和局部变量,全局变量的声明周期会一直持续,直到页面关闭;而局部变量声明在函数中,它的声明周期从执行函数开始,直到函数执行结束。在这个过程中,局部变量会在堆或栈上被分配相应的空间以存储它们的值,函数执行结束,这些局部变量也不再被使用,它们所占用的空间也就被释放
<button>按钮</button>
<script>
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
const num = Math.random
console.log(num);
})
</script>
8.事件类型
用户操作鼠标的相关事件类型:
- 鼠标事件
- click:鼠标点击
- mouseenter:鼠标移入
- mouseleave:鼠标移出
- 表单焦点事件
- focus:获得焦点(光标在内部闪烁)
- blur:失去焦点(光标从内部消失)
- 键盘事件
- keydown:键盘按键按下
- keyup:键盘按键弹起
- 文本编辑事件
- input:用户输入
<script>
// 获取元素
const input = document.querySelector('input')
// 绑定事件
// input.addEventListener('mouseenter',function(){
// console.log('鼠标移入了')
// })
// input.addEventListener('mouseleave',function(){
// console.log('鼠标离开了')
// })
// input.addEventListener('focus',function(){
// console.log('input获取焦点了')
// })
// input.addEventListener('blur',function(){
// console.log('input失去焦点了')
// })
// input.addEventListener('keydown',function(){
// console.log('键盘按下了')
// })
// input.addEventListener('keyup',function(){
// console.log('键盘弹起了')
// })
input.addEventListener('input',function(){
console.log('用户输入内容了')
})
</script>
9.事件对象
9-1.获取事件对象
1.目标:能说出什么是事件对象
事件对象也是个对象,这个对象里有事件触发时的相关信息
例如:鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息
2.使用场景
可以判断用户按下哪个键,比如按下回车键可以发布新闻
可以判断鼠标点击了哪个元素,从而做相应的操作
3.在事件绑定的回调函数的第一个参数就是事件对象 ,一般命名为event、ev、e
4.语法:
元素.addEventListener('click',function(e){})
//此处e 就是事件对象
9-2.事件对象常用属性
部分常用属性
- type
- 获取当前的事件类型
- clientX/clientY
- 获取光标相对于浏览器可见窗口左上角的位置
- offsetX/offsetY
- 获取光标相对于当前DOM元素左上角的位置
- key
- 用户按下的键盘键的值
- 现在不提倡使用keyCode
<input type="text">
<script>
const ipt = document.querySelector('input')
ipt.addEventListener('keyup',function(e){
console.log(e.key);
if (e.key=='Enter') {
console.log(123);
}
})
</script>
10-拓展-trim方法
字符串trim方法:字符串.trim()
返回结果:去除字符串两端空格后的新字符串(掐头去尾)
<script>
let str = ' abc '
console.log(str.length);
console.log(str.trim().length);
</script>
11.拓展-焦点伪类选择器
<style>
input:focus {
height: 80px;
}
input {
height: 35px;
transition: all 1s;
}
</style>
</head>
<body>
<input type="text">
</body>
12.环境对象
环境对象:指的是函数内部特殊的变量 this ,它代表着当前函数运行时所处的环境
**作用:**弄清楚this的指向,可以让我们代码更简洁
- 函数的调用方式不同,this 指代的对象也不同
- 【谁调用, this 就是谁】 是判断 this 指向的粗略规则
- 直接调用函数,其实相当于是 window.函数,所以 this 指代 window
总结理解:环境对象this:每一个函数都有一个this变量,指向当前调用函数的对象,最后一次调用函数的上一级对象,就是this的指向对象
13.回调函数
回调函数:
- 把函数当作另外一个函数的参数传递,这个函数就叫回调函数
- 回调函数本质还是函数,只不过把它当成参数使用
- 使用匿名函数作为回调函数比较常见
简单理解:作为参数的函数,就称为回调函数
<body>
<button>按钮</button>
<script>
//例一: 定时器
setInterval(function () {
console.log('回调函数被调用了');
}, 3000)
// 例二:事件监听函数
document.querySelector('button').addEventListener('click',function(){
console.log('事件触发了,当前函数被浏览器调用了');
})
</script>
</body>
14.事件流
事件流指的是事件完整执行过程中的流动路径
- 说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
- 简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父
- 实际工作都是使用事件冒泡为主
14-1 事件捕获(了解)
概念:浏览器从最外层的元素向内逐层触发事件对应函数(由外向内)
注意点:
-
事件捕获需要将事件监听函数的第三个参数设置为true
DOM.addEventListener(事件类型,处理函数,是否捕获)
-
事件监听函数的第三位参数默认值为false
-
如果使用L0事件监听,只有冒泡效果,没有捕获
14-2 事件冒泡
事件冒泡:
- 概念:当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发,这一过程被称为事件冒泡
- 简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
- 默认情况下绑定的事件都是位于冒泡阶段执行,并且L2事件监听第三个参数默认也为false,是冒泡阶段执行
14-3 阻止事件冒泡
场景:由元素触发的事件流,会继续向上冒泡,可能会影响父级标签,阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。
阻止冒泡步骤:
1. 获取事件对象
2. 在事件处理函数中执行 事件对象.stopPropagation() 方法
<!--
使用事件对象阻止标签默认事件:e.stopPropagation()
-->
<script>
// 1. 为整个文档对象添加点击事件
document.addEventListener('click', function () {
alert('文档对象点击生效了')
}, false)
// 2. 为father盒子添加点击事件
document.querySelector('.father').addEventListener('click', function () {
alert('father点击生效了')
}, false)
// 3. 为son盒子添加点击事件
document.querySelector('.son').addEventListener('click', function (e) {
// 1.接收事件对象
// 2.调用阻止冒泡方法
e.stopPropagation()
alert('son点击生效了')
}, false)
</script>
14-4 阻止元素默认行为
使用事件对象阻止标签默认事件:e.preventDefault()
<body>
<!--
使用事件对象阻止标签默认事件:e.preventDefault()
-->
<form action="https://www.itcast.cn">
<input type="submit" value="提交按钮">
</form>
<a href="https://www.baidu.com">点我去百度</a>
</body>
</html>
<script>
// 原来点击按钮会跳转 阻止跳转
const form = document.querySelector('form')
form.addEventListener('submit', function (e) {
// 通过事件对象 阻止默认行为
e.preventDefault()
})
//原来点击a标签也会跳转 阻止跳转
const oA = document.querySelector('a')
oA.addEventListener('click', function (e) {
// 通过事件对象 阻止默认行为
e.preventDefault()
})
</script>
14-5 解绑事件
事件解绑方法:
- L0注册事件方式:元素.onclick = null
- L2注册事件方式:元素.removeEventListener(‘事件类型’,处理函数名)
<script>
// L0注册方式
const btn1 = document.querySelector('.btn1')
btn1.onclick = function () {
alert('btn1被点击了')
}
// 使用重新赋值的方式替换原来的函数
btn1.onclick = null
// L2注册方式
const btn2 = document.querySelector('.btn2')
// 需要使用同一个函数的话,要将函数命名,通过名字引用
function func(){
alert('btn2被点击了')
}
// btn2.addEventListener('click', function () {
// alert('btn2被点击了')
// })
btn2.addEventListener('click', func)
// 调用removeEventListener移除事件
btn2.removeEventListener('click', func)
</script>
14-6 mouseover和mouseenter的区别
鼠标移入/移出事件中:
- mouseover/mouseout: 会有冒泡效果
- mouseenter/mouseleave:没有冒泡效果(推荐使用)
14-7 两种注册事件的区别
- 传统on注册(L0)
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖偶就可以实现事件的解绑
- 都是冒泡阶段执行的
- 事件监听注册(L2)
- 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
- 后面注册的事件不会覆盖前面注册的事件(同一个事件)
- 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
- 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
- 匿名函数无法被解绑
15.事件委托
事件委托是利用事件流的特征解决一些开发需求的知识技巧 (事件委托:将点击事件绑定到父元素身上,子元素触发事件冒泡到父元素上,从而触发事件处理函数)
- 优点:减少注册次数,可以提高程序性能
- 原理:事件委托其实是利用事件冒泡的特点。
- 给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件
- 实现:事件对象.target. tagName 可以获得真正触发事件的元素
事件委托中获取真正触发事件的子元素:
- 事件对象.target: 真正触发事件的子元素
- 事件对象.target.tagName: 真正触发事件的子元素的标签类型
<div>
<p>我是第1个p标签</p>
<p>我是第2个p标签</p>
<p>我是第3个p标签</p>
<p>我是第4个p标签</p>
<p>我是第5个p标签</p>
<h4>我是标题标签</h4>
</div>
</body>
</html>
<script>
// 需求:通过事件委托实现P标签的点击颜色变红,排除h4标签
const div = document.querySelector('div')
div.addEventListener('click', function (e) {
console.log(this)
console.log(e.target)
console.dir(e.target) //对象??里面有方法和属性?
if (e.target.tagName == 'P') {
e.target.style = 'color:red'
}
})
</script>
如何找到真正触发的那个元素?
事件对象.target.tagName
16.其他事件
16-1 页面加载事件
load事件:
- 当窗口所有内容加载完成时:window.addEventListener(“load”,function(){})
监听整个页面资源给window加
DOMContentLoaded
- 当文档结构加载完成时:document.addEventListener(“DOMContentLoaded”,function(){})
给document加。无需等待样式表、图像等完全加载
加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
有些时候需要等页面资源全部处理完了做一些事情
事件名:load
监听页面所有资源加载完毕:
给 window 添加 load 事件
//等待页面所有资源全部加载完毕,就回去执行回调函数
window.addEventListener('load', function() {
// 执行的操作
})
//等页面的DOM节点加载完毕就开始执行,给document添加DOMContentLoaded事件
16-2 页面滚动事件
滚动条在滚动的时候持续触发的事件
页面滚动事件:scroll
window.addEventListener('scroll', function() {
// xxxxx
})
//给window或document添加scroll事件,我们一般习惯给window加,监听某个元素的内部滚动直接给某个元素加即可
1.页面滚动事件-获取位置
获取元素滚动出去的距离:
- 元素.scrollTop:获取向上滚动出去的距离
- 元素.scrollLeft:获取向左滚动出去的距离
- 这两个值是可读写的
<script>
// 举例:为窗口添加滚动事件
window.addEventListener('scroll', function () {
console.log('页面滚动了')
})
// 页面向上滚动的距离
console.log(document.documentElement.scrollTop)
// 需求:求出div元素向上滚动的像素值
const div = document.querySelector('div')
div.addEventListener('scroll', function () {
console.log(div.scrollTop);
})
</script>
注意: - 获取body标签的方法:document.body
- 获取html标签的方法: document.documentElement
2.scrollTop细节
- scrollTop是可读的 ,得到的是 不带单位 的 数字
- 也可以写,即代表可以赋值,同样,不要带单位,直接给数字
document.documentElement.scrollTop=800
//可读写,意味着可以赋值,但是不要带单位
window.addEventListener('scroll', function () {
// 必须写在里面,因为页面一滚动就要获取一个最新的值
// 得到的是 不带单位 的 数字
console.log(document.documentElement.scrollTop)
})
3.总结
-
被卷去的头部或者左侧用那个属性?是否可以读取和修改?
scrollTop / scrollLeft
可以读取,也可以修改(赋值)
-
检测页面滚动的头部距离(被卷去的头部)用那个属性?
document.documentElement.scrollTop
4.页面滚动事件-滚动到指定坐标 scrollTo()
语法:
元素.scrollTo(x,y)
//让页面滚动到y轴1000像素的位置
windows.scrollTo(0,1000)
16-3 页面尺寸事件
窗口尺寸发生变化事件:resize
获取元素可见部分宽高:
- clientWidth:获取元素宽度,不包含margin、border和滚动大小
- clientHeight:获取元素宽度,不包含margin、border和滚动大小
简单理解:clientWidth和clientHeight只包含content和padding的大小
会在窗口尺寸改变的时候触发事件:
window.addEventListener('resize', function() {
// 执行的代码
})
17.元素尺寸与位置
1.元素offset系列属性
获取宽高 (可视宽高,包含元素自身设置的宽高content、padding、border)
offsetWidth: 获取元素自身宽、包含元素content、padding、border
offsetHeight:获取元素自身高、包含元素content、padding、border
获取位置(只读属性)
offsetLeft:获取元素相对带有定位的父级左偏移值,没有的话则默认以body为准
offsetTop: 获取元素相对带有定位的父级上偏移值,没有的话则默认以body为准
注意:
offset获取的元素可视状态下的值,如果盒子是隐藏的那么结果为0
2.element.getBoundingClientRect()
快速获取元素大小及其相对于视口的位置的方法(了解):
- 元素.getBoundingClientRect()
注意点:
- getBoundingClientRect() 获取的top和left值,是相对于浏览器可视窗口的,当页面出现向上滚动距离时,滚出部分不会被算在内
18.总结
属性 | 作用 | 说明 |
---|---|---|
scrollLeft和scrollTop | 被卷去的头部和左侧 | 配合页面滚动来用,可读写 |
clientWidth 和 clientHeight | 获得元素宽度和高度 | 不包含border,margin,滚动条 用于js 获取元素大小,只读属性 |
offsetWidth和offsetHeight | 获得元素宽度和高度 | 包含border、padding,滚动条等,只读 |
offsetLeft和offsetTop | 获取元素距离自己定位父级元素的左、上距离 | 获取元素位置的时候使用,只读属性 |
19.日期对象
1.实例化
在代码中发现了 new 关键字时,一般将这个操作称为实例化
创建时间对象:
new Date()
new Date(时间字符串),例如 new Date(‘2034-08-12 20:00:00’)
-
获得当前时间
const date = new Date() console.log(date)
-
获得指定日期 (括号中加字符串)
let date = new Date('2022-9-1 08:30:10') console.log(date)
2.日期对象方法
方法 | 作用 |
---|---|
getFullYear() | 获取四位年份 |
getMonth() | 获取月份,取值为 0 ~ 11 |
getDate() | 获取月份中的每一天,不同月份取值也不相同 |
getDay() | 获取星期,取值为 0 ~ 6 0指的是星期天 |
getHours() | 获取小时,取值为 0 ~ 23 |
getMinutes() | 获取分钟,取值为 0 ~ 59 |
getSeconds() | 获取秒,取值为 0 ~ 59 |
// 1.获得日期对象 方法是对象里面的方法,所以要先获取对象才可以使用
const date = new Date()
// 2.使用里面的方法
console.log(date.getFullYear())
// 月份要加1
console.log(date.getMonth() + 1)
拓展:字符串补全padstart()
padStart() —— 用于在开头位置补全字符串
padEnd() —— 用于在结束位置补全字符串
str.padStart(length,'填充的内容') // 当str的长度没有达到MaxLength,会将第二个参数填充到这个str前直到相当
str.padEnd(length,'填充的内容') // 和上面一样不过是往后添加
//不够两位补两位 前面补0
const date = new Date()
const y = date.getFullYear()
const m = (date.getMonth() + 1+'').padStart(2,'0')
const d = (date.getDate()+'').padStart(2,'0')
const h = (date.getHours()+'').padStart(2, '0')
const f = (date.getMinutes()+'').padStart(2, '0')
const s = (date.getSeconds()+'').padStart(2, '0')
3.获取日期的另外一种写法
获取时间的另外写法(了解):
日期对象.toLocaleString():2022/8/13 00:17:11
日期对象.toLocaleDateString():2022/8/13
日期对象.toLocaleTimeString():00:17:11
const div = document.querySelector('div')
const date = new Date()
div.innerHTML = date.toLocaleString() //2022/8/24 10:57:34
div.innerHTML = date.toLocaleDateString() //2022/8/24
div.innerHTML = date.toLocaleTimeString() //10:58:38
4.时间戳
什么是时间戳:
时间戳是指1970年01月01日00时00分00秒起至现在的毫秒数,它是一种特殊的计量时间的方式
算法:
将来的时间戳 - 现在的时间戳 = 剩余时间毫秒数
剩余时间毫秒数 转换为 剩余时间的 年月日时分秒 就是 倒计时时间
比如 将来时间戳 2000ms - 现在时间戳 1000ms = 1000ms
1000ms 转换为就是 0小时0分1秒
转换公式:
d = parseInt(总秒数/ 60/60 /24); // 计算天数
h = parseInt(总秒数/ 60/60 %24) // 计算小时
m = parseInt(总秒数 /60 %60 ); // 计算分数
s = parseInt(总秒数%60); // 计算当前秒数
获取时间戳的三种方式:
-
使用 getTime() 方法,必须先实例化
const date =new Date() console.log(date.getTime())
-
简写 +new Date()
console.log(+new Date()) // 指定时间的时间戳 console.log(+new Date('2022-8-29 18:30:00'))
-
使用 Date.now()
无需实例化
但是只能得到当前的时间戳, 而前面两种可以返回指定时间的时间戳
console.log(Date.now());
20.节点操作
1.DOM节点
DOM节点
- DOM树里每一个内容都称之为节点
节点类型
- 元素节点
- 所有的标签 比如 body、 div
- html 是根节点
- 属性节点
- 所有的属性 比如 href ,比如 class属性
- 文本节点
- 所有的文本 ,比如标签里面的文字
- 其他
2.查找节点
- 父节点查找:
- parentNode 属性
- 返回最近一级的父节点 找不到返回为null
- 子节点查找:
- childNodes(了解)
- 获得所有子节点、包括文本节点(空格、换行)、注释节点等
- children 属性 (重点)
- 仅获得所有元素节点 (获得标签)
- 返回所有子元素组成的伪数组,选择的是亲儿子
- childNodes(了解)
- 兄弟关系查找:
- 下一个兄弟节点
- 元素.nextElementSibling:获取元素的下一个兄弟节点
- 上一个兄弟节点
- 元素.previousElementSibling:获取元素的上一个兄弟节点
- 下一个兄弟节点
//父节点查找
<body>
<div class="dad">
<div class="baby"></div>
</div>
<script>
const baby = document.querySelector('.baby')
console.log(baby) //返回DOM对象
console.log(baby.parentNode) //返回DOM对象
</script>
</body>
//子节点查找
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
const ul = document.querySelector('ul')
console.log(ul.children)//获得的还是一个伪数组,选择的是亲儿子
//查找兄弟
const li2 = document.querySelector('ul li:nth-child(2)')
console.log(li2.nextElementSibling)//上一个兄弟
console.log(li2.previousElementSibling)//下一个兄弟
</script>
</body>
3.增加节点
3-1.创建节点
即创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点
创建元素节点方法:
创造一个新的元素节点
document.createElement('标签名')
3-2.追加节点
要想在界面看到,还得插入到某个父元素中
插入到父元素的最后一个子元素:
//插入到这个父元素的最后
父元素.appendChild(要插入的元素)
插入到父元素中某个子元素的前面
//插入到某个子元素的前面
父元素.insertBefore(要插入的元素,在哪个元素前面)
3-3 克隆节点
克隆节点的方法:
元素.cloneNode(参数):克隆当前元素节点,返回一个节点对象
参数:
false:默认值。表示克隆时不包含元素内部标签
true:表示克隆时包含内部标签
注意点:
appendChild会改变DOM树结构,直接追加一个元素,会将元素从原来的位置移除
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
const ul = document.querySelector('ul')
// 1.克隆节点 元素.cloneNode(true)
const li1 = ul.children[0].cloneNode(true)
console.log(li1)
// 2.追加节点
ul.appendChild(li1)
</script>
</body>
3-4 总结
增加节点分为两步,1:创建节点,2:追加节点
<script>
// 创造一个新的元素节点
// document.createElement('标签名')
// 1.创建节点
const div = document.createElement('div')
console.log(div)
// 2. 追加节点
document.body.appendChild(div)
const ul = document.querySelector('ul')
const li = document.createElement('li')
li.innerHTML = '我是新加的li'
// 插入到这个父元素的最后
// 父元素.appendChild(要插入的元素)
ul.appendChild(li)
// 插入到某个子元素的前面
// 父元素.insertBefore(要插入的元素,在哪个元素前面)
ul.insertBefore(li, ul.children[0])
</script>
4.删除节点
若一个节点在页面中已不需要时,可以删除它
在 JavaScript 原生DOM操作中,要删除元素必须通过父元素删除
语法:
- 父元素.removeChild(要删除的元素)
注:
如不存在父子关系则删除不成功
删除节点和隐藏节点(display:none) 有区别的: 隐藏节点还是存在的,但是删除,则从html中删除节点
<body>
<ul>
<li>哈哈</li>
</ul>
<script>
// 删除节点:父元素.removeChild(子元素)
const ul = document.querySelector('ul')
// const li = document.querySelector('li')
// ul.removeChild(li)
ul.removeChild(ul.children[0])
</script>
</body>
21.M端事件
移动端采用的是触摸操作,有别于PC端的点击事件,常见的触摸事件有:
触屏touch事件 | 说明 |
---|---|
touchstart | 手指触摸到一个DOM元素时触发 |
touchmove | 手指在一个元素上滑动时触发 |
touchend | 从一个元素上离开触摸时触发 |
BOM
1.window对象
1.BOM(浏览器对象模型)
BOM(Browser Object Model ) 是浏览器对象模型
window对象是一个全局对象,也可以说是JavaScript中的顶级对象
像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的。
所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法
window对象下的属性和方法调用的时候可以省略window
2.延时器-延时函数
JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout ,延迟函数:使一段代码延迟一段时间后执行
语法: setTimeout(执行函数,延迟毫秒数)
setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window
清除延时函数: clearTimeout( 延迟函数返回值timer )
注意点
延时器需要等待,所以后面的代码先执行
每一次调用延时器都会产生一个新的延时器
2.JS执行机制-同步和异步
JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。
同步 :
前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同步 做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。
异步
你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。
他们的本质区别: 这条流水线上各个流程的执行顺序不同。
同步任务
同步任务都在主线程上执行,形成一个执行栈。
异步任务
JS 的异步是通过回调函数实现的。 一般而言,异步任务有以下三种类型:
1、普通事件,如 click、resize 等
2、资源加载,如 load、error 等
3、定时器,包括 setInterval、setTimeout 等
异步任务相关添加到任务队列中(任务队列也称为消息队列)。
JS 执行机制
- 先执行执行栈中的同步任务。
- 异步任务放入任务队列中。
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)。
4.location对象
location数据类型是对象:包含了当前网络位置的信息
常用属性和方法:
- href: 获取当前网络地址URL,对其赋值时是地址的跳转
- search:获取当前网络地址中的携带的参数,符号 ? 和后面部分的内容
- hash:获取当前网络地址中#号后面的内容
<form action="">
<input type="text" name="uname"><br>
<input type="password" name="password"><br>
<button type="submit">提交</button>
</form>
<hr>
<a href="#num1">我是第1个小a</a>
<a href="#num2">我是第2个小a</a>
<a href="#num3">我是第3个小a</a>
<a href="#num4">我是第4个小a</a>
</body>
</html>
<script>
//经常用href,利用js的方法去跳转页面
location.href = 'http://www.baidu.com'
console.log(location.search)//?uname=11&password=1
console.log(location.hash)//#num1
</script>
方法:
- reload(): 该方法用于刷新当前页面,传入true表示强制刷新
5.navigator对象
navigator对象:用于客户端标识和状态
属性:userAgent:返回用户代理的字符串,通过 userAgent 检测浏览器的版本及平台
<script>
console.log(navigator)
// 主要应用:根据客户端标识来判断是移动端还是PC端,从而显示对应的网页
;(function () {
const userAgent = navigator.userAgent
// 验证是否为Android或iPhone
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone,则跳转至移动站点
if (android || iphone) {
location.href = 'http://m.itcast.cn'
}
})();
</script>
6.history对象
history对象:用于管理历史记录,该对象与浏览器地址栏操作相对应,例如前进、后退、历史记录等
方法:
- back(): 返回上一页
- forward(): 前进一页
- go(参数):0表示刷新、1表示前进、-1表示后退
<script>
const btn1 = document.querySelector('.btn1')
const btn2 = document.querySelector('.btn2')
const btn3 = document.querySelector('.btn3')
btn1.addEventListener('click',function(){
// 前进一步
history.forward()
})
btn2.addEventListener('click',function(){
// 后退一步
history.back()
})
btn3.addEventListener('click',function(){
// 刷新
history.go(0)
})
</script>
2.本地存储
1.localStorage
localStroage对象: 用于管理浏览器的本地存储
特点:
- 本地存储的数据是存放在硬盘中,理论上可以永久保存,除非用户删除
- 同一个域名(站点)下可以跨页面访问数据,达到共享效果
方法:
- setItem(key,value): 以键值对的方式向浏览器中存入数据
- getItem(key): 通过键名来获取对应的值
- removeItem(key): 通过键名来删除对应的值
存、取等键名都要加引号:eg:localStorage.setItem(‘键’,’值‘)
注意:
- 只能存储字符串,其他类型的数据会被转换成字符串从而进行存储
- 里面的键要加引号,里面的值在存储的时候全部转化为字符串
-
localStorage 作用是什么?
可以将数据永久存储在本地(用户的电脑), 除非手动删除,否则关闭页面也会存在
-
localStorage 存储,获取,删除的语法是什么?
存储:localStorage.setItem(key, value)
获取:localStorage.getItem(key)
删除:localStorage.removeItem(key**)**
2.sessionStorage
sessionStorage对象: 用于管理浏览器的本地存储
特点:
- sessionStorage存放在内存中,关闭窗口后数据消失
- 同一个域名(站点)下数据可以共享
方法:
- setItem(key,value): 以键值对的方式向浏览器中存入数据
- getItem(key): 通过键名来获取对应的值
- removeItem(key): 通过键名来删除对应的值
注意事项:
- 只能存储字符串,其他类型的数据会被转换成字符串从而进行存储
3.存储复杂数据类型
**问题:**本地只能存储字符串,无法存储复杂数据类型
**解决:**需要将复杂数据类型转换成JSON字符串,在存储到本地
语法:JSON.stringify(复杂数据类型)
将复杂数据转换成JSON字符串 存储 本地存储中
JSON字符串介绍:
- JSON 是一种语法,用来序列化对象、数组、数值、字符串、布尔值和 null
方法:
- JSON.stringify(复杂数据类型): 将一个对象抓换成JSON格式字符串
**问题:**因为本地存储里面取出来的是字符串,不是对象,无法直接使用
**解决:**把取出来的字符串转换为对象
语法:JSON.parse(JSON字符串)
将JSON字符串转换成对象 取出 时候使用
补充:
JSON对象中,属性和值都有引号,而且引号都是双引号
4.数组map方法 迭代数组
map 可以处理数据,并且返回新的数组 (不会改变原数组)
遍历数组的方法: 数组.map()
作用:遍历数组的每一项,并且通过回调函数的返回值组成一个新的数组作为处理后的结果返回
语法: let newArr = arr.map(function(item,index){
// code…
return item
})
<script>
// 需求:遍历数组的每一项,在后面拼接上'老师'
// map 方法也是遍历 处理数据 可以返回一个数组
let arr = ['张三', '李四', '王五', '赵六', ]
const newArr = arr.map(function (item, i) {
// console.log(item) //获得的是数组元素 arr[i]
// console.log(i) //获得下标
// 处理数据
return item + '老师'
})
console.log(newArr) //['张三老师', '李四老师', '王五老师', '赵六老师']
// 每一项加10
const arr1 = [10, 20, 30]
let newArr1 = arr1.map(function (item, i) {
return item + 10
})
console.log(newArr1)
</script>
旧数组和新数组存在一一对应的关系,**map 也称为映射。**映射是个术语,指两个元素的集之间元素相互“对应”的关系
5. 数组中join方法
作用:
将一个数组的所有元素使用指定字符连接成一个字符串并返回这个字符串
语法:
数组.join(‘字符’)
参数:
数组元素是通过参数里面指定的分隔符进行分隔的
<script>
let arr = ['张三', '李四', '王五', '赵六', ]
// 需求:将各个字符串使用 - 字符拼接成一个大的字符串
console.log(arr.join(' ')) //张三 李四 王五 赵六
console.log(arr.join('-')) //张三-李四-王五-赵六
</script>
正则表达式
1.正则表达式的使用步骤:
1. 定义规则: let reg = /正则字符/
2. 检测字符串:
reg.test(被检测的字符串)
reg.exec(被检测的字符串)
注意:
正则的test方法返回的是boolean类型的结果
正则的exec方法返回的是数组,如果匹配不成功则返回null
2.元字符?
正则中的边界符:
^表示以谁开始
$表示以谁结束
元字符中的量词:表示当前模式可以重复出现的次数
量词符号:
*零次或者重复更多次 类似 >=0 次
+一次或者重复多次 类似 >=1 次
?零次或者一次 类似 0 || 1
{n}重复n次 量词 {n} 写几,就必须出现几次
{n,}重复n次或者更多次 量词 {n,} >=n
{n,m}重复n到m次 量词 {n,m} 逗号左右两侧不能有空格 >=n && <= m
元字符之括号写法 正则匹配字符:
语法[若干字符],匹配的字符串如果是当前语法中的其中一个字符,则匹配成功,是多选一的效果
举例:
[a-z]表示a到z的26个字母
[a-zA-Z]表示26个字母大小写都可以
[0-9]表示0到9的数字
// 小练习:腾讯QQ号的规则,从10000开始产出,写出可以验证的正则表达式
let reg = /[1-9][0-9]{4,9}/
console.log(reg.test(100001));
字符类中括号的特殊写法:
匹配字符时,中括号中的^表示取反
.表示匹配换行符之外的任意字符
<script>
console.log(/[A-Z]/.test('P')) //true
// 匹配单个字符,要求不为A-Z
console.log(/[^A-Z]/.test('P')) //false
console.log(/[^A-Z]/.test('你')) //true
console.log('----------------------')
// 匹配任意非换行符号
console.log(/./.test('A')) //true
console.log(/./.test('a')) //true
console.log(/./.test('你好')) //true
console.log(/./.test('\n')) //false
</script>
正则修饰符和replace替换
:修饰符用于约束正则执行的一些细节,例如是否区分大小写、匹配范围等
语法:/正则表达式/修饰符
修饰符号:
i,是单词ignore缩写,表示正则匹配时不区分大小写
g,是单词global缩写,匹配所有满足正则表达式的结果
拓展:
正则表达式中使用|表示或的意思
<script>
console.log(/^java$/.test('java'))
console.log(/^java$/.test('JAVA'))
console.log(/^java$/i.test('JAVA'))
console.log(/^java$/i.test('Java'))
const str = 'java是一门编程语言,学完JAVA工资很高'
// 把上面语句中的java都换成前端
let result = str.replace(/java/ig, 'JavaScript')
console.log(result)
let result2=str.replace(/java|JAVA/g,'JavaScript')
console.log(result2);
</script>