1. JS的背景
W3C将网页的标准分成了三部分:
HTML 页面的结构
CSS 页面的样式
JavaScript 页面的行为
JavaScript(JS)
- 发展背景
- 创建JavaScript的初衷
- JavaScript最开始由网景公司创建的,参考了当时最火热编程语言java
- ECMA(欧洲计算机制造联合会)
- JavaScript被 ECMA组织更名为 ECMAscript(ES)
2--JS的特点
JavaScript是一门轻量级、基于面向对象的、即时编译的一门解释型语言。
- 编程语言:人与计算机交流的语言,
- 就像人们平时交流使用 中文 、 英文、 德文等
- 计算机语言按照编译模型进行分类:
- 编译型 : c语言
- 编译型语言就是先将代码整体编译后生成一个编译文件,最终交给计算机执行该文件
- 解释型 : JavaScript、python(JIT: JUST IN TIME)
- 解释型语言,不会生成一个编译文件,而是解释一行执行一行。
3--js引用方式
js引入到网页的三种方式;
- 1、行内式 通过 on+事件名 的形式书写js代码
- 这种方式可以,但不推荐!实际开发中不要使用
- 2、内部引入式 (通过html中script标签)
- 3、外部js文件的引入
- 注意:使用外部js文件引入,需要通过src属性去指定js文件的路径
- src属性它具备阻塞页面的能力,它会将src所指引的文件全部下载完毕后,才会执行后面的内容
- 所以,通常会将外部js文件的引入标签放到body的末尾
4--变量类型
基本的数据类型: Number String Boolean Symbol
两空: Null 、 Undefined
变量是保存数据的容器
JavaScript是一门弱类型语言,动态的
typeof 运算符 可以检测对应变量的类型
5--变量转换
数据类型的相互转换:
- 强制转换 (人为)
- 隐士转换(自动转换)
6--运算符
算术运算符
\+ - * / % ++(自加) --(自减) **(幂运算)
+ ++是自加1, 放在变量前,先自加1,再参与运算; 放在变量后,先参与运算,再自加1
+ --是自减1,放在变量前,先自减1,再参与运算; 放在变量后,先参与运算,再自减1
<script>
let a = 3, b = 2;
console.log(a + b); //5
console.log(a - b); //1
console.log(a * b); //6
console.log(a / b); //除法, 1.5
console.log(a % b); //模运算,取余数, 1
console.log(a ** b); //幂运算, a 的 b次方, 9
console.log(a + b++); //b++, b变量自己加1, 等效于 b = b + 1。 ++放在变量的后面,先参与当前语句的运算,然后再自加
console.log(b); // b = 3
console.log(a + ++b); //++b, b变量自己加1,++放在变量的前面, 先自加,然后再参与语句的运算
console.log(b);// b = 4
console.log(a-- + b); // -- 自减1, --放在变量之后,先参与运算, 再自己减1
console.log(a); //a = 2
console.log(--a + b); // -- 放在变量之前, 先自减1, 再参与运算
console.log(a); // a = 1
// ++ 和 -- 可以独立成为一条语句
a++;
b--
++a;
--b;
</script>
(1) 比较运算符
< == ===(全等) >= <= !=(不等于) !==(不全等)
<script>
let a = 3, b = 2;
let c = 2;
let d = "2";
console.log(a > b); //true
console.log(a < b); //false
console.log(a == b); //false
console.log(b == c); //true
console.log(b === c); //true
console.log(c == d);// true
console.log(c === d); //false, 一个是数字类型,一个是字符串类型, 所以类型不等
console.log(b >= c);//true
console.log(b <= c); //true
console.log(b != c); //false
console.log(c !== d); //true
</script>
逻辑运算符
&& 与, || 或, ! 非, ~ 异或, & 按位与, | 按位或
<script>
let a = true;
let b = false;
// && 只有当两个条件都为真的时候,返回的结果才是true, 否则(一真一假, 或者两个都为假)返回的是false
console.log(a && b); //false
console.log(a && a); //true
console.log(b && b); //false
// || 只要有一个条件为真,返回结果就为true
console.log(a || a); //true
console.log(a || b); //true
console.log(b || b); //false
//! 返回相反的结果
console.log(!a); //false
console.log(!b); //true
let m = 5;
let n = 3;
// 也可以对表达式进行逻辑运算
console.log( m>n && m==n); //false
console.log(m>n || m==n); // true
console.log(!(m > n)); //false
let i = 5;
let j = 2;
// 5: 101
// 2: 010
// &: 000
// | 111
console.log(i & j); //0
console.log(i | j); //7
// console.log(~i); //对二进制的每一位取反
</script>
三目运算符
根据第一个表达式的值,决定返回后两个结果中的哪一个(true, 返回第一个; false, 返回第二个)
//语法结构: boolean expression ? result1 : result2
let b = true;
let m = 3, n = 2;
let result = b ? 1 : 0;
console.log(result);
result = !b ? 1 : 0;
console.log(result);
//判断性别
let sex = 0; //1-男; 0-女
console.log(sex == 1 ? '男' : '女');
</script>
赋值运算符
= , +=, -=, *=, /=
<!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>
<p>innerHTML的+=拼接</p>
</div>
<script>
let i = 5; //把数值5赋给i变量
console.log(i += 2);//7, 等效于: i = i + 2
console.log(i -= 2);//5, 等效于: i = i - 2;
//+= 来拼接innerHTML
let box = document.querySelector("div");
// box.innerHTML = box.innerHTML + '<p>新的内容</p>';
box.innerHTML += '<p>新的内容</p>';
</script>
</body>
</html>
运算符的优先级
从高到低:算术运算符 > 比较运算符 > 逻辑运算符 > 三目运算符 > 赋值运算符
<script>
console.log(5 > 10 - 2);// 先算10-2, 再算 5>8, 所以返回结果是false
console.log(5 > 10 - 2 && 3 > 2);// 5 > 8 && 3 > 2, false && true, false
let b = 5 > 10 - 2 && 3 > 2;
console.log(b);
let i = 5;
</script>
条件判断
+ if(){ } 只有一个分支,要么执行,要么不执行
+ if(){ }else{ } 两个分支,一定有一个分支被执行,二选一
+ if(){ }else if(){ }else{ } 多分支(相当于if else 嵌套)
</head>
<body>
<script>
let sex = 1;//1-男; 0-女
//小括号里面是一个布尔类型的表达式,如果表达式的值为true,大括号里面的代码才会执行
if(sex == 1){
console.log('男');
}
if(sex == 0){
console.log('女');
}
//if else。 两种情况(分支),如果……就……否则……
if(sex == 1){
console.log('男');
}else{
console.log('女');
}
//18岁以下少年,18~60岁中青年;60岁以上老年
// if else if else
let age = 22;
if(age < 18){
console.log('少年');
}else if(age >= 18 && age <= 60){
console.log('中青年');
}else{
console.log('老年');
}
//给出预算,判断能买什么手机200以下, 200~1000, 1000~3000, 3000~5000, 5000以上
let money = 150;
if(money < 200){
console.log('老年机');
}else if(money >= 200 && money < 1000){
console.log('小米手机');
}else if(money >= 1000 && money < 3000){
console.log('Oppo');
}else if(money >= 3000 && money < 5000){
console.log('华为');
}else{
console.log('你是土豪,想买啥随意');
}
</script>
</body>
switch case条件判断
<script>
/*
switch()括号里面时要判断的变量或表达式
每一个case就是一个分支, case后面的值要求是一个常量或确定的值,最多只有一个case会被执行
如果default分支,前面的case都满足条件就执行default.
每个case后面都有一个break语句,只要有一个case满足条件,后面的case都不会再判断,一直执行直到break结束
*/
let menu = '文件';
switch(menu){
case '文件':
console.log('创建文件');
break;
case '保存':
console.log("保存文件");
break;
case '编辑':
console.log('修改文件');
break;
case '帮助':
console.log('显示帮助信息');
break;
default:
console.log('其它情况');
break;
}
</script>
数组
一组数据,存放数据的容器
数组基础
+ 定义数组 []
new Array()
+ 数组访问
数组名字[序号]
+ 数组的长度
数组名字.length
<script>
// 定义数组(声明数组),使用方括号
let arr1 = []; //定义空数组
// console.log(typeof arr1);
// 定义数组并赋值,元素用逗号分割
let names = ['袁金祥', '罗玉鸿', '郭鹏']; //数组放字符串
let nums = [1, 3, 5, 7, 9]; //数组放数字
let booleans = [true, false, true, false, false]; //数组放boolean
//数组放对象
let students = [
{
name:'张三',
age: 22
},
{
name: '李四',
age: 21
}
];
//数组定义,用Array对象
let arr2 = new Array(); //空数组
let arr3 = new Array(2, 4, 6, 8);
let arr4 = new Array('HTML', 'CSS', 'JavaScript');
//访问(读写)数组
console.log(names[0]); //读取数组元素: 数组名[下标],下标从0开始
console.log(students[0].name);
names[1] = '罗玉宏';//数组元素的赋值
//数组的长度:length
console.log(names.length);//数组长度(元素的个数)
</script>
数组操作
+ push() 在数组的后面添加元素
+ pop() 删除并返回数组最后一个元素
+ unshift() 在数组的前面插入一个元素
+ shift() 删除并返回数组第一个元素
+ splice(start, count, element) 删除指定元素或在指定位置插入新的元素
第一个参数:起始位置的下标
第二个参数:要删除的元素的个数(0就不删除)
第三个参数:要插入的新元素,这个参数可以省略
+ concat() 两个数组拼接为一个新数组
+ join() 把数组的元素用参数指定的字符串连接成一个字符串并返回
+ includes() 判断是否包含元素,包含返回true; 不包含返回false
+ indexOf() 返回指定元素的下标 ,如果不存在返回-1
+ lastIndexOf() 返回指定元素在数组中最后一次出现的下标,如果不存在返回-1
<script>
let names = [];
console.log(names.length);
// push()方法往数组的最后添加元素
names.push('Tom');
names.push('Jack');
names.push(88); //js的数组元素的类型可以不同
console.log(names.length);
console.log(names);
// pop() 方法返回数组的最后一个元素,并且把最后一个元素从数组中删掉
console.log(names.pop());
console.log(names.pop());
console.log(names.length);
//unshift()方法在数组的前面插入元素
names.unshift('张三');
names.unshift('李四');
console.log(names);
//shift()返回数组的第一个元素,并且把第一个元素从数组中删掉
console.log(names.shift());
console.log(names);
names.push('小红');
names.push('小明');
console.log(names);
/*
splice() 一是可以删除元素,二是插入新元素
第一个参数: 起始位置的下标
第二个参数: 要删除的元素的个数(从当前下标往后面删除),如果为0就是不删
第三个参数: 插入的新元素,可以省略不写(不会插入)
*/
names.splice(1, 1, '老王'); //相当于替换
console.log(names);
names.splice(1, 2); //删除下标1开始的元素,连续删除两个
console.log(names);
names.splice(1, 0, '刘某');//在指定位置插入元素
console.log(names);
// concat() 连接两个数组,并返回一个新数组。不影响原来的两个数组
let arr1 = [1, 3, 5];
let arr2 = [2, 4, 6];
let arrNew = arr1.concat(arr2);
console.log(arr1);
console.log(arr2);
console.log(arrNew);
let arrNew2 = arrNew.concat([100, 101, 102])
console.log(arrNew2);
// 克隆新数组
let arrNew3 = arrNew2.concat([]);
console.log(arrNew3);
//join() 把数组元素用指定的分隔符号(参数的字符串)连接成一个字符串
let str = names.join(",");
console.log(str);
console.log(names);
//includes() 判断数组中是否包含指定的元素,包含返回true;没有包含返回false
console.log(names.includes('小明'));
//indexOf() 判断数组中是否包含指定元素,包含返回这个元素的下标;没有包含返回-1
console.log(names.indexOf('小明'));
names.push('张三');
console.log(names);
//lastIndexOf() 返回数组中最后一个指定元素的下标,如果没有返回-1
console.log(names.lastIndexOf('张三'));
</script>
判断数组类型
// Array.isArray() 判断一个变量是否是数组,是返回true; 不是返回false
console.log(Array.isArray(names));
let arr5 = 100;
console.log(Array.isArray(arr5));
二维(多维)数组
//二维数组的访问,用两个下标,第一个下标是外层数组的下标,第二个下标是内层数组的下标
console.log(groups[0][1]);
groups[0][1] = '2';
循环
## for循环
for(循环变量; 循环条件; 循环遍历改变){
//循环体
}
执行顺序:
首先执行1;
2, 3, 4 周而复始循环执行,直到循环条件为false才结束
<!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>
<ul>
<li>文件</li>
<li>编辑</li>
<li>帮助</li>
</ul>
<script>
// 重复做相同的事情,程序中循环重复执行同一段代码
for(let i = 0; i < 5; i++){
console.log(i + '循环代码');
}
//循环变量的改变也可以是减少
for(let i = 5; i > 0; i--){
console.log(i);
}
// 循环变量的定义可以放到for循环之前;循环变量的改变可以放到循环体;甚至循环条件都可以不写(默认为true,死循环)
// 但是分号不能省略
let i = 0;
for(; i < 5;){
console.log(i);
i++;
}
// 多个循环变量
for(let i = 0, j = 10; i < j; i++, j--){
console.log(i + ', ' + j);
}
// 练习:计算从1加到100的结果
let sum = 0;
for(let i = 1; i <= 100; i++){
sum += i;
}
console.log(sum);
// 循环终止
for(let i = 0; i < 10; i++){
console.log(i);
if(i == 8){
break;// 结束循环
}
}
// 跳过某次循环
for(let i = 0; i < 10; i++){
if(i == 5){
continue; //跳过本轮循环,继续执行下一轮循环
}
console.log(i);
}
// 练习:for循环遍历数组
let names = ['张三', '李四', '王五'];
for(let i = 0; i < names.length; i++){
console.log(names[i]);
}
// 通过循环对标签添加事件
let lis = document.querySelectorAll("ul li");//选择的标签返回数组
// console.log(lis);
// 循环标签数组,添加事件
for(let i = 0; i < lis.length; i++){
lis[i].addEventListener('click', function(){
alert(this.innerText);
})
}
</script>
</body>
</html>
循环嵌套
<script>
// 循环嵌套(两层(重)循环),外层循环没执行一次,内层的循环要全部执行一遍,总的执行次数是外层次数乘以内层次数
for(let i = 0; i < 5; i++){
for(let j = 0; j < 5; j++){
console.log(i + ", " + j);
}
}
// 遍历二维数组
let arr = [
[1, 3, 5, 7, 9],
[2, 4, 6, 8, 10],
]
for(let i = 0; i < arr.length; i++){
for(let j = 0; j < arr[i].length; j++){
console.log(arr[i][j]);//i是外层数组的下标,j是内层数组的下标
}
}
// 练习:用*打印10乘5的矩阵
for(let i = 0; i < 5; i++){
for(let j = 0; j < 10; j++){
document.write('*');
}
document.write("<br>");
}
</script>
for in循环
+ 用于遍历数组
+ 用于遍历对象的属性
<!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></div>
<script>
// for in用于数组遍历
let arr = [1, 5, 16, 23, 55];
// in 关键字后面是数组, 前面自己定义一个变量,它用于接收循环出来的序号
for(let index in arr){
console.log(arr[index]); //用序号作为下标去读取数组的元素
}
// for in 用于对象的属性遍历
let student = {
name: '小明',
sex: '男',
age: 18
}
// in 后面是对象,前面定义的变量接受对象的属性
for(let key in student){
// console.log(key);
console.log(student[key]); //如果对象的属性是个变量,用[]来读取属性的值
}
// 练习:遍历标签对象的style的属性
let d = document.querySelector("div");
// console.log(typeof d.style);
for(let c in d.style){
console.log(c);
}
</script>
</body>
</html>
for of循环
用于数组遍历,遍历出来就是数组元素
不能用于对象属性遍历
<!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>
<script>
let arr = ['a', 'b', 'c', 'd'];
// for of 返回的是数组的元素
for(let el of arr){
console.log(el);
}
// for of不能用于对象属性遍历,它只能用于迭代类型的数据,比如数组,选择的一组标签
</script>
</body>
</html>
while循环
+ while(){ } 条件为true,继续执行循环;条件为false结束循环
+ do{ }while() 先执行循环,再判断条件,条件为true,继续执行循环;条件为false结束循环
while循环一般用于不知道循环次数的情况
while循环也可以用continue和break语句
<script>
// 找出10开始的,能被7整除的第一个数
let i = 10;
// while循环括号里只有一个布尔类型的表达式,当表达式的值为true,循环继续执行;为false,循环结束
while( i % 7 != 0){
i++;
console.log(i);
}
// 用while循环遍历数组
let arr1 = [2, 4, 6, 8];
let j = 0;
while(j < arr1.length){
console.log(arr1[j]);
j++;
}
// 打印100以内的奇数
let m = 1;
while(true){ //死循环,循环体一定要有break语句
if(m % 2 != 0){
console.log(m);
}
m++;
if(m > 100){
break; //终止循环
}
}
// do while循环,先执行循环,再判断条件,条件为true继续循环,为false结束
let x;
do{
x = Math.random();//产生一个0~1之间的随机数
console.log(x);
}while(x < 0.5)
</script>
数组的排序和遍历
+ sort() 排序
+ reverse() 倒序
+ forEach() 遍历
+ every() 遍历
+ map() 映射
+ filter() 过滤
<script>
let arrStr = ['aa', 'bb', 'cc', 'xx', 'dd'];
//sort() 排序,会影响原数组
let arrSort = arrStr.sort();
console.log(arrSort);
console.log(arrStr);
// reverse() 颠倒数组元素
arrStr.reverse();
console.log(arrStr);
// 练习:用reverse颠倒单词的字母
let str = "Hello";
let arrHello = str.split("");//按照指定的字符串拆分字符串,返回数组
arrHello.reverse();
str = arrHello.join(""); //把数组元素取出来,用指定的分隔符拼接成一个字符串
console.log(str);
// forEach() 遍历数组
arrStr.forEach((el, index, arr) => {
// console.log(el);
// console.log(index);
// console.log(arr);
})
// every() 遍历数组。靠return结果决定是否继续执行:return true继续执行;return false结束循环
arrStr.every((el, index, arr) => {
console.log(el);
console.log(index);
console.log(arr);
if(index > 2){
return false;
}
return true;
})
// map() 映射出一个新的数组(把原数组的每个元素进行转换,返回一个新元素)
let arrNum = [1, 3, 5, 7, 4, 8];
let arrNumNew = arrNum.map((el, index, arr) => {
return el * 2;
})
console.log(arrNum);
console.log(arrNumNew);
//filter() 过滤数组,按条件返回一个新数组
let arrFilter = arrNum.filter((el, index, arr) => {
if(el % 2 != 0){
return true; //当前这个元素会返回给新数组
}else{
return false; //不返回这个元素到新数组
}
})
console.log(arrFilter);
</script>
数组查找的方法
+ find() 查找符合条件的第一个对象,并返回。没有找到返回undefined, 如果找到了,后面的元素不再遍历
+ findIndex() 类似与find,返回的元素的下标,如果没有找到返回-1
+ some() 查找是否有符合条件的元素,有返回true, 没有返回false
<script>
let students = [
{
name: '小红',
age: 16
},
{
name: '小明',
age: 18
},
{
name: '小王',
age: 18
}
]
// 查找年龄18岁的同学
// find() 查找数组中第一个符合条件的元素并返回(如果找到了就不会再遍历后面的元素).没找到返回undefined
let result = students.find((el, index, arr) => {
console.log(el.name);
return el.age == 18; //返回true就是找到了
})
console.log(result);
// findIndex() 返回符合条件的元素的下标. 没有查找到,返回-1
result = students.findIndex((el, index, arr) => {
return el.age == 16;
})
console.log(result);
//some() 查找数组中是否有符合条件的元素,有就返回true, 没有返回false
result = students.some((el, index, arr) => {
return el.age == 18;
})
console.log(result);
</script>
```
函数
程序中函数就是能完成特定功能的一段代码的集合
使用函数目的避免代码重复,实现重用
函数定义和使用
+ 没有参数,没有返回值的函数定义和调用
//函数定义
//语法: function 函数名(){
//
// }
function sayHello(){
console.log('Hello HQYJ');
}
// 函数调用
sayHello();
sayHello();
有参数的函数定义和调用
// 有参数的函数定义和使用
// name 形式参数,简称形参,参数名随便起名
// 如果有多个参数,参数之间用逗号分割
function helloArg(name){
console.log('Hello ' + name);
}
// 调用函数是传递的参数叫实际参数,简称实参
helloArg('Tom');
helloArg('张三');
helloArg('李四', 100);//函数调用时可以传跟形参数量一致的参数,也可以少传,也可以多传
// 多个参数的函数定义,多个参数用逗号分割
function add(num1, num2){
let sum = num1 + num2;
console.log(sum);
}
add(3, 2);//调用多参数函数,实参也是用逗号分割
```
+ 有返回值的函数的定义和使用
console.log('具有返回值的函数的定义');
// 具有返回值得函数的定义
// return 返回函数执行之后的结果,并且结束函数(return 后面的语句不会执行)
function addReturn(num1, num2){
let sum = num1 + num2;
return sum; //函数的返回值
console.log(sum);
// return num1 + num2;
}
// 调用有返回值的函数后,它会返回一个结果,这个结果可以用于给变量赋值,或者在表达式中参与运算
let sum = addReturn(10, 20);
console.log(100 + addReturn(5, 8));
// 函数中有多个return语句,逻辑上保证只有一个return会被执行
function checkSex(sex){
if(sex == 1){
return '男';
}else{
return '女';
}
}
```
+ 形参的默认值
// 函数的形参可以设置默认值,给形参赋初始值,如果调用函数时没有传参数,就使用默认值
function sayMsg(msg = 'HQYJ'){
console.log('hello ' + msg);
}
sayMsg();
sayMsg('2');
函数定义的其它场景
// 函数定义的其它场景
// 不需要写函数名(匿名函数),用变量名调用
const sub = function(a, b){
return a - b;
}
console.log(sub(5, 2));
//匿名函数用于事件
let btn = document.querySelector('button');
btn.onclick = function(){
alert('按钮被点击');
}
btn.addEventListener('mouseover', function(){
alert('鼠标移入');
})
// 匿名函数用于事件的方法
let student = {
name: '张三',
age:22,
study:function(){
console.log('张三正在努力学习');
}
}
student.study();
// 箭头函数(朗姆达表达式)定义
const addLamda = (a, b) => {
return a + b;
}
console.log(addLamda(4, 6));
```
传值调用和传引用调用
+ 传值调用
在函数中修改形参的值,不影响调用函数的实参
+ 传引用调用
在函数中修改形参的值,会影响实参的值
<script>
// 传值(数字,字符串,boolean)调用, 在函数中修改形参的值,不影响调用函数的实参
function add(a, b){
a++;
b++;
return a + b;
}
let x = 6, y = 8;
let sum = add(x, y);
console.log(sum);
console.log('x=' + x + ', y=' + y);
// 传引用(对象)调用,在函数中修改形参的值,会影响实参的值
let student = {
name: '小红',
age: 22
}
function changeAge(obj){
obj.age += 2;
}
changeAge(student);
console.log(student);
</script>
函数预编译
js在执行之前,先要扫描整个Js代码,会对函数进行预编译处理(登记),还会对函数的形参赋值
预编译完成之后,才从上到下逐行执行js代码,所以调用函数的代码可以放到任意位置,不会像变量一样出现未定义的情况
<script>
// console.log(i); //变量的访问是不能在变量定义之前
let i = 5;
msg();//函数调用是可以放在函数定义之前
function msg(){
console.log('some message');
}
</script>
变量的作用域
作用域(scope)就是有效范围
+ 全局:定义在script标签下的的变量是全局变量,它能被当前js任意位置可访问
+ 局部:定义在代码块内的变量称为局部变量,有效范围是当前代码块内
+ 未定义直接赋值的变量默认是全局变量(不管写在什么位置)
<script>
//直接在script标签下定义的变量,称为全局变量,在当前Js代码的任意位置都可以访问
let i = 0;
if(i == 0){
// 在代码块里定义的变量称为局部变量,只能在当前代码块中能访问,在其他地方不能访问
let x = 100;
console.log(i);//可以访问
console.log(x);
}
for(let j = 0; j < 5; j++){
console.log(i);//可以访问
//console.log(x);//x 不可访问
}
function test(){
let m = 66;
if(true){
console.log(m);//父代码块中的变量可以被子代码块访问
}
n = 88; //为声明的变量直接赋值,会变成全局变量
var p = 99;
console.log(i);//可以访问
}
test();
//console.log(m);//m不可访问
console.log(n); //可以访问
console.log(p); //不可访问
</script>
自调用函数
<script>
// 函数自调用
// 语法结构:(函数定义)()
// 如上格式定义的函数不需要调用,它会自动执行(执行一次)。函数可以用匿名函数
(function(){
console.log('Hello');
})()
(() => {
console.log('箭头函数');
})();
</script>
闭包
## 变量污染的问
如果一个Html页面中引入多个Js文件,这些文件中定义同名的全局变量,就造成变量污染(冲突,重复定义)
这就会导致页面报异常,所以引入闭包的概念,作用就是把变量封闭在自己函数内,别的代码无权访问,这样就不会冲
const 函数名 = ( ()=> {
let 变量1;
let 变量2;
return (参数1, 参数2) => {
//函数代码
}
})()
```
案例
一个Html文件中引入两个闭包函数文件
+ html
```html
<!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>
<!-- 引入独立的js文件 -->
<script src="./js/sum.js"></script>
<script src="./js/avg.js"></script>
<script>
// 如果引入不同的Js文件,里面出现相同的全局变量名,
// 这种现象称为全局变量的污染
// 解决办法使用闭包的写法: 在函数外面套一层自调用函数
let arr = [23, 56, 18, 57, 90];
console.log(sum(arr));
console.log(avg(arr));
</script>
</body>
</html>
```
+ sum.js
```js
const sum = (() =>{
let result = 0;
return function(arr){
arr.forEach(element => {
result += element
});
return result;
};
})();
```
+ avg.js
```js
const avg = (() => {
let result = 0;
return (arr) => {
arr.forEach(element => {
result += element;
});
result /= arr.length;
return result;
}
})()
字符串
<script>
let str = 'hello world !!!';
// 字符串的长度
console.log(str.length);
// 用下标来访问字符串中字符
console.log(str[6]);//w
// charAt() 返回指定位置的字符,参数是下标
console.log(str.charAt(8));//r
// split() 按指定的字符串拆分字符串,返回数组
let arr1 = str.split(" ");//['hello', 'world', '!!!']
console.log(arr1);
console.log(arr1.join('_'));
// replace() 替换字符串中第一次出现的指定字符串为新字符串。第一个参数是要替换字符串,第二个参数是新字符串
console.log(str.replace('!', '.'));//hello world .!!
// replaceAll()类似replace(), 替换所有相同的字符串
console.log(str.replaceAll('!', '.')); //hello world ...
let strBlank = ' ' + str + ' ';
console.log(strBlank);
// 去掉字符串左右的空格
console.log(strBlank.trim());//hello world !!!
// substring(), 截取字符串,第一个参数是起始位置,第二个参数是结束位置(不包含)
console.log(str.substring(6, 11));//world
// 第二个参数可以省略,省略后截取到字符串末尾
console.log(str.substring(6));//world !!!
//substr() 截取字符串,第一个参数是起始位置,第二个参数要截取字符的个数
console.log(str.substr(6, 5));//world
// startsWith() 判断字符串是否是以指定字符串开头,是返回true, 不是返回false
console.log(str.startsWith('hello'));// true
// endsWith() 判断字符串是否以指定的字符串结束,是返回true,不是返回false
console.log(str.endsWith('!!!'));//true
// indexOf() 返回指定字符串出现的起始位置(第一次出现的位置)
console.log(str.indexOf('o'));//4
// lastIndexOf() 返回指定字符串最后一次出现的起始位置
console.log(str.lastIndexOf('o'));//7
// toUpperCase() 字符串中字母全部转大写
console.log(str.toUpperCase());//HELLO WORLD !!!
// toLowerCase() 字符串中字母转小写
console.log(str.toLowerCase());//hello world !!!
</script>
Map和Set
<!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></body>
<script>
// Map 的使用类似 json 对象
// 声明 Map
let obj = {
a: 1,
b: 2,
};
let map = new Map([
["a", 1],
["b", 2],
]);
console.log(map);
// 读值
console.log(map.get("a"));
// 赋值
map.set("c", 3);
console.log(map);
// has 判断是否存在某个 key
console.log("a" in obj);
console.log("b" in map);
console.log(map.has("b"));
// 迭代map
let keys = map.keys(); // => keys 方法将返回一个迭代器
console.log(keys);
// k 是一个零时变量 用于存储迭代器next方法返回的每一个key
// k 数据结构为 { value: 'key名称', done: '是否迭代完成' }
let k;
// 迭代器都包含一个 next 方法,用于迭代下一个
while (!(k = keys.next()).done) {
let v = map.get(k.value); // 通过 key 读取 value
console.log(`key: ${k.value}; value: ${v}`);
}
// 可以使用 forEach 迭代
map.forEach((value, key, self) => {
// value 值
// key 键
// self map自己
console.log(`value: ${value}; key: ${key}`);
console.log(self);
});
// for of 可以用来迭代存在迭代器的对象
// for of 遍历 Map
for (const kv of map) {
console.log(kv);
let key = kv[0];
let value = kv[1];
console.log(key, value);
}
// Set 是不重复的数据集,若存入多个重复数据会自动去重
let set = new Set([
1,
1,
2,
23,
3,
3,
4,
"hello",
"hello",
true,
true,
false,
]);
console.log(set);
// 添加
set.add(123);
console.log(set);
// 读取set长度
console.log(set.size);
// 删除
set.delete("hello");
console.log(set);
// 判断是否存在某个值
console.log(set.has(5));
// 迭代 set
let vs = set.values(); // values 方法返回一个迭代器
let v;
while (!(v = vs.next()).done) {
console.log(v); // v 的内容和 map 相同为 {value: '集合中的每个成员', done:'是否迭代完成'}
console.log(v.value); // v.value 就是集合中的成员
}
// for of 遍历 Set
for (const value of set) {
console.log(value);
}
console.log("set foreach");
// foreach 迭代 set
set.forEach((value, key, self) => {
console.log(value);
console.log(key);
console.log(self);
});
// 通过 set 转换成数组
// 可以通过这种方法来让数组去重
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
set = new Set(arr);
// 将set转换回数组
arr = Array.from(set);
console.log(arr);
</script>
</html>
ES6语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<body></body>
<script>
// let 关键字
let x = 1;
var y = 1;
// let 比 var 拥有更明确的作用域,它只会在声明它的代码块中可访问
for (var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
});
}
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
});
}
// ... 三个点能将数组和对象中的内容拆出来并用 “,” 逗号隔开
let obj = { a: 1, b: 2, c: 3 };
let obj2 = { x: 1, y: 2, z: 3 };
// ...obj => a: 1, b: 2, c: 3
let arr = [1, 2, 3];
let arr2 = [4, 5, 6];
// ...arr => 1, 2, 3
// 使用 ... 扩展对象
obj = { ...obj, ...obj2 };
console.log(obj);
// 使用 ... 克隆对象
let result = { ...obj };
// 使用 ... 扩展数组
arr = [...arr, ...arr2];
console.log(arr);
// 使用 ... 克隆数组
result = [...arr2];
console.log(result);
// lamda 表达式 也称 箭头函数
let fn = () => {};
// 带参数
fn = (a, b, c) => {};
// 如果参数只有一个,可以省略括号
fn = (x) => {};
// 箭头函数的箭头后面可以省略花括号
fn = () => "hello world"; // 箭头后面的内容就是函数返回值
fn = () => ({ x: 1, y: 2 }); // 返回对象要在箭头后面打圆括号
// const 定义常量 常量是readonly只读的属性
// 一旦初始化后 就不能修改其值
const PI = Math.PI;
// 模板字符串
let d = new Date();
let year = d.getFullYear();
let month = d.getMonth() + 1;
let date = d.getDate();
let str = `${year}/${month}/${date}`;
console.log(str);
// 模板字符串换行输出
str = `
123
456
789
hello
`;
console.log(str);
// 解构
obj = { a: 1, b: 2 };
// 结构赋值
// 对象的解构赋值
// 若 obj 存在属性 a 则将 obj.a 赋值给变量 a
// 同理 若 obj 存在属性 b 则将 obj.b 赋值给变量 b
let { a, b } = obj;
console.log(a, b);
// 数组的结构赋值
// 数组会将对应索引位置的成员赋值给变量
// 例如 此处的 0 号成员 赋值给变量 a
// 1 号成员赋值给变量 b
arr = ["a", "b", "c"];
[a, b] = arr;
console.log(a, b);
// 函数参数中使用解构
function logUser({ name, sex }, age) {
console.log(`${name}: ${sex}: ${age}`);
}
function logUser2([name, sex], age) {
console.log(`${name}: ${sex}: ${age}`);
}
logUser({ name: "张三", sex: "male" }, 16);
logUser2(["李四", "female"], 24);
// 函数的 rest 参数(parameter)
// rest 参数 仅出现在参数列表的最后一个位置上
function logNames(a, b, ...c) {
// 变量 c 将作为数组使用
console.log(a, b, c);
}
// rest 参数的位置 可以传入任意多个参数
// 从 rest 参数开始 所有的参数值都会被存入到数组 c 中
logNames("x", "y", "z", "a", "b", "c");
// 定义对象属性和方法的简写
let name = "隔壁老王";
let age = 30;
let sayHello = () => {
console.log("hello");
};
let user = {
// 当属性名和属性值的变量名相同时,可以省略冒号后面的内容
name,
age,
sayHello,
// sayGoodBye: function () {},
// 对象方法可以简写如下:
// 去掉 : 冒号和函数的关键字 function
sayGoodBye() {
console.log("good bye");
},
};
// ? 问好语法
// 判断是否存在,存在就执行,否则不执行
user = null;
user?.sayHello();
// user?sayHello() 等价于下列代码
if (user) {
user.sayHello();
}
</script>
</html>
delete关键字
<script>
// delete 删除 对象属性,对象行为
let obj = {
x: 1,
y: 2,
sayGreet: () => {
console.log("greeting");
},
};
// delete 语法:
// delete object.attrName
// 删除属性如下
delete obj.x;
delete obj.sayGreet;
console.log(obj);
// 删除数组成员
let arr = [1, 2, 3];
delete arr[1];
console.log(arr);
// 一般不会用 delete 删除数组成员 因为会留下一个 empty 占位
// 所以请使用 arr.splice 删除数组成员
// 不要直接删除变量 这是不规范的
// 若要想删除变量 请赋值 undefined 代替
obj = undefined;
</script>
instanceof关键字
<script>
// instanceof : 用于判断一个实例是否属于某个类型
// 语法:object instanceof class
// 该表达式返回一个布尔值,true:代表object属于class的类型,否则不属于
class Human {
name;
sex;
constructor(name, sex) {
this.name = name;
this.sex = sex;
}
}
let xiaoHong = new Human("小红", "女");
let zhangSan = new Object();
// 判断小红是否属于人类
console.log(xiaoHong instanceof Human); // --> true
console.log(xiaoHong instanceof Object); // --> true
// 判断张三是否属于人类
console.log(zhangSan instanceof Human); // --> false
console.log(zhangSan instanceof Object); // --> true
// instanceof 一般用于服务器上,用来判断自定义异常类型
// 自定义异常类
// 类型异常
class TypeError extends Error {
constructor(msg) {
super(msg);
}
}
// 除数为0异常
class ChuShuIsZeroError extends Error {
constructor() {
super("除数不能为 0");
}
}
function div(x, y) {
if (typeof x !== "number") {
// 若 x 不是数字抛出类型异常
throw new TypeError("x 不是数字");
}
if (typeof y !== "number") {
// 若 y 不是数字抛出类型异常
throw new TypeError("y 不是数字");
}
if (y === 0) {
throw new ChuShuIsZeroError();
}
return x / y;
}
try {
let result = div(1, 2);
console.log(result);
} catch (error) {
// 使用 instanceof 判断异常对象的类型
if (error instanceof TypeError) {
console.log("参数类型异常");
console.log(error.message);
} else if (error instanceof ChuShuIsZeroError) {
console.log("除数为0");
console.log(error.message);
} else {
console.log(error);
}
}
</script>
lamda表达式返回值的简写
<script>
// 声明一个箭头函数
let add = (x, y) => {
return x + y;
};
// 为了方便,可以简写返回值
// 省略函数的花括号 代表直接返回箭头后的内容
add = (x, y) => x + y;
console.log(add(1, 2));
// 箭头函数返回对象
add = (x, y) => {
return {
he: x + y,
};
};
// 简写
add = (x, y) => ({ he: x + y });
</script>
原型链
<script>
// 什么是原型链?
// 原型链是由原型构成的一个层层访问的引用链条
function A() {
this.name = "张三";
}
function B() {
this.age = 17;
}
// B 继承 A
B.prototype = new A();
function C() {
this.sex = "male";
}
// C 继承 B
C.prototype = new B();
let c = new C();
console.log(c.sex);
console.log(c.age); // c.__proto__.age
console.log(c.name); // c.__proto__.__proto__.name
// 当访问子类实例的属性时,js先会从子类实例中查找是否存在该属性,若不存在则向其原型中查找是否存在该属性,若不存在就继续从原型的原型中查找,直到查到或找不到为止
</script>
。this关键字
<script>
// 参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this
// this 关键字代表什么?
// 非严格模式下,根据不同的场景,this关键字代表东西不同,有以下几种情况
// 非严格模式下的this
// js 执行上下文中的 this
console.log(this); // => window (当前环境下的全局变量)
// 函数内的this
function fn() {
console.log(this); // => window
}
// 对象方法的this
let obj = {
name: "张三",
fn: function () {
console.log(this); // 对象方法中的this 代表该对象自身
fn2();
function fn2() {
console.log("fn2");
console.log(this); // => window
}
},
fn2: () => {
console.log(this);
},
};
// 对象方法的this
class A {
fn() {
console.log(this);
fn2();
function fn2() {
console.log("fn2");
console.log(this); // => undefined
}
}
}
// 对象方法中的this始终是自己
let a = new A();
a.fn();
// 声明变量存储 A 类的 fn 函数,再次调用并观察 this
// 给变量赋值函数值,默认情况下 this 为 undefined
let fnOfA = a.fn;
fnOfA();
// 有些情况下,回调函数中的this可能被其他函数赋值,所以this不是undefined
// 此处 事件回调函数被 addEventListener 赋值了其中的 this
// 所以这里的this指的是绑定事件的那个dom对象
const btn = document.querySelector("button");
btn.addEventListener("click", function () {
console.log(this);
});
// 总结:
// 1. js 执行上下文 => window
// 2. 执行上下文下声明的函数内 this => window
// 3. 方法内的this始终是方法所在对象自身
// 4. this 可能被其他函数赋值 例如 addEventListener 这种时候 this 既不是 window 也不是 undefined,它取决于函数 addEventListener 自己
</script> -->
<script>
"use strict";
// 结论:
// 1. js 执行上下文 => window
// 2. 只要是函数 this 就是 undefined
// 3. 方法内的this始终是方法所在对象自身
// 4. this 可能被其他函数赋值 例如 addEventListener 这种时候 this 既不是 window 也不是 undefined,它取决于函数 addEventListener 自己
console.log(this); // => window
function fn() {
console.log(this); // => undefined
}
let obj = {
fn() {
console.log(this); // => obj
fn2();
function fn2() {
console.log(this); // => undefined
}
},
};
class A {
fn() {
console.log(this); // => a
fn2();
function fn2() {
console.log(this); // => undefined
}
}
}
let a = new A()
// a.fn()
let fnOfA = a.fn
fnOfA()
</script>