JavaScript
此文章适用于有编程基础的朋友想要学习JavaScript,其中缺少一部分与其它编程相同的内容(例如:for循环等),对于编程零基础朋友的不友好
文章目录
书写位置
JavaScript在HTML中可以存放于<head>
标签或<body>
标签中,但最好用于<body>
标签的末尾,这样可以先加载网页内容再加载JavaScript
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>console.log</title>
</head>
<body>
<h1>Hello World</h1>
<script>
console.log("Hello World");
</script>
</body>
</html>
但一个优秀的前端开发者会将JavaScript单独拎出来
代码如下:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JS单独文件</title>
</head>
<body>
<h1>Hello World</h1>
<script src="index.JS"></script>
</body>
</html>
index.JS
console.log("Hello World");
输出结果:
算术运算符
运算符 | 说明 |
---|---|
= | 赋值运算符 |
== | 等同于,内容相等,其数据类型可以不等 |
=== | 恒等于,内容和数据类型都要相等 |
!= | 不等于,相反于==,即 ==为真时,!=为假 |
!== | 不等于,相反于===,即 === 为真时,!== 为假 |
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
&& | 与,0&&0=0,0&&1=0,1&&0=0,只有1&&1=1 |
|| | 或,0||1=1,1||0=1,1||1=1,只有0||0=0 |
! | 非,!1=0,!0=1 |
或
||
除了判断,也可以用于赋值
c
o
n
s
t
n
u
m
=
0
∣
∣
5
const num = 0 || 5
constnum=0∣∣5
可以得到 num = 5,适用于赋值给如果前面条件不成立,则赋值后面内容
数据类型
据类型种类:
- String:字符串类型
- Number:数字类型(整数,小数,正数,负数),特殊:即为数字类型但表示不是数字的NaN
- Boolean:布尔类型
- NULL:空值
- undefined:不定义值,即表示一个变量最原始的状态,而非人为操作的结果
代码如下:
const userName = "Jack"; //字符串
const age = 20; //数值
const rate = 4.5; //数值
const isCool = true; //布尔值
const x = null; //空值
const y = undefined; //不定义值
let z; //不定义值
console.log(typeof userName);
console.log(typeof age);
console.log(typeof rate);
console.log(typeof isCool);
console.log(typeof x);
console.log(typeof y);
console.log(typeof z);
结果如下:
string
number
number
boolean
object
undefined
undefined
String类型
通过单引号(’ '),双引号(" ")或反引号(``)包裹的数据都叫字符串,单引号和双引号没有区别,推荐在JavaScript使用单引号
连接方式
代码如下:
let userName = 'John';
let sex = "男";
let age = 18;
console.log('My name is ' + userName + ' I am ' + age + ' years old and I am ' + sex);
console.log("My name is " + userName + " I am " + age + " years old and I am " + sex);
console.log(`My name is ${userName} I am ${age} years old and I am ${sex}`); //模板字符串
console.log('我是"Tony老师"');
结果如下:
My name is John I am 18 years old and I am 男
My name is John I am 18 years old and I am 男
My name is John I am 18 years old and I am 男
我是"Tony老师"
复杂数据类型
数组和对象是复杂数据类型,其变量存储的是地址而不是值
代码如下:
let obj = {
person: "John",
};
let obj2 = obj;
obj2.person = "Tom";
console.log(obj.person);
let arr1 = [1, 2, 3, 4];
let arr2 = arr1;
arr2[0] = 999;
console.log(arr1[0]);
结果如下:
Tom
999
数据类型转换
隐式转换
某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换
规则:
- + 号两边只要有一个是字符串,都会把另外一个转成字符串
- 除了 + 号以外的算数运算符,比如- * / 等都会将把数据转换成数字类型
技巧:
- + 号作为正号解析可以转换成数字型
- 任何数据和字符串相加结果都是字符串
代码如下:
const str = "John";
console.log("name" + str);
console.log("pink" + 1);
console.log('2' + 2);
console.log("po" * 1);
console.log('78' * 3);
console.log(+12);
console.log(+'123'); //+号做正号,将字符串转换为数字型
console.log(typeof +'123');
console.log(+'11'+11); //先将前面的字符串转换成数字类型再与后面的数字相加
结果如下:
nameJohn
pink1
22
NaN //代表不是一个数字
234
12
123 //数字类型
number
22 //数字类型
显式转换
数字转换
Number
:转换为数字类型parseInt
:只保留整数parseFloat
:保留小数
Number(转换为数字类型)
强制转换成数字类型
代码如下:
/* 字符串为数字转换为数字类型*/
console.log(Number("123"));
console.log(typeof Number("123"));
/* 字符串没数字转换为数字类型*/
console.log(Number("ping"));
console.log(typeof Number("ping"));
/* 字符串夹杂数字转换为数字类型*/
console.log(Number("12null"));
/* 输入直接转换为数字类型*/
console.log(typeof Number(prompt("请输入你的年薪:"))); //输入100
console.log(typeof +prompt("请输入你的年薪:")); //输入100
/* 布尔类型转换为数字类型 */
console.log(Number(true));
console.log(Number(false));
/* null和undefined转换为数字类型 */
console.log(Number(null));
console.log(Number(undefined));
结果如下:
123
number
NaN
number
NaN
number
number
1
0
0
NaN
parseInt(保留整数)
只保留整数部分
代码如下:
console.log(parseInt('12px'));
console.log(parseInt('12.98px'));
console.log(parseInt('12.98px34.74'));
console.log(parseInt('abc12.98px'));
结果如下:
12
12
12
NaN
parseFloat(保留小数)
保留小数部分
代码如下:
console.log(parseFloat('12px'));
console.log(parseFloat('12.98px'));
console.log(parseFloat('12.98px34.74'));
console.log(parseFloat('abc12.98px'));
结果如下:
12
12.98
12.98
NaN
字符串转换
String
强制转换为字符串类型,对于null
和undefined
也可以转换为字符串
代码如下:
console.log(String(123));
console.log(typeof String(123));
console.log(String(null));
console.log(typeof String(null));
console.log(String(undefined));
console.log(typeof String(undefined));
结果如下:
123
string
null
string
undefined
string
toString
与String()
方法相似,但不能转换null
和undefined
代码如下:
let str1 = null;
let str2 = undefined;
console.log(str1.toString());
console.log(str2.toString());
结果如下:
Uncaught TypeError: Cannot read properties of null (reading 'toString')
布尔转换
Boolean
强制转换为布尔类型
- 数字:除了0和NaN,其余都为true
- 字符串:除了空串,其余都为true
- null和undefined:false
- 对象:true
console.log(Boolean(0));
console.log(Boolean(NaN));
console.log(Boolean(""));
console.log(Boolean(" "));
console.log(Boolean("dasd"));
console.log(Boolean(null));
console.log(Boolean(undefined));
console.log(Boolean({userName:"Tom"}));
结果如下:
false
false
false
true
true
false
false
true
进制转换
- 以 0x 开头,表示16进制数;
- 以 0o 开头,表示8进制数;
- 以 0b 开头,表示2进制数(但不是所有浏览器都支持);
转换为十进制
代码如下:
let num1 = 0x12;
let num2 = 0o50;
let num3 = 0b01001;
console.log(parseInt(num1,10));
console.log(parseInt(num2,10));
console.log(parseInt(num3,10));
结果如下:
18
40
9
十进制转为其它进制
代码如下:
let num = 255;
console.log(num.toString(2));
console.log(num.toString(8));
console.log(num.toString(16));
// 进制转换后为字符串型
console.log(typeof(num.toString(2)));
console.log(typeof(num.toString(16)));
// 其它方法
console.log((255).toString(16));
console.log(255..toString(16));
结果如下:
11111111
377
ff
string
string
ff
ff
输入和输出
输入语法
语法:prompt()
prompt("提示信息")
作用:显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字
注意:输入的数据是字符串类型
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>prompt</title>
</head>
<body>
<script>
prompt("请输入你的年龄");
</script>
</body>
</html>
结果如下:
输出语法
- 语法1:
document.weite()
向body内输出内容 - 语法2:
alert()
页面弹出警告对话框 - 语法3:
console.log()
控制台输出语句,程序员调试使用
HTML输出
document.write()
document.write("要输出的内容");
作用:向body内输出内容
注意:如果输出的内容写的是标签,也会被解析成网页元素
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>document.write()</title>
</head>
<body>
<script>
document.write("要输出的内容");
document.write('<br/><span style="color:red;">输出红颜色的字</span>');
</script>
</body>
</html>
结果如下:
弹窗
alert()
alter("要输出的内容")
作用:页面弹出警告对话框
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>alert</title>
</head>
<body>
<script>
alert("要输出的内容");
</script>
</body>
</html>
结果如下:
确认框
confirm()
格式:
confirm("文本")
举例说明:确定是否删除?
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
if (confirm('确认要删除吗?'))
alert('确认删除')
else
alert('取消删除')
})
结果如下:
控制台输出
console.log()
console.log("要输出的内容")
作用:控制台输出语句,程序员调试使用
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>console.log</title>
</head>
<body>
<script>
console.log("要输出的内容");
console.log(2 + 2);
</script>
</body>
</html>
结果如下:
变量和常量
变量 是计算机用来存放数据的“容器”,它可以让计算机变得有记忆
注意:变量不是数据本身,它仅仅是一个用于存储数值的容器。可以理解为一个个用来装东西的纸盒子
声明变量:
var
:全局变量(没有其他两个好用)let
:局部变量(值可以修改)const
:常量(值不可以修改,但对于数组和对象可以修改其中的某一值)- 特殊:不声明直接用,当全局变量用,例如:num=10;此时num是全局变量
变量的命名规则
规则:
- 不能用关键字,例如:let、var、if、for
- 字母、数字、下划线、$ 四种
- 字母严格区分大小写,如Age和age是不同变量,其中不能以数字开头
规范:
- 起名有意义
- 遵守小驼峰命名法。即第一个单词首字母小写,后面每个单词首字母大写,例如:userName
let(局部变量)
代码如下:
// 声明变量
let num1;
//赋值
num1 = 10;
//修改
num1 = "Tom";
console.log(num1);
//声明变量时赋值
let num2 = 10;
console.log(num2);
//声明多个变量,多个变量中间用逗号隔开
//不推荐,最好每行就写一个变量
let num3=10,num4;
console.log(num3,num4);
结果如下:
Tom
10
10 undefined
const(常量)
代码如下:
/* 唯一正确的声明方式 */
const num = 10;
console.log(num);
// const num1; 声明不赋值,错误
// num = 10; 常量不能更改,错误
结果如下:
10
但当常量是数组或对象时,可以修改其中的元素
代码如下:
const person = {
userName: "John",
age: 10
};
const num = [1,2,3,4,5];
person.userName = "Tom";
num[2] = 8;
console.log(person.userName);
console.log(num[2]);
结果如下:
Tom
8
数组
数组(Array):是一种可以按顺序保存数据的数据类型
在数组中,数据的编号也叫索引和下标
声明方法
-
**语法1:**对象创建
new Array(元素1,元素2,,...)
代码如下:
const numbers = new Array(1,2,3,4,true); console.log(numbers);
结果如下:
(5) [1, 2, 3, 4, 5]
-
**语法2:**中括号创建
[元素1,元素2,...]
代码如下:
const fruits = ["apples","oranges","pears",10]; console.log(fruits);
结果如下:
(4) ['apples', 'oranges', 'pears']
增删改查
增
.unshift()
:头部增加元素
.push()
:尾部增加元素
代码如下:
const fruits = ["apples","oranges","pears",10];
fruits.unshift("bananas");
fruits.push(true);
console.log(fruits);
结果如下:
(6) ['bananas', 'apples', 'oranges', 'pears', 10, true]
删
.shift()
:删除首部元素
.pop()
:删除末尾元素
.splice()
:删除指定元素
代码如下:
const fruits = ["apples","oranges","pears",10];
fruits.shift();
fruits.pop();
console.log(fruits);
结果如下:
(2) ['oranges', 'pears']
splice
**格式:**array.splice(start,deleteCount)
- start起始位置
- 指定修改的开始位置(从0计数)
- deleteCount删除几个元素
- 表示要移动的数组元素的个数
- 可选的,如果省略则默认从指定的起始位置删除到最后
代码如下:
const array = [1,2,3,4,5];
array.splice(1,2);
console.log(array);
array.splice(1);
console.log(array);
结果如下:
(3) [1, 4, 5]
[1]
改
代码如下:
let array = [1,2,3,4,5];
array[0] = 6;
console.log(array[0]);
结果如下:
6
查
.indexOf()
:索引(即对应下标)
代码如下:
const fruits = ["apples","oranges","pears",10];
console.log(fruits.indexOf("oranges"));
结果如下:
1
函数
函数(方法)是被设计为执行特定任务的代码块
function
:函数声明关键字- 使用函数时位置随意,不必在声明函数的下方(与C语言不同)
代码如下:
sayHi();
function sayHi(){
console.log("hi~~")
}
sayHi();
结果如下:
hi~~
函数名命名规范
-
和变量名基本一致
-
尽量使用小驼峰命名法
-
前缀应该为动词
-
命名建议:常用动词约定
动词 含义 can 判断是否可执行某个动作 has 判断是否含义某个值 is 判断是否为某个值 get 获取某个值 set 设置某个值 load 加载某些数据
参数
- 分为形参和实参,函数设置时括号里时形参,函数调用时括号里是实参
- 形参默认值为undefined
- 可以给形参赋初始值
代码如下:
function getSum(start = 0,end = 0){
let sum=0;
for(let i=start;i<=end;i++)
sum+=i;
return sum;
}
function getADD(a,b){
return a+b;
}
console.log(getSum(7,9));
console.log(getSum());
console.log(getADD())
结果如下:
24
0
NaN
匿名函数
没有名字的函数,无法直接使用,与之相反为具名函数
使用方式:
- 函数表达式
- 立即执行函数
函数表达式
将匿名函数赋值给一个变量,并且通过变量名称进行调用,我们将这个称为函数表达式
函数表达式和具名函数的区别:
- 具名函数的调用可以写到任何位置
- 函数表达式必须先声明后调用
代码如下:
let fn = function(){
console.log("我是函数表达式")
}
fn();
结果如下:
我是函数表达式
立即执行函数
声明一个函数,并马上调用这个匿名函数就叫做立即执行函数;也可以说立即执行函数是一种语法,让你的函数在定义以后立即执行
**注意:**立即执行函数必须加分号(;)
代码如下:
(function(a,b){
console.log(a+b);
})(2,6);
(function(a,b){
console.log(a+b);
}(2,6));
// 这些也是立即执行函数的特殊写法
!function(){}();
+function(){}();
~function(){}();
结果如下:
8
通用函数
typeof(检测数据类型)
-
数字类型 typeof 返回值是 number
代码如下:
const num = 100; typeof(num); typeof(1);
结果如下:
number number
-
字符串类型 typeof 返回的值是 string
代码如下:
const str = "John"; console.log(typeof str); console.log(typeof "Tom");
结果如下:
string string
-
布尔类型 typeof 返回的值是 boolean
const fal = false; console.log(fal); console.log(true);
结果如下:
false true
-
数组或对象 typeof返回的值是 object
代码如下:
const array = [1,2,3,4]; const person = { userName:"John", age:18 }; console.log(typeof array); console.log(typeof person); console.log(typeof [1,2,3,4,5]); console.log(typeof {pe:"tom"});
结果如下:
object object object object
-
函数类型,返回的值是 function。
比如:typeof(eval),typeof(Date)返回的值都是 function。 -
不存在的变量、函数或者 undefined,将返回 undefined。
比如:typeof(abc)、typeof(undefined) 都返回 undefined
length(长度检测)
.length
可以测得数组和字符串的长度
注意:空格、特殊字符计算在长度中
代码如下:
let array = [1,2,3,4,5,'tom'];
let str = "T #\nd\0";
console.log(array.length);
console.log(array[5].length);
console.log(str.length);
结果如下:
6
3
6
trim(去除左右空格)
trim()
函数:去除字符串左右两端的空格
代码如下:
const str = " pink "
console.log(str);
console.log(str.trim())
结果如下:
match(搜索匹配子串)
match()
方法用于在字符串中搜索匹配的子串,并返回一个包含匹配结果的数组。
它是基于正则表达式进行模式匹配的,并且可以非常灵活地进行字符串搜索与提取。
语法:
string.match(regexp);
string
:要进行匹配的字符串。可以是一个变量或字面量regexp
:一个正则表达式对象,用于指定匹配规则
reset(表单重置)
表单进行重置
格式:
form.reset();
对象
对象
:JavaScript中的一种数据类型
可以理解为一种无序的数据集合
对象中可以有任何数据类型,由属性和方法组成
声明方法
-
**语法1:**对象创建
new Object()
代码如下:
let person = new Object(); person.userName = "John"; person.eat = function(){ console.log(this.userName + "正在吃饭"); }; person.eat(); console.log(person);
结果如下:
John正在吃饭 {userName: 'John', eat: ƒ}
-
**语法2:**大括号创建
对象名 = {对象内容}
代码如下:
let person = { userName: "John", sex: '男' }; console.log(person.userName); console.log(person);
结果如下:
John {userName: 'John', sex: '男'}
代码如下:
const person = {
firstName: "John",
lastName: "Doe",
age: 30,
hobbies: ["music","movies","sports"],
address:{
street: '50 main st',
city: "Boston",
state:"MA"
}
};
console.log(person.address.state);
console.log(person.hobbies[1]);
console.log(person)
结果如下:
MA
movies
{firstName: 'John', lastName: 'Doe', age: 30, hobbies: Array(3), address: {…}}
同名取值
用同名的变量将对象中的数值取出
代码如下:
const person = {
firstName: "John",
lastName: "Doe",
age: 30,
hobbies: ["music","movies","sports"],
address:{
street: '50 main st',
city: "Boston",
state:"MA"
}
};
const {firstName, lastName, address:{city}} = person;
console.log(firstName);
console.log(city);
结果如下:
John
Boston
增删改查
增
对象增加属性
格式:对象名.添加的属性 = 属性值
代码如下:
const person = {
firstName: "John",
lastName: "Doe",
age: 30,
hobbies: ["music","movies","sports"],
address:{
street: '50 main st',
city: "Boston",
state:"MA"
}
};
person.email = "john@qq.com";
console.log(person.email);
结果如下:
john@qq.com
删
语法:delete 对象名.添加的属性 = 属性值
代码如下:
const person = {
firstName: "John",
lastName: "Doe",
age: 30,
hobbies: ["music","movies","sports"],
address:{
street: '50 main st',
city: "Boston",
state:"MA"
}
};
delete person.age;
console.log(person);
结果如下:
{firstName: 'John', lastName: 'Doe', hobbies: Array(3), address: {…}}
改
格式:对象名.添加的属性 = 新值
代码如下:
const person = {
firstName: "John",
lastName: "Doe",
age: 30,
hobbies: ["music","movies","sports"],
address:{
street: '50 main st',
city: "Boston",
state:"MA"
}
};
person.firstName = "Tom";
console.log(person.firstName);
结果如下:
Tom
查
语法:
-
对象名.添加的属性
代码如下:
const person = { firstName: "John", lastName: "Doe", age: 30, hobbies: ["music","movies","sports"], address:{ street: '50 main st', city: "Boston", state:"MA" } }; console.log(person.firstName);
结果如下:
John
-
对象名.[“属性”] 此语法适用于属性名中有除命名字符外的其它符号,例如 - + * /
中括号里面可以是单引号也可以是双引号
属性名中有命名字符外的其它符号要用单引号或双引号括起来
代码如下:
const person = { "first-Name": "John", sex$_123: '男', }; // console.log(person.first-Name); 此代码为person.first 减去 Name // console.log(person."first-Name") 此代码格式错误 console.log(person["first-Name"]); console.log(person.sex$_123);
结果如下:
John 男
遍历对象
因为对象是无序的,所以不能用下标的方式遍历对象
使用一个特殊的for循环
格式:for(let 变量名 in 对象名)
代码如下:
const obj = {
uname: "刘德华",
sex: '男',
age: 18,
};
for(let k in obj){ //k得到的数据是字符串类型,即使在数组中也是字符串类型,所以不能使用obj.k形式
console.log(k);
console.log(obj[k]);
}
结果如下:
uname
刘德华
sex
男
age
18
数组对象
用数组方式存储对象
代码如下:
const todos = [
{
id: 1,
text: "Take out trash",
isCompleted: true,
},
{
id: 2,
text: "Meeting with boss",
isCompleted: true,
},
{
id: 3,
text: "Dentist appt",
isCompleted: false,
},
]
console.log(todos[1].text)
结果如下:
Meeting with boss
内置对象
数学对象
方法名 | 说明 |
---|---|
random | 生成0~1之间的随机数 [0,1)(包含0,不包含1) |
ceil | 向上取整 |
floor | 向下取整 |
round | 四舍五入(注意负数时,以 .5 为分界线,小于.5则为更小的负数,其余为更大的负数) |
max | 找最大数 |
min | 找最小数 |
pow | 幂运算 |
sqrt | 平方根 |
abs | 绝对值 |
数学公式
代码如下:
// 属性
console.log(Math.PI); // 3.141592653589793
// ceil 天花板 向上取整
console.log(Math.ceil(1.1)); // 2
console.log(Math.ceil(1.5)); // 2
console.log(Math.ceil(1.9)); // 2
// floor 地板 向下取整
console.log(Math.floor(1.1)); // 1
console.log(Math.floor(1.5)); // 1
console.log(Math.floor(1.9)); // 1
// 四舍五入
console.log(Math.round(1.1)); // 1
console.log(Math.round(1.4)); // 1
console.log(Math.round(1.5)); // 2
console.log(Math.round(1.9)); // 2
console.log(Math.round(-1.1)); // -1
console.log(Math.round(-1.5)); // -1
console.log(Math.round(-1.51)); // -2
console.log(Math.round(-1.9)); // -2
// 最大和最小
console.log(Math.max(1,5,2,4,5,47,8,23)); // 47
console.log(Math.min(1,5,2,4,5,47,8,23)); // 1
// 幂运算
console.log(Math.pow(2,3)); // 8
console.log(Math.pow(2,-3)); // 0.125
console.log(Math.pow(1)); // NaN
// 平方根
console.log(Math.sqrt(9)); // 3
console.log(Math.sqrt(-9)); // NaN
// 绝对值
console.log(Math.abs(-9)); // 9
console.log(Math.abs(9)); // 9
生成随机数
random
:生成0~1之间的随机数 [0,1)(包含0,不包含1)
代码如下:
// 0~10的随机数
let num1 = Math.floor(Math.random() * (10+1) + 0); // [0,11)的向下取整,即[0,10]
console.log(num1);
// 5~10的随机数
let num2 = Math.floor(Math.random() * (10+1) + 5); // [5,11)的向下取整,即[5,10]
console.log(num2);
// N~M的随机数
let N = 200;
let M = 300
let num3 = Math.floor(Math.random() * (M-N+1) + N); //[N,M+1)的向下取整,即[N,M]
console.log(num3);
实例:
-
随机颜色
代码如下:
function cool(x=0,y=255){ return parseInt(Math.random() * (y-x+1) + x); }; document.write(` <div style="width:100px;height:100px; background:rgb(${cool(0,255)},${cool(0,255)},${cool(0,255)});"></div> `);
-
抽奖
注意:抽中后不能再抽中此人
let arr = ['赵云','黄忠','关羽','张飞','马超','刘备','曹操']; // 0~6随机数 for(let i=0;i<7;i++){ let random = Math.floor(Math.random() * (arr.length)); console.log(arr[random]); arr.splice(random,1); }
时间对象
实例化
-
在代码中发现了
new
关键字时,一般将这个操作称为实例化
-
创建一个时间对象并获取时间
-
获取当前时间
const date = new Date(); console.log(date);
-
获取指定时间
const date = new Date('2008-8-8 08:30:00'); console.log(date);
-
日期对象方法
**使用场景:**因为日期对象返回的数据我们不能直接使用,所以需要转换成实际开发中的常用的格式
方法 | 作用 | 说明 |
---|---|---|
getFullYear() | 获得年份 | 获得四位数年份 |
getMonth() | 获得月份 | 取值为0~11,一月为0 |
getDate() | 获取月份中的每一天 | 不同月份取值也不相同 |
getDay() | 获取星期 | 取值为0~6,星期七为0 |
getHours() | 获取小时 | 取值为0~23 |
getMinutes() | 获取分钟 | 取值为0~59 |
getSeconds() | 获取秒 | 取值为0~59 |
toLocaleString() | 日期格式化 | 以 2024/03/07 00:00:00 格式输出 |
toLocaleDateString() | 年月日格式化 | 以2024/03/07格式输出年月日 |
toLocaleTimeString() | 时分秒格式化 | 以 00:00:00格式输出时分秒 |
代码如下:
const date = new Date('2024-3-7 17:51:23');
console.log(date);
console.log(date.getFullYear()); // 返回年
console.log(date.getMonth()+1); // 返回月
console.log(date.getDate()); // 返回当月日期
console.log(date.getDay()); // 返回星期
console.log(date.getHours()); // 返回小时
console.log(date.getMinutes()); // 返回分钟
console.log(date.getSeconds()); // 返回秒
结果如下:
时间戳
- **使用场景:**如果计算倒计时效果,前面方法无法直接计算,需要借助于时间戳完成
- 什么是时间戳:
- 是指1970年01月01日00时00分00秒起至现在的
毫秒数
,它是一种特殊的计量时间的方式
- 是指1970年01月01日00时00分00秒起至现在的
- 算法:
- 将来的时间戳 - 现在的时间戳 = 剩余时间毫秒数
- 剩余时间毫秒数 转换为 剩余时间的 年月日时分秒 就是 倒计时
- 比如 将来的时间戳 2000ms - 现在的时间戳 1000ms = 1000ms
- 1000ms 转换为就是 0小时0分1秒
三种方式获取时间戳
-
使用
getTime()
方法const date = new Date() console.log(date.getTime())
-
简写
+new Date()
console.log(+new Date())
-
使用
Date.now()
- 无需实例化
- 但是只能返回当前的时间戳,而前面两种可以返回指定时间的时间戳
console.log(Date.now())
Web API
**作用:**就是使用JS去操作HTML和浏览器
**分类:**DOM(文档对象模型)、BOM(浏览器对象模型)
DOM
- DOM(Document Object Model——文档对象模型)是用来呈现以及与任意HTML或XML文档交互的API
- 白话文:DOM是浏览器提供的一套专门用来操作网页内容的功能
DOM对象
浏览器根据HTML标签生成的JS对象
- 所有的标签属性都可以在这个对象上找到
- 修改这个对象的属性会自动映射到标签身上
小知识:console.dir
主要表示在控制台显示指定JS对象的属性,而console.log不行
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="/index.css" />
</head>
<body>
<div>123</div>
<script>
const div = document.querySelector("div");
console.dir(div);
</script>
</body>
</html>
结果如下:
获取DOM元素
根据CSS选择器来获取DOM元素
-
语法:
document.querySelector('CSS选择器')
**参数:**包含一个或多个有效的CSS选择器字符串
**返回值:**CSS选择器匹配的第一个元素,如果没有匹配则返回null
代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <link rel="stylesheet" href="/index.css" /> </head> <body> <div class="box">123</div> <div class="box">1234</div> <ul> <li>测试1</li> <li>测试2</li> <li>测试3</li> </ul> <script> const div = document.querySelector(".box"); console.dir(div.innerHTML); const li = document.querySelector("ul li"); console.dir(li.innerHTML); </script> <!-- <script src="/index.js"></script> --> </body> </html>
结果如下:
123 测试1
-
语法:
document.querySelectorAll('CSS选择器')
**参数:**包含一个或多个有效的CSS选择器字符串
**返回值:**CSS选择器匹配 NodeList 对象集合
**说明:**其是一个伪数组,即有长度、索引,但是没有pop()、push()等数组方法
代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <ul> <li>测试1</li> <li>测试2</li> <li>测试3</li> </ul> <script> const lis = document.querySelectorAll("ul li"); console.log(lis); </script> </body> </html>
结果如下:
NodeList(3) [li, li, li]
修改DOM对象
通过document.querySelector('CSS选择器')
的DOM对象可以直接选择属性修改
通过document.querySelectorAll('CSS选择器')
的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>
<style>
div {
width: 100px;
height: 100px;
background: pink;
}
</style>
</head>
<body>
<div></div>
<div id="nav"></div>
<div></div>
<script>
const nav = document.querySelector("#nav");
nav.style.background = "red";
const divs = document.querySelectorAll("div");
divs[2].style.background = "green";
</script>
</body>
</html>
结果如下:
操作元素内容
-
元素.innerText
- 将文本内容添加、更新到任意标签位置
- 显示纯文本,不解析标签
代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>innerText</title> </head> <body> <ul class="nav"> <li>我的首页</li> <li>产品介绍</li> <li>联系方式</li> </ul> <script> const lis = document.querySelectorAll(".nav li"); lis[1].innerText = "没有产品"; lis[2].innerText = "<b>没有产品</b>"; </script> </body> </html>
结果如下:
- 元素.innerHTML
- 将文本内容添加、更新到任意标签位置
- 会解析标签,多标签建议使用模板字符
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>innerHTML</title>
</head>
<body>
<ul class="nav">
<li>我的首页</li>
<li>产品介绍</li>
<li>联系方式</li>
</ul>
<script>
const lis = document.querySelectorAll(".nav li");
lis[1].innerHTML = "没有产品";
lis[2].innerHTML = "<b>没有产品</b>";
</script>
</body>
</html>
结果如下:
操作元素常用属性
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<img src="/媒体/图像/动漫/间谍过家家/阿尼亚01小.PNG" alt="">
<script>
const img = document.querySelector('img');
img.src = '/媒体/图像/动漫/间谍过家家/阿尼亚01.png'
</script>
</body>
</html>
常用操作元素
属性 | 说明 | 举例 |
---|---|---|
checked | 是否被选中 | checkbox.checked = true(复选框被选中) |
操作元素样式属性
style 属性操作CSS
注意:
- 使用小驼峰命名法,例如 background-color——backgroundColor
- 要注意加单位
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
background: pink;
}
</style>
</head>
<body>
<div></div>
<script>
const div = document.querySelector("div");
div.style.width = "300px";
div.style.backgroundColor = "blue";
div.style.border = "1px solid blue";
div.style.borderTop = "5px solid red";
</script>
</body>
</html>
结果如下:
操作类名(className)操作CSS
修改样式比较多,直接通过style属性修改比较繁琐,我们可以借助CSS类名的形式
语法:元素.className = 'active'
注意:
- 由于class是关键字,所以使用 className 去代替
- className 是使用新值换旧值,如果需要添加一个类,需要保留之前的类名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
div {
width: 100px;
height: 100px;
background-color: pink;
}
.box {
width: 200px;
height: 200px;
background-color: skyblue;
}
</style>
</head>
<body>
<div></div>
<script>
const div = document.querySelector('div');
div.className = 'box'
</script>
</body>
</html>
操作类名(classList)操作CSS
为了解决className 容易覆盖以前的类名,我们可以通过classList 方式追加和删除类名
语法:
元素.classList.add("类名")
:追加一个类元素.classList.remove("类名")
:删除一个类元素.classList.toggle("类名")
:切换一个类(有就删掉,没有就加上)元素.classList.contains("类名")
:查询一个类,有返回true,没有返回false
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
color: #333;
}
.active {
color: red;
background-color: pink;
}
</style>
</head>
<body>
<div class="box">文字</div>
<script>
const box = document.querySelector('.box');
// 追加类
box.classList.add('active');
// 删除类
box.classList.remove('box');
// 切换类 有就删掉,没有就加上
box.classList.toggle('box');
</script>
</body>
</html>
自定义属性
区分于标准属性:标签天生自带的属性,比如 class、id、title
自定义属性:
- 在html5中推出来了专门的
data-
自定义属性 - 在标签上一律以 data- 开头
- 在DOM对象上一律以 dataset 对象方式获取
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div data-id="1">1</div>
<div data-id="2">2</div>
<div data-id="3">3</div>
<div data-id="4">4</div>
<div data-id="5">5</div>
<script>
const one = document.querySelector('div:nth-child(5)');
console.log(one.dataset.id);
</script>
</body>
</html>
结果如下:
5
DOM节点
- DOM节点
- DOM树里的每一个内容都称之为节点
- 节点类型
- 元素节点
- 所有的标签 比如 body、div
- html 是根节点
- 属性节点
- 所有的属性 比如 href
- 文本结点
- 所有的文本
- 其它
- 元素节点
查找节点
节点关系:针对的找亲戚返回的都是对象
- 父节点
- 子节点
- 兄弟节点
父节点查找
parentNode
属性- 返回最近一级的父节点 找不到返回为 null
子元素.parentNode
举例:广告点击关闭
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>点击关闭</title>
<style>
.adv {
width: 1000px;
height: 200px;
background: pink;
position: relative;
margin: 100px auto;
font-size: 50px;
text-align: center;
line-height: 100px;
}
.close {
width: 20px;
height: 20px;
position: absolute;
right: 20px;
top: 10px;
background-color: skyblue;
text-align: center;
line-height: 20px;
font-size: 16px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="adv">
我是广告
<div class="close">X</div>
</div>
<script>
// const adv = document.querySelector('.adv')
const close = document.querySelector('.close');
close.addEventListener('click', function () {
this.parentNode.style.display = 'none';
})
</script>
</body>
</html>
子节点查找
-
childNodes
- 获得所有子节点、包括文本节点(空格、换行)、注释节点等
-
children 属性(重点)
- 仅获得所有元素节点
- 返回的还是一个伪数组
父元素.children
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>
<p>第一个段落</p>
</li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
const ul = document.querySelector('ul');
console.log(ul.children);
</script>
</body>
</html>
兄弟节点查找
-
下一个兄弟节点
nextElementSibling
属性 -
上一个兄弟节点
previousElementSibling
属性
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
<script>
const li2 = document.querySelector('li:nth-child(2)');
console.log(li2.previousElementSibling);
console.log(li2.nextElementSibling);
</script>
</body>
</html>
增加节点
- 很多情况下,我们需要在页面中增加元素
- 比如:点击发布按钮,可以新增一条信息
- 一般情况下,我们新增节点,按照如下操作:
- 创建一个新的节点
- 把创建的新的节点放入到指定的元素内部
创建节点
-
语法:
// 创建一个新的元素节点 document.createElement('标签名')
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 创建节点,但并没有给予位置
const div = document.createElement('div');
console.log(div);
</script>
</body>
</html>
追加节点
-
要想在界面中看到,还得插入到某个父元素中
-
插入到父元素的最后一个子元素:
// 插入到这个父元素的最后 父元素.appendChild(要插入的元素)
-
插入到父元素中某个子元素的前面
// 插入到某个子元素的前面 父元素.insertBefore(要插入的元素,在哪个元素前面)
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>我是老大</li>
</ul>
<script>
// 创建节点,但并没有给予位置
const ul = document.querySelector('ul');
const li = document.querySelector('li');
const li1 = document.createElement('li');
const li2 = document.createElement('li');
li1.innerHTML = '追加元素'
li2.innerHTML = '追加元素2'
// 追加节点,作为最后一个子元素
ul.appendChild(li1);
// 追加节点,作为某个子元素的前一个元素
ul.insertBefore(li1, ul.children[0]);
ul.insertBefore(li2, ul.children[0]);
ul.insertBefore(li, ul.children[0]);
const lis = document.querySelector('li:first-child');
</script>
</body>
</html>
克隆节点
-
特殊情况下,我们新增节点,按照如下操作:
- 复制一个原有的节点
- 把复制的节点放入到指定的元素内部
-
语法:
// 克隆一个已有的元素节点 元素.cloneNode(布尔值)
-
cloneNode
会克隆一个跟原标签一样的元素,括号内传入布尔值- 若为 true,则代表克隆时会包含后代节点一起克隆
- 若为 false,则代表克隆时不包含后代结点,
默认值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
const ul = document.querySelector('ul');
// 克隆节点 元素.cloneNode(true)
const li1 = ul.children[0].cloneNode(true);
// 将克隆节点追加到 ul 的最后
ul.appendChild(li1);
</script>
</body>
</html>
删除节点
-
在JavaScript 原生DOM操作中,要删除元素必须通过
父元素删除
-
语法:
父元素.removeChild(要删除的元素)
-
注:
- 如不存在父子关系则删除不成功
- 删除节点和隐藏节点(display:none)有区别的:隐藏节点还是存在的,但是删除,则从html中删除节点
定时器
间歇函数
格式:setInterval(函数,间隔时间)
**单位:**ms(毫秒)(1 s = 1000 ms)
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
setInterval(function () {
console.log('一秒执行一次');
}, 1000)
或
function fun(){
console.log('一秒执行一次');
}
// 函数不要加括号,否则就是引入函数,先执行函数,返回值进行运算
setInterval(fun,1000);
</script>
</body>
</html>
结果如下:
定时器有一个独立的 id 值,其为函数返回值
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function fun() {
console.log('一秒执行一次');
}
let time = setInterval(fun, 1000);
console.log(time);
</script>
</body>
</html>
结果如下:
1
关闭计时器
格式:clearInterval(定时器id)
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function fun() {
console.log('一秒执行一次');
}
let time = setInterval(fun, 1000);
console.log(time);
clearInterval(time);
</script>
</body>
</html>
M端事件
移动端事件。比如 触屏事件 touch
(也称触摸事件),Android和IOS都有
-
常见的触屏事件
触屏touch事件 说明 touchstart 手指触摸到一个DOM元素时触发 touchmove 手指在一个DOM元素上滑动时触发 touchend 手指在一个DOM元素上移开时触发
事件
事件监听
语法:事件源.addEventListener('事件类型',要执行的函数)
事件监听三要素:
- **事件源:**哪个DOM元素被事件触发,要获取DOM元素
- **事件类型:**用什么方式触发,比如鼠标单击 click 、鼠标经过 mouseover 等
- **事件调用的函数:**要做什么事
说明:
- 事件监听有垃圾回收功能,指事件完成后,事件内定义的变量或常量自动删除
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>点击</button>
<script>
const but = document.querySelector('button');
but.addEventListener('click', function () {
alert('我弹出来了');
})
</script>
</body>
</html>
结果如下:
事件类型
类型名 | 说明 |
---|---|
click | 鼠标点击 |
mouseenter | 鼠标经过(没有冒泡效果) |
mouseleave | 鼠标离开(没有冒泡效果) |
mouseover | 鼠标经过(有冒泡效果) |
mouseout | 鼠标离开(有冒泡效果) |
mousemove | 鼠标移动事件 |
focus | 获得焦点(获得光标) |
blur | 失去焦点 |
keydown | 键盘按下触发 |
keyup | 键盘抬起触发 |
input | 用户输入事件 |
submit | 提交 |
load | 等待页面加载完毕 |
change | 内容改变 |
不在addEventListener中
事件 | 说明 |
---|---|
ontimeupdate | 事件在视频/音频当前的播放位置发送改变时触发 |
onloadeddate | 事件在当前帧的数据加载完成 且还没有足够的数据播放视频/音频的下一帧触发 |
video.ontimeupdate = function(){
// 代码
}
事件对象
获取事件对象
- 事件对象是什么
- 也是个对象,这个对象里有事件触发时的相关信息
- 例如:鼠标点击事件中,事件对象就存了鼠标在哪个位置等信息
- 使用场景
- 可以判断用户按下哪个键,比如按下回车键可以发布新闻
- 可以判断鼠标点击了哪个元素,从而做相应的操作
- 语法:如何获取
- 在事件绑定的回调函数的第一个参数就是事件对象
- 一般命名为event、ev、e
元素.addEventListener('click',function(e){})
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text">
<script>
const input = document.querySelector('input');
input.addEventListener('keyup', function (e) {
console.log(e.key);
})
</script>
</body>
</html>
常用属性
属性名 | 说明 |
---|---|
type | 获取当前的事件类型 |
clientX/clientY | 获取光标相对于浏览器可见窗口左上角的位置 |
offsetX/offsetY | 获取光标相对于当前DOM元素左上角的位置 |
key | 用户按下的键盘键的值 |
环境对象
目标:能够分析判断函数运行在不同环境中 this
所代指的对象
**环境对象:**值的是函数内部特殊的变量 this
,它代表着当前函数运行时所处的环境
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>按钮</button>
<script>
const but = document.querySelector('button');
but.addEventListener('click', function () {
this.style.background = 'red'
})
</script>
</body>
</html>
回调函数
如果将函数 A 作为参数传递给函数 B 时,我们称函数 A 为回调函数
常见的使用场景:
定时器
function fun(){
console.log("我是回调函数");
};
// fun传递给了setInterval,fun就是回调函数
setInterval(fun,1000);
事件触发
box.addEventListener('click',function(){
console.log("我是回调函数")
})
事件流
事件流指的是事件完整执行过程中的流动路径
- 捕获阶段
- 冒泡阶段
事件捕获
**概念:**从DOM的根元素开始去执行对应的事件(从外到里)
DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制);
-
addEventListener 第三个参数传入 true 代表是捕获阶段触发,传入 false 默认值代表是冒牌阶段触发
-
标记 true 参数的对于其子元素,应优先执行
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件捕获</title>
<style>
.father {
width: 200px;
height: 200px;
background-color: hotpink;
}
.son {
width: 100px;
height: 100px;
background-color: orange;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
// 获取爸爸
const father = document.querySelector('.father');
// 获取儿子
const son = document.querySelector('.son');
document.addEventListener('click', function () {
alert('我是爷爷');
}, true)
father.addEventListener('click', function () {
alert('我是爸爸');
}, true)
son.addEventListener('click', function () {
alert('我是儿子');
}, true)
</script>
</body>
</html>
- 点击 document 区域,弹出 “我是爷爷”
- 点击 .father 区域,先弹出"我是爷爷",再弹出"我是爸爸"
- 点击 .son 区域,先弹出"我是爷爷",再弹出"我是爸爸",最后弹出"我是儿子"
事件冒泡
概念:当一个元素的事件被触发时,同样的事件会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒牌
**简单理解:**当一个元素触发事件后,会依次向上调用所有父级元素的 同名事件
事件冒泡是默认存在的
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件捕获</title>
<style>
.father {
width: 200px;
height: 200px;
background-color: hotpink;
}
.son {
width: 100px;
height: 100px;
background-color: orange;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
// 获取爸爸
const father = document.querySelector('.father');
// 获取儿子
const son = document.querySelector('.son');
document.addEventListener('click', function () {
alert('我是爷爷');
}, false)
father.addEventListener('click', function () {
alert('我是爸爸');
}, false)
son.addEventListener('click', function () {
alert('我是儿子');
}, false)
</script>
</body>
</html>
- 点击 document 区域,弹出 “我是爷爷”
- 点击 .father 区域,先弹出"我是爸爸",再弹出"我是爷爷"
- 点击 .son 区域,先弹出"我是儿子",再弹出"我是爸爸",最后弹出"我是爷爷"
阻止冒泡
**问题:**因为默认就有冒泡模式的存在,所有容易导致事件影响到父级元素
**需求:**若想把事件就限制在当前元素中,就需要阻止事件冒泡
**前提:**阻止事件冒泡需要拿到事件对象
语法:
事件对象.stopPropagation()
**注意:**此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件捕获</title>
<style>
.father {
width: 200px;
height: 200px;
background-color: hotpink;
}
.son {
width: 100px;
height: 100px;
background-color: orange;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
// 获取爸爸
const father = document.querySelector('.father');
// 获取儿子
const son = document.querySelector('.son');
document.addEventListener('click', function () {
alert('我是爷爷');
})
father.addEventListener('click', function () {
alert('我是爸爸');
})
son.addEventListener('click', function (e) {
alert('我是儿子');
// 阻止流动传播
e.stopPropagation();
})
</script>
</body>
</html>
- 此时,点击 .son 区域,只弹出 “我是儿子”
阻止默认行为
我们某些情况下需要阻止默认行为的发生,比如阻止链接的跳转,表单域跳转
语法:
事件对象.preventDefault()
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>免费注册</title>
</head>
<body>
<form action="http://www.baidu.com">
<input type="submit" value="免费注册">
</form>
<a href="http://www.baidu.com">百度一下</a>
<script>
const form = document.querySelector('form');
form.addEventListener('click', function (e) {
e.preventDefault()
})
const a = document.querySelector('a');
a.addEventListener('click', function (e) {
e.preventDefault()
})
</script>
</body>
</html>
解绑事件
on事件方式(L0)
传统方式:对象.onclick = function(){}
解绑方式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="but">按钮</button>
<button class="nul">解绑</button>
<script>
const but = document.querySelector('.but');
const nul = document.querySelector('.nul');
but.onclick = function () {
alert('我出现了');
}
nul.addEventListener('click', function () {
but.onclick = null;
})
</script>
</body>
</html>
addEventListener事件解绑(L2)
addEventListener方式,解绑必须使用:
removeEventListener(事件类型,事件处理函数,[获取捕获或冒泡阶段])
**注意:**匿名函数不能解绑
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>按钮</button>
<script>
const but = document.querySelector('button');
let i = 0;
function fun() {
alert(++i);
if (i === 3)
but.removeEventListener('click', fun);
}
but.addEventListener('click', fun);
</script>
</body>
</html>
- 点击3次就不能弹出弹框了
鼠标经过事件的区别
mouseover和mouseout有冒泡效果
mouseenter和mouseleave没有冒泡效果
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.dad {
width: 400px;
height: 400px;
background-color: hotpink;
}
.baby {
width: 200px;
height: 200px;
background-color: orange;
}
</style>
</head>
<body>
<div class="dad">
<div class="baby"></div>
</div>
<script>
const dad = document.querySelector('.dad')
const baby = document.querySelector('.baby')
dad.addEventListener('mouseover', function () {
console.log('鼠标经过');
})
dad.addEventListener('mouseout', function () {
console.log('鼠标离开')
})
</script>
</body>
</html>
- 鼠标经过 .dad 是显示 “鼠标经过”,当鼠标进入 .baby 中时,进入子元素认为离开 .dad ,显示 “鼠标离开”,此时向上冒泡,显示 “鼠标经过”
事件委托
事件委托是利用事件流的特征解决一些开发需求的知识技巧
- **优点:**减少注册此时,可以提高程序性能
- **原理:**事件委托其实是利用事件冒泡的特点
- 给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件
- 将点击冒泡到父元素时,如果想控制按下的按钮,需控制事件对象,即回调函数的第一个参数
- this控制的是当前事件源,即父元素
target
:是触发对象target.tagName
可以获得真正触发事件的元素,可以解决子元素中不想改变的元素,对应的数据必须是大写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li><button>按钮</button></li>
<li><button>按钮</button></li>
<li><button>按钮</button></li>
<li><button>按钮</button></li>
</ul>
<script>
const ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
e.target.style.backgroundColor = 'red';
})
</script>
</body>
</html>
- 此时点击 li ,字体会变为红色,点击 p 不会变色
其他事件
页面加载事件
load事件
- 加载外部资源(如:图片、外联CSS和JavaScript等)加载完毕时触发的事件
- 监听页面所有资源加载完毕
- 给window添加load事件
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
window.addEventListener('load', function () {
const but = document.querySelector('button');
but.addEventListener('click', function () {
alert(11);
})
})
</script>
</head>
<body>
<button>按钮</button>
</body>
</html>
DOMContentLoaded事件
-
当初始的 HTML 文档被完全加载和解析后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完全加载
-
监听页面DOM加载完成
- 给 document 添加 DOMContentLoaded 事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
document.addEventListener('DOMContentLoaded', function () {
const but = document.querySelector('button');
but.addEventListener('click', function () {
alert(11);
})
})
</script>
</head>
<body>
<button>按钮</button>
</body>
</html>
页面滚动事件
-
滚动条在滚动的时候持续触发的事件
-
事件名:
scroll
-
监听整个页面滚动
winndow.addEventListener('scroll',function(){ })
- 给 window 或 document 都可以
-
监听某个元素的内部滚动直接给某个元素加即可
获取位置
scrollLeft
和scrollTop
(属性)
- 获取被卷去的大小
- 获取元素内容往左、往上滚出去看不到的距离
- 这两个值是可读写的,即既能读取,也能修改,就是页面打开时就在设定的 scrollTop 位置上
**注意:**整个页面的滚动不是 body 在滚动,而是 html 在滚动
获取方法为:document.documentElement
指向整个 html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style>
body {
height: 3000px;
}
div {
width: 200px;
height: 200px;
border: 1px solid #000;
overflow: scroll;
margin: 100px auto;
/* TODO: */
/**/
display: none;
}
</style>
</head>
<body>
<div>
我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素我里面有很多元素
</div>
<script>
const div = document.querySelector('div');
window.addEventListener('scroll', function () {
const n = document.documentElement.scrollTop;
if (n >= 100) {
div.style.display = 'block'
} else {
div.style.display = 'none'
}
})
</script>
</body>
</html>
scrollTo
滚动到指定位置
**语法:**元素.scrollTo(X,Y)
// 让页面滚动到 y 轴1000像素的位置
window.scroll(0,1000)
页面尺寸事件
会在窗口尺寸改变的时候触发事件:
-
resize
window.addEventListener('resize',function(){ // 执行的代码 })
-
检测屏幕宽高
-
clientWidth
(检测宽)和clientHeight
(检测高) -
获取元素的可见部分宽高(不包含边框,外边距,滚动条等)
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
div {
/* width: 200px; */
height: 200px;
background-color: hotpink;
padding: 10px;
display: inline-block;
}
</style>
</head>
<body>
<div>1231312314545646512331563444</div>
<script>
const div = document.querySelector('div');
console.log(div.clientWidth);
</script>
</body>
</html>
元素尺寸与位置
使用场景:
- 通过 js 的方式,得到元素在页面中的位置
- 页面滚动到某个元素,就可以做某些事
获取元素宽高
offsetWidth
和offsetHeight
- 获取元素的自身宽高、包含元素自身设置的宽高、padding、border
- 获取出来的是数值,方便计算
- **注意:**获取的是可视宽高,如果盒子是隐藏的,获取的结果是0
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 100px;
height: 100px;
border: 1px solid #000;
padding: 10px;
margin: 100px auto;
}
</style>
</head>
<body>
<div></div>
<script>
const div = document.querySelector('div');
console.log(div.offsetWidth);
console.log(div.offsetHeight);
</script>
</body>
</html>
获取元素位置
offsetLeft
和offsetTop
- 获取元素距离自己定位父级元素的左、上距离
- **注意:**是只读属性
- 当父级元素没有定位时,依次向上查找,而当祖先元素有定位后,查询的是此元素距离自己的左、上距离
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
body {
padding: 0;
margin: 0;
}
div {
width: 200px;
height: 200px;
background-color: hotpink;
margin: 100px;
position: relative;
}
p {
width: 50px;
height: 50px;
margin: 50px;
background-color: orange;
}
</style>
</head>
<body>
<div>
<p></p>
</div>
<script>
const div = document.querySelector('div');
const p = document.querySelector('p');
console.log(div.offsetTop);
console.log(div.offsetLeft)
console.log(p.offsetLeft);
</script>
</body>
</html>
获取元素尺寸
获取位置
元素.getBoundingClientRect()
- 获取元素的位置,这个方法没有参数
- 获取页面中某个元素的左上右下,分别相对于浏览器视图的位置
- 是DOM元素到浏览器可视范围的距离(不包含文档卷起的部分)
方法返回元素的大小及相对于视窗的位置
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
height: 2000px;
}
div {
width: 200px;
height: 200px;
background-color: hotpink;
margin: 100px;
}
</style>
</head>
<body>
<div></div>
<script>
const div = document.querySelector('div');
console.log(div.getBoundingClientRect());
</script>
</body>
</html>
结果如下:
鼠标的位置
pageX
:获取鼠标X地址pageY
:获取鼠标Y地址
总结
代码 | 作用 | 说明 |
---|---|---|
scrollLeft和scrollTop | 被卷去的头部和左侧 | 配合页面滚动来用,可读写 |
clientWidth和clientHeight | 获得元素宽度和高度 | 不包含border,margin,滚动条 用于js获取元素大小只读 属性 |
offsetWidth和offsetHeight | 获取元素宽度和高度 | 包含border,padding,滚动条等,只读 |
offsetLeft和offsetTop | 获取元素距离自己定位父元素的左、上距离 | 获取元素位置的时候使用,只读 属性 |
window.devicePixelRatio | 返回当前显示设备的物理像素分辨率与CSS 像素分辨率之比,即屏幕缩放比例 | 分辨电脑模式或手机模式 |
document.documentElement | 返回文档对象document 的根元素的只读属性,即整个 html | 查询当前可视页面被卷去的头部 |
Window对象
BOM
BOM(Browser Object Model)
是浏览器对象模型
- window 对象是一个全局对象,也可以说是JavaScript中的顶级对象
- 像document、alert()、console.log() 这些都是 window 的属性,基本BOM的属性和方法都是window的
- 所有通过var定义在全局作用域中的变量、函数都会变成window 对象的属性和方法
- window 对象下的属性和方法调用的时候可以省略 window
定时器-延时函数
-
JavaScript 内置的一个用来让代码延迟执行的函数,叫setTimeout
-
语法:
setTimeout(回调函数,等待的毫秒数)
-
setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行,平时省略 window
-
清除延迟函数
clearTimeout(延迟函数id)
-
注意点
- 延迟器需要等待,所以后面产生的代码先执行
- 每一次调用延时器都会产生一个新的延时器
JS执行机制
JavaScript 语言的一大特点就是单线程
,也就是说,同一时间只能做一件事
这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:
如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个
线程。于是,JS 中出现了同步
和异步
。
-
同步
前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同
步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。 -
异步
你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。他们的本质区别:
这条流水线上各个流程的执行顺序不同。
同步任务
同步任务都在主线程上执行,形成一个执行栈。
异步任务
JS 的异步是通过回调函数实现的。
一般而言,异步任务有以下三种类型:
- 普通事件,如 click、resize 等
- 资源加载,如 load、error 等
- 定时器,包括 setInterval、setTimeout 等
异步任务相关添加到任务队列中(任务队列也称为消息队列)。
执行步骤
- 先执行执行栈中的同步任务。
- 异步任务放入任务队列中。
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待 状态,进入执行栈,开始执行
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)
location对象
-
location 的数据类型是对象,他拆分并保存了URL地址的各个组成部分
-
常用属性和方法:
-
href
属性获取完整的 URL 地址,对其赋值时用于地址的跳转// 三秒之后跳转到百度首页 setTimeout(function () { location.href = 'http://baidu.com' }, 3000)
-
search
属性获取地址中携带的参数,符号?
的后面部分<form action=""> <input type="text" name="uname"> <input type="password" name="pwd"> <button>提交</button> </form>
提交后新网址名为:http://127.0.0.1:5500/JavaScript初步练习/24.3/11/03-search.html?uname=123&pwd=123123
-
hash
属性获取地址中的哈希值,符号#
后面部分<a href="#/my">我的</a> <a href="#/friend">关注</a> <a href="#/download">下载</a>
点击链接后新网址名为:http://127.0.0.1:5500/JavaScript初步练习/24.3/11/04-hash.html#/my
-
reload()
方法用来刷新当前页面,传入参数 true 时表示强制刷新<button>刷新页面</button> <script> const btn = document.querySelector('button') btn.addEventListener('click', function () { location.reload(true); }) </script>
-
navigator对象
navigatoe
的数据类型是对象,该对象记录了浏览器自身的相关信息- 常用属性和方法:
userAgent
检测浏览器的版本及平台
代码如下:
// 检测userAgent(浏览器信息)
const userAgent = navigator.userAgent;
// console.log(userAgent);
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/);
// console.log(android);
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/);
// console.log(iphone);
if (android || iphone)
location.href = 'http://baidu.com';
histroy对象
history
的数据类型是对象,主要管理历史记录,该对象与浏览器地址栏的操作相对应,如前进、后退、历史记录等
常用属性和方法:
方法 | 作用 |
---|---|
back() | 可以实现后退功能 |
forward() | 可以实现前进功能 |
go(参数) | 前进后退功能 参数如果是1 前进1个页面 如果是-1 后退一个页面 |
本地存储
- 数据存储在
用户浏览器
中 - 设置、读取方便、甚至页面刷新不丢失数据
- 容量较大,sessionStorage和localStorage约 5M 左右
localStorage存储方式
**作用:**可以将数据永久存储在本地(用户的电脑),除非手动删除,否则关闭页面也会存在
特性:
- 可以多窗口(页面)共享(同一浏览器可以共享)
- 以键值对的形式存储使用
- 本地存储只能存储
字符串
存储数据
格式:
// key 为存储信息的名称 vlaue 为存储信息的内容
localStorage.setItem(key,value)
代码如下:
localStorage.setItem('uname', 'Tom');
读取数据
格式:
// key 为读取信息的名称
localStorage.getItem(key)
代码如下:
// 2. 读取数据
console.log(localStorage.getItem("uname"));
结果如下:
删除数据
格式:
localStorage.removeItem(key)
代码如下:
// 3. 删除数据
localStorage.removeItem("uname");
更改数据
格式:
localStorage.setItem(key,value)
sessionStorage存储方式
特性:
生命周期为关闭浏览器窗口
- 在同一窗口(页面)下数据可以共享
- 以键值对的形式存储使用
- 用法跟 localStorage 基本相同
存储复杂数据类型
-
本地只能存储字符串,无法存储复杂数据类型
-
需要将复杂数据类型转换成JSON字符串,再存储到本地
-
**语法:**JSON.stringigy(复杂数据类型)
const obj = {} localStorage.setItem('obj',JSON.stringify(obj)) console.log(JSON.parse(localStorage.getItem('obj')));
正则表达式
正则表达式大白话就是一个能判定你的输入内容是否符合设计者规定的一个式子
作用:
- 测试字符串是否合规
- 替换某确定的文本
- 从字符串中提取一个子字符串/是否包含某子字符串
语法:
const regexp_1 = /a/;
const regexp_2 = new Regexp('a');
这是两种创建正则表达式的方法,两种方法完全等价
举例:
const regexp = /a/;
const string = 'abc';
console.log(regexp.test(string));
结果:
true
常见的正则表达式属性
三种匹配规则
匹配规则符号 | 规则含义 |
---|---|
i | 匹配时忽略大小写 |
g | 执行全局匹配(会匹配整个语句,而非匹配到第一个目标后终止) |
m | 执行多行匹配 |
语法:
const regexp_1 = /a/i;
const regexp_2 = /a/g;
const regexp_3 = /a/m;
const regexp_4 = new Regexp('a', 'i');
const regexp_5 = new Regexp('a', 'g');
const regexp_6 = new Regexp('a', 'm');
const regexp_7 = /a/igm;
const regexp_8 = new Regexp('a', 'igm');
上面展示了两种正则表达式定义方法下的匹配规则的语句:
- 对于第一种正则表达式,我们直接在斜线后加上匹配规则对应的符号即可
- 对于第二种正则表达式,我们在括号里传入第二个参数即可,参数仍然是对应的符号
- 起始不传入参数、斜线后面不写任何东西,代表了默认情况,也就是普通的正则匹配
- 可以有多种规则同时执行,例如可以同时忽略大小写,全局匹配(需要几个就加几个参数符号)
举个例子:
const regexp_1 = /a/;
const regexp_2 = /a/i;
const str = 'Abc';
console.log(regexp_1.test(str));
console.log(regexp_2.test(str));
结果入下:
false
true
五种常见的属性
这五种属性是正则表达式的属性,即是例如 /a/ 的属性
属性 | 说明 |
---|---|
ignoreCase | 返回一个布尔值 。true 代表正则表达式设置了 i 匹配规则(忽略大小写),false 代表未设置 |
global | 返回一个布尔值 。true 代表正则表达式设置了 g 匹配规则(全局匹配),false 代表未设置 |
multiline | 返回一个布尔值 。true 代表正则表达式设置了 m 匹配规则(多行匹配),false 代表未设置 |
lastIndex | 返回一个 int 值,表示下一次搜索开始的索引位置,只在设置了 g 匹配规则时才有实用意义 |
source | 返回一个字符串 ,字符串是正则表达式的字符串形式(不含斜线) |
代码如下:
const regexp = /a/im;
console.log(regexp.ignoreCase);
console.log(regexp.global);
console.log(regexp.multiline);
console.log(regexp.lastIndex);
console.log(regexp.source);
结果如下:
true
false
true
0
a
规则g的使用详解
当我们遇见这样的字符串str="s_s_s"
,如果使用普通的正则表达式regexp=/s/
,最后结果只能匹配到第一个 s
所以这时候就需要全局匹配 p
代码如下:
const regexp = /s/g
const str = 's_s_s_s_s_s_s_s'
regexp.test(str)
console.log(regexp.lastIndex)
regexp.test(str)
console.log(regexp.lastIndex)
regexp.test(str)
console.log(regexp.lastIndex)
regexp.test(str)
console.log(regexp.lastIndex)
regexp.test(str)
console.log(regexp.lastIndex)
结果如下:
1
3
5
7
9
这里输出的是下一个开始的索引,所以所有的索引-1
才是 s 位置的索引
常见的正则表达式方法
test方法
test() 方法是正则表达式最常用一个方法,用于检测一个字符串是否匹配某个模式
如果是则返回 true,否则就返回 false
格式:
const regexp = /a/g; // 正则表达式
const str = 'abc';
console.log(regexp.test(str));
正则子表达式匹配
先匹配一下st_,匹配到之后再匹配一下里面的
t 和 _ ,也就是我想要一次把st_ 、t 和 _ 都匹配出来
代码如下:
const regexp = /s(t)(_)/;
const str = 'st_t_s_s_';
const str2 = 's_t_s_s_';
console.log(regexp.test(str));
console.log(regexp.test(str2));
结果如下:
true
false
只有整体匹配成功,后面的子字符串匹配才会被执行匹配,否则如果整体没有匹配到,即使子字符串能匹配到内容,也都会被返回空值
exec方法
exec()方法类似于正则子表达式匹配,只不过 exec() 方法返回的是一个数组
当整句匹配失败时,会返回一个null的空数组;
否则,有:数组的第0个元素存储的是整句匹配到的字符串,第1个元素存放的是第一个引用型分组(子表达式)匹配的字符串,第2个元素存放的是第二个引用型分组(子表达式)匹配的字符串,依次类推。
代码如下:
const regexp = /s(t)(_)/
const str = 'st_s_s_s_s_s_s_s'
const list = regexp.exec(str)
console.log(list)
结果如下:
(3) ['st_', 't', '_', index: 0, input: 'st_s_s_s_s_s_s_s', groups: undefined]
0: "st_"
1: "t"
2: "_"
groups: undefined
index: 0
input: "st_s_s_s_s_s_s_s"
length: 3
replace替换
格式:
字符串.replace(/正则表达式/,'替换的文本')
代码如下:
const str = 'java是一门编程语言,学完JAVA工资很高';
console.log(str.replace(/java/i, '前端'));
console.log(str.replace(/java/g, '前端'));
console.log(str.replace(/java/ig, '前端'));
结果如下:
// 前端是一门编程语言,学完JAVA工资很高
// 前端是一门编程语言,学完JAVA工资很高
// 前端是一门编程语言,学完前端工资很高
正则表达式常见字符与对应含义
[\u4e00-\u9fa5]
:为所有汉字
边界符
^a
:表示首字母匹配a$
:表示尾字母匹配^a$
:表示精准匹配,必须为 a 表示的格式
代码如下:
// 边界符
console.log(/^a/.test('aaaa')); // true
console.log(/^a/.test('baaa')); // false
console.log(/a$/.test('baaa')); // true
console.log(/a$/.test('aaab')); // false
// 精准匹配
console.log(/^a$/.test('a')); // true
console.log(/^a$/.test('aa')); // false
console.log(/^aa$/.test('aa')); // true
console.log(/^aa$/.test('aabb')); // false
console.log(/^a+$/.test('aaaa')); // true
console.log(/^a+$/.test('aabb')); // false
量词
-
a+
:表示 a 有一个或多个(即 a 的数量>=1) -
a*
:表示 a 有零个或多个(即 a 的数量>=0) -
a?
:表示 a 有零个或一个(即 a 的数量为0或1) -
a{n}
:表示 a 有 n 个(即 a 的数量==n) -
a{n,}
:表示 a 至少有 n 个 (即 a 的数量>=n) -
a{n,m}
:表示 a 至少有 n 个,最多有 m 个(即 n<= a 的数量 <= m)
代码如下:
// 量词
console.log(/a+/.test('a')); // true
console.log(/a+/.test('aaaa')); // true
console.log(/a+/.test('bb')); // false
console.log(/a+/.test('bb')); // false
console.log(/a*/.test('b')); // true
console.log(/a*/.test('baa')); // true
console.log(/a?/.test('b')); // true
console.log(/a?/.test('ba')); // true
console.log(/a?/.test('baa')); // true
console.log(/a{2}/.test('baa')); // true
console.log(/a{2}/.test('baaa')); // false
console.log(/a{2,}/.test('baaa')); // true
console.log(/^a{2,4}$/.test('aaaa')); // true
console.log(/^a{2,4}$/.test('aaaaa')); // false
字符类
-
[]
里面加上 - 连字符- 后面的字符串只要包含 abc 中任意一个字符,都返回 true
// 字符类 console.log(/[abc]/.test('a')); // true console.log(/[abc]/.test('d')); // false console.log(/^[abc]$/.test('c')); // true console.log(/^[abc]$/.test('ab')); // false console.log(/^[abc]{2}$/.test('ab')); // true
- 比如:
- [a-z]:表示a到z的26个小写英文字母都可以
- [A-Z]:表示A到Z的26个大写英文字母都可以
- [0-9]:表示0~9的数字都可以
-
[]
里面加上^
取反符号- 比如:
- [^a-z]:匹配除了小写字母以外的字符
- 注意要写到中括号里面
- 比如:
-
.
匹配除换行符之外的任何单个字符代码如下:
console.log(/^a.$/.test('ab')); // true console.log(/^a.$/.test('aac')); // false
-
预定义
:是指某些常见模式的简写方式预定类 说明 \d 匹配0~9之间的任一数字,相当于[0-9] \D 匹配所有0~9以外的字符,相当于[^0-9] \w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9] \W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9] \s 匹配空格(包括换行符、制表符、空格符等),相当于[\t\r\n\v\f] \S 匹配非空格的字符,相当于[^\t\r\n\v\f] 代码如下:
console.log(/\d{4}/.test('2025')) // true
JS高级运用
作用域
作用域对程序执行的影响及作用域链的查找机制,使用闭包函数创建隔离作用域避免全局变量污染。
- 作用域(scope)规定了变量能够被访问的 “范围” ,离开了这个 “范围” 变量便不能被访问
- 作用域分为:
- 局部作用域
- 全局作用域
局部作用域
局部作用域
分为函数作用域
和块作用域
1. 函数作用域
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问
function getFun(a) {
const num = 100;
a = 200;
}
console.log(num); // 此处报错 num is not defined
console.log(a); // 此处报错 a is not defined
总结:
- 函数内部声明的变量,在函数外部无法被访问
- 函数的参数也是函数内部的局部变量
- 不同函数内部声明的变量无法互相访问
- 函数执行完毕后,函数内部的变量实际被清空了
2. 块作用域
在JavaScript中使用{}
包裹的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问
let i = 100;
for (let i = 1; i < 3; i++) {
let j = 200;
console.log(i);
}
console.log(i);
console.log(j); // 此处报错
总结:
- let 声明的变量会产生块作用域,var 不会产生块作用域
- const 声明的常量也会产生块作用域
- 不同代码块之间的变量无法相互访问
- 推荐使用 let 或 const
全局作用域
<script> 标签和 .js 文件的【最外层】就是所谓的全局作用域,再次声明的变量在函数内部也可以被访问。
全局作用域中声明的变量,任何其它作用域都可以被访问
let a = 100;
!function () {
console.log(a);
}
注意:
- 为 window 对象动态添加的属性默认也是全局的,不推荐!
- 函数中未使用任何关键字声明的变量为全局变量,不推荐!!
- 尽可能少的声明全局变量,防止全局变量污染
作用域链
作用域链本质上是底层的变量查找机制
- 在函数执行时,会
优先查找当前
函数作用域中查找变量 - 如果当前作用域查找不到则会依次
逐级查找父级作用域
直到全局作用域
let a = 10;
let b = 20;
function f() {
let a = 100;
function g() {
console.log(a);
}
g();
}
f(); // 100
总结:
- 嵌套关系的作用域串联起来形成作用域链
- 相同作用域链中按着从小到大的规则查找变量
- 子作用域能够访问父作用域,父级作用域无法访问子作用域
垃圾回收机制
垃圾回收机制
(Garbage Collection) 简称 GC
JS 中内存
的分配和回收都是自动完成
的,内存在不使用的时候会被垃圾回收器
自动回收
内存的生命周期
JS环境中分配的内存,一般有如下生命周期
:
内存分配
:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存内存使用
:即读写内存,也就是使用变量、函数等内存回收
:使用完毕,由垃圾回收器
自动回收不再使用的内存
说明:
- 全局变量一般不会回收(关闭页面回收)
- 一般情况下
局部变量的值
,不用了,会被自动回收
掉
内存泄漏:程序中分配的内存
由于某种原因程序未释放
或无法释放
叫做内存泄漏
堆栈空间分配区别:
- 栈(操作系统):由
操作系统自动分配释放
函数的参数值、局部变量等,基本数据类型放到栈里面 - 堆(操作系统):一般由程序员分配释放,若程序员不释放,由
垃圾回收机制
回收。复杂数据类型
放到堆里面
下面介绍两种常见的浏览器垃圾回收算法
:引用计数法
和 标记清除法
引用计数
IE采用的引用计数算法,定义"内存不再使用",就是看一个对象是否有指向它的引用,没有引用了就回收对象
算法:
- 跟踪记录被
引用的次数
- 如果被引用了一次,那么就记录次数1,多次引用会
累加++
- 如果减少一个引用就
减1
– - 如果引用次数是
0
,则释放内存
例如:
let person = { // 此时 person 指向对象 对象的引用为 1
age: 18,
name: '佩奇'
}
let p = person // 此时 p 也指向对象 对象的引用加一为 2
person = 1 // 此时 person 不指向对应对象地址 引用减一为 1
p = null // 此时 p 也不指向对应地址 引用减一为 0 释放内存
但它却存在一个致命的问题:嵌套引用
(循环引用)
如果两个对象相互引用
,尽管他们已不再使用,垃圾回收器也不会进行回收,导致内存泄漏
function fn() {
let o1 = {} // o1对象的引用加1为 1
let o2 = {} // o2对象的引用加1为 1
o1.a = o2 // o2对象的引用加1为 2
o2.a = o1 // o1对象的引用加1为 2
return '引用计数无法回收'
}
fn(); // 函数引用结束 o1、o2 对象的引用减1为 1 不为0 则不会进行垃圾回收
因为它们的引用次数永远不会是0.这样的相互引用如果说很大量的存在就会导致大量的内存泄漏
标记清除法
- 现代的浏览器已经不再使用引用计数算法
- 现代浏览器通用的大多是基于
标记清除算法
的某些改进算法,总体思想都是一致
核心:
- 标记清除算法将 “不在使用的对象” 定义为 “
无法到达的对象
” - 就是从
根部
(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达
的对象,都是还需要使用
的 - 那些
无法
由根部出发触及到的对象被标记
为不再使用,稍后进行回收
闭包
**概念:**一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
**简单理解:**闭包 = 内层函数 + 外层函数的变量
简单代码:
function outer() {
const a = 1;
function f() {
console.log(a);
}
f();
}
outer();
闭包作用
封闭数据,提供操作,外部也可以访问函数内部的变量
闭包的基本格式:
function outer() {
let i = 1;
function fn() {
console.log(i);
}
return fn;
}
const fun = outer();
fun(); // 1
// 外层函数使用内部函数的变量
优化一下
function outer() {
let i = 1;
return function() {
console.log(i);
}
}
const fun = outer();
fun(); // 1
// 外层函数使用内部函数的变量
闭包应用
实现数据的私有
比如,我们要做个统计函数调用次数,函数调用一次,就++
let i = 0;
function fun() {
i++;
console.log(`函数被调用了${i}次`);
}
但是,这个 i 是个全局变量,很容易被修改
优化一下
function count() {
let i = 0;
function fn() {
i++;
console.log(`函数被调用${i}次`);
}
return fn;
}
const fun = count();
- 此时,fun函数是全局变量,不会回收
- fun函数等于count函数的返回值 fn函数
- fn函数中应用到 局部变量 i 所以局部变量 i 不会被回收
变量提升
变量提升是 JavaScript 中比较 “奇怪” 的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)
注意:
- 变量在未声明即被访问时会报语法错误
- 变量在var声明之前即被访问,变量的值为 undefined
- let/const 声明的变量不存在变量提升
- 变量提升出现在相同作用域当中
- 实际开发中推荐先声明再访问变量
变量提升是设么流程?
- 先把var变量提升到当前作用域于最前面
- 只提升变量声明,不提升变量赋值
- 然后依次执行代码
函数进阶
函数提升
函数提升与变量提升比较相似,是指函数在声明之前即可被调用
// 函数调用
fun();
// 声明函数
function fun() {
console.log('函数提升了');
}
// 函数提升了
但在赋值函数中
fun();
var fun = function () {
console.log('函数提升了');
}
// 报错了
// 此相当于
var fun
fun();
fun = function () {
console.log('函数提升了')
}
总结:
- 函数提升能够使函数的声明调用更灵活
- 函数表达式不存在提升的现象
- 函数提升出现在相同作用域当中
函数参数
函数参数的使用细节,能够提升函数应用的灵活性
- 动态参数
- 剩余参数
动态参数
arguments
是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参
function getSum() {
let sum = 0;
for (let i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
// console.log(arguments);
console.log(sum);
}
getSum(2, 3)
getSum(2, 3, 5, 7, 8, 9, 3, 45)
总结:
- arguments 是一个
伪数组
,只存在于函数中 - arguments 的作用是动态获取函数的实参
- 可以通过 for 循环依次得到传递过来的实参
剩余参数
- … 是语法符号,置于最末函数形参之前,用于获取
多余
的实参 - 借助 … 获取的剩余实参,是个
真数组
function fun(a, b, ...arr) {
console.log(arr);
}
fun(2, 3) // 数组arr为 []
fun(2, 3, 8, 5, 6, 5) // 数组arr为 [8,5,6,5]
开发时,还是提倡使用剩余参数
箭头函数
引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
箭头函数更适用于那些本来需要匿名函数的地方
基本语法
-
基本语法
const fn = (x) => { console.log(x); } fn(1); // 1
-
只有一个参数,可以省略括号
const fn = x => { console.log(x); } fn(10); // 10
-
只有一行代码,可以省略大括号,并且无需写 return 直接返回值
const fn = x => x + 100; console.log(fn(100)); // 200
-
箭头函数可以返回一个对象
const fn = (uname) => ({ uname: uname }) console.log(fn('刘德华'));
箭头函数参数
箭头函数没有动态参数,但有 剩余参数
利用剩余参数求和:
const getSum = (...arr) => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += i;
}
return sum;
}
console.log(getSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
箭头函数 this
在箭头函数出现之前,每一个新函数根据它是被如何调用的
来定义这个函数的this值
箭头函数不会创建自己的this
,它只会从自己的作用域链的上一层沿用 this
-
普通函数中的this
// 1. 普通的this console.log(this); // window // 普通函数 function fn() { console.log(this); // window } window.fn(); // 对象方法里面的this const obj = { name: 'Tom', sayHi: function () { console.log(this); // obj } } obj.sayHi(); //
-
箭头函数中的this
// 是上一层作用域的this 指向 const fn = () => { console.log(this); // window } fn(); // 对象方法箭头函数this // sayHi中没有this 沿用上一级obj的this window调用obj 所以this指向window const obj = { uname: 'Tom', sayHi: () => { console.log(this); // window } } const obj = { uname: 'Tom', sayHi: function () { let i = 10; const count = () => { console.log(this); // obj } count() } } obj.sayHi();
-
DOM对象
// DOM对象 // 普通函数 btn.addEventListener('click', function () { console.log(this); // button }) // 箭头函数 btn.addEventListener('click', () => { console.log(this); // window })
因此,DOM事件回调函数不建议使用箭头函数
解构赋值
使用解构赋值简洁语法快速为变量赋值
数组解构
基本语法
- 赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
- 变量的顺序对应数组单元值的位置依次进行赋值操作
const arr = [1, 2, 3];
const [a, b, c] = arr;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
典型应用
变量交互:
let [a, b] = [1, 2];
[b, a] = [a, b];
console.log(a); // 2
console.log(b); // 1
注意:
- [a,b]=[1,2]这种代码在上一行有代码时,上一行代码要注意加上 ; 号,不然本行会和上一行代码看成一行代码
数组解构会遇到的各种情况
- 变量多而单元值少的情况下
const [a, b, c, d] = [1, 2, 3];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(d); // undefined
- 变量少而单元值多的情况下
const [a, b, c] = [1, 2, 3, 4];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
- 利用剩余参数解决变量少而单元值多的情况
const [a, b, ...c] = [1, 2, 3, 4, 5, 6];
console.log(a); // 1
console.log(b); // 2
console.log(c); // [3,4,5,6]
- 防止有 undefined 传递单元值的情况,可以设置默认值
const [a = 0, b = 0] = [1];
console.log(a); // 1
console.log(b); // 0
- 按需导入,忽略某些返回值
const [a, , c, d] = [1, 2, 3, 4];
console.log(a); // 1
console.log(c); // 3
console.log(d); // 4
- 支持多维数组的结构
const [a, b, c] = [1, 2, [3, 4]];
console.log(a); // 1
console.log(b); // 2
console.log(c); // [3,4]
// 或
const [a, b, [c, d]] = [1, 2, [3, 4]];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(d); // 4
对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
基本语法:
- 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
- 对象属性的值将被赋值给与属性名
相同的
变量 - 注意解构的变量名不要和外面的变量名冲突否则报错
- 对象中找不到与变量名一致的属性时变量值为 undefined
格式如下:
const user = {
uname: 'Tom',
age: 18
}
const { uname, age } = user;
console.log(uname); // Tom
console.log(age); // 18
对象解构的变量名,可以重新改名
const { uname: username, age } = { uname: 'Tom', age: 18 };
console.log(username); // Tom
console.log(age); // 18
数组对象的解构
const pig = [
{
uname: '佩奇',
age: 18
}
]
const [{ uname, age }] = pig;
console.log(uname); // 佩奇
console.log(age); // 18
多级对象解构
const pig = {
name: '佩奇',
family: {
mother: '猪妈妈',
father: '猪爸爸',
border: '乔治'
},
age: 6
}
const { name, family: { mother, father, border }, age } = pig;
console.log(name); // 佩奇
console.log(mother); // 猪妈妈
console.log(father); // 猪爸爸
console.log(border); // 乔治
console.log(age); // 6
构造函数
**构造函数:**是一种特殊的函数,主要用来初始化对象
**使用场景:**常规的 {…} 语法允许创建一个对象。比如我们创建了佩奇的对象,继续创建乔治的对象还需要重新写一遍,此时可以通过构建函数来快速创建多个类似的对象
语法:
function Obj(参数){
this.属性名 = 参数;
......
}
const obj = new Obj(实参);
注意:
- 函数名首字母大写
- 生成新对象时必须加上
new
举例:
// 创建商品构造函数
function Goods(name, price, count) {
this.name = name;
this.price = price;
this.count = count;
}
// 创建三个对象
const XM = new Goods('小米', 1999, 20);
const HW = new Goods('华为', 3999, 59);
const Vivo = new Goods('Vivo', 1888, 100);
对象生成过程
-
创建新对象
const obj = new Obj(实参)
new
代表创建了一个新对象 -
这时
this
指向新的对象this.name = name
代表在对象中创建一个新的属性并赋值
-
返回新对象
const obj = new Obj(参数)
这时
obj
接受来自构造函数返回的对象,这里不需要 return 来进行返回操作,构造函数会自动返回对象
实例成员
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)
function Person() {
this.name = '小明';
this.sayHi = () => console.log('大家好');
}
// 实例对象
const p1 = new Person();
console.log(p1);
// 实例属性
console.log(p1.name);
// 实例方法
p1.sayHi();
说明:
- 为构造函数传入参数,创建结构相同但值不同的对象
- 构造函数创建的实例对象彼此独立互不影响
静态成员
构造函数的属性和方法被称为静态成员(静态属性和静态方法)
function Person() { }
Person.a = 2; // 静态属性
Person.sayHi = function () { // 静态方法
console.log(this);
}
Person.sayHi();
说明:
- 静态成员只能构造函数来访问
- 静态方法中的this指向构造函数
比如:Date.now() Math.PI Math.random()
内置构造函数
在 JavaScript 中中最主要的数据类型有6种:
基本数据类型:
字符串 数值 布尔型 underfied null
引用类型:
对象
但是我们会发现有些特殊情况:
// 普通字符串
const str = 'andy';
console.log(str.length); // 4
其实字符串、数值、布尔等基本数据类型也有专门的构造函数,这些我们称为包装类型
// 虽然表面上是这样
const str = 'pink'
// 其实js底层是这么执行的
// 把简单数据类型包装 为了引用数据类型
const str = new String('pink')
JS中几乎所有的数据都可以基于构成函数创建
Object
Object
是内置的构造函数,用于创建普通对象
const obj = new Object();
创建对象建议使用字面量,不用 Object 构造函数创建
获取所有属性
Object.keys(object)
静态方法获取对象中的所有属性(键)
语法:
const o = { name: 'Tom', age: 18 }
const arr = Object.keys(o);
console.log(arr);
// ['name','age']
**注意:**返回的是一个数组
获取所有属性值
Object.values(object)
静态方法获取对象中的所有属性值
语法:
const o = { name: 'Tom', age: 18 }
const arr = Object.values(o)
console.log(arr);
// ["Tom",18]
**注意:**返回的是一个数组
对象拷贝
Object.assign(获取拷贝的对象,被拷贝的对象)
静态方法常用于对象拷贝
**使用:**经常使用的场景给对象添加属性
解释:将obj1
拷贝给 obj2
。执行完毕后,obj2 的值会被更新。
作用:将 obj1 的值追加到 obj2 中。如果对象里的属性名相同,会被覆盖。
技巧:该方法也会将获取拷贝的对象返回
语法:
const o = { name: 'Tom', age: 18 }
Object.assign(o, { gender: '女' })
console.log(o);
// {name:'Tom',age:18,gender:'女'}
Array
Array
是内置的构造函数,用于创建数组
const arr = new Array()
创建数组建议使用字面量,不用 Array 构造函数创建
重点内置方法:
方法 | 作用 | 说明 |
---|---|---|
forEach | 遍历数组 | 不返回数组,经常用于查找遍历数组元素 |
filter | 过滤数组 | 返回新数组,返回的是筛选满足条件的数组元素 |
map | 迭代数组 | 返回新数组,返回的是处理之后的数组元素,想要使用返回的新数组 |
reduce | 累计器 | 返回累计处理的结果,经常用于求和等 |
迭代数组
array.map()
方法——迭代数组
是数组原型的一个函数,该函数用于对数组中的每个元素进行处理,将其转换为另一个值,最终返回一个新的数组,该数组包含了经过处理后的每个元素。
语法:
array.map(callback (currentValue[, index[, array]]){
// 执行代码
}[, thisArg])
其中:
-
callback
:表示对数组中的每个元素要执行的函数-
currentValue
:表示正在处理的当前元素 -
index
:可选参数,表示正在处理的当前元素的索引 -
array
:可选参数,表示正在处理的当前数组
-
-
thisArg
:可选参数,表示执行 callback 函数时的this
值
使用场景:
- 将一组数据转换为另一种形式或格式;
- 对一组数据进行过滤、去重等操作;
- 对一组DOM元素进行修改等操作;
map方法的举例使用
-
使用map方法将数组的元素乘以2并返回新数组
代码如下:
const arr = [1, 2, 3]; const arr_2 = arr.map(function (num) { return num * 2; }) console.log(arr_2);
结果如下:
Array(3) 0: 2 1: 4 2: 6 length: 3
-
将字符数组转换为数字数组
代码如下:
const str = ['1', '2', '3']; const num = str.map(function (str) { return Number(str); }) console.log(typeof (num[0]));
结果如下:
number
-
需要注意的是,在使用
map()
方法时,我们可以选择传递可选参数thisArg
来设置回调函数的this
值。如果不传递thisArg
参数,则默认情况下,回调函数的this
值为undefined
代码如下:
const num = [1, 2, 3]; const obj = { product: 2 } const num_2 = num.map(function (num) { return num * this.product; }, obj) console.log(num_2);
结果如下:
Array(3) 0: 2 1: 4 2: 6 length: 3
遍历数组
array.forEach()
方法用于调用数组的每一个元素,并将元素传递给回调函数
主要使用场景:遍历数组的每个元素
语法:
被遍历的数组.forEach(function(当前数组元素[,当前元素索引号]){
// 函数体
})
与 map方法的区别为 不返回新数组
举例:
const arr = ['red', 'green', 'blue'];
arr.forEach(function (item, index) {
console.log(item);
console.log(index);
})
// red
// 0
// green
// 1
// blue
// 2
过滤数组
array.filter()
方法创建一个新的数组,新数组中的元素是通过检查值得顶数组中符合条件的所有元素
主要使用场景:筛选数组符合条件的元素,并返回筛选之后元素的新数组
语法:
const arr1 = [10, 20, 30, 40, 50, 60];
const arr2 = arr1.filter(item => {
if (item > 30)
return item;
})
// arr2 => [40,50]
累计器
array.reduce(function(上一次值,当前值){},初始值)
作用:reduce 返回累计处理的结果,经常用于求和等
代码如下:
const arr = [1, 2, 3, 4];
const sum = arr.reduce((prev, current) => prev + current,10)
console.log(sum);
// 20
过程解析:
const arr = [1, 2, 3, 4];
const sum = arr.reduce((prev, current) => {
console.log(prev); // 10 11 13 16
console.log(current); // 1 2 3 4
return prev + current;
}, 10)
console.log(sum); // 20
- 如果没有起始值,则上一次值以数组的第一个数组元素的值出现
- 每一次循环,把返回值给做为 下一次循环的上一次值
- 如果有起始值,则起始值做为上一次值
如果累计对象是个 对象
// 设计构造函数 薪资
const salary = function (name, salary) {
this.name = name;
this.salary = salary;
}
// 创建薪资数组
const arr = [
new salary('张三', 10000), new salary('李四', 10000), new salary('王五', 10000)
]
// 累计器计算薪资
// 第一种方法
const sum = arr.reduce(function (prev, current) {
return prev + current.salary
}, 0)
// 第二种方法
const sumSalary = arr.reduce((prev, { salary: current }) => prev + current, 0);
// console.log(arr);
console.log(sumSalary); // 3000
console.log(sum); // 3000
不论哪种方法,都要加上 初始值 , 必须使得其为数字类型,因为返回值返回到 上一次值位置
其它方法
array.方法(参数)
方法 | 作用 | 说明 |
---|---|---|
join | 拼接数组元素 | 数组元素拼接为字符串,返回字符串 |
find | 查找数组元素 | 查找元素,返回符合测试条件的第一个数组元素,如果没有符合条件的则返回 undefined |
every | 检测数组元素(全部满足) | 检测数组所有元素是否都符合指定条件,如果所有元素都通过检测返回 true ,否则返回 falses |
some | 检测数组元素(部分满足) | 检测数组中的元素是否满足指定条件,如果部分元素满足条件返回 true ,否则返回 false |
concat | 合并数组 | 合并两个数组,返回生成新数组 |
sort | 排序数组 | 对原数组单元值排序,改变原数组 |
splice | 替换数组 | 删除或替换原数组单元 |
reverse | 反转数组 | 将数组反转 |
findIndex | 查找索引 | 查找数组中满足条件的第一项元素的索引值 |
slice | 提取数组元素 | 提取数组元素,返回一个新数组 |
Array.方法(array)
方法 | 作用 | 说明 |
---|---|---|
isArray | 判断是否为数组 | 判断是否为数组,为数组返回 true ,不为数组返回 false |
from | 伪数组转换真数组 | 返回新数组,将伪数组转换成真数组 |
拼接数组元素
array.join()
方法就是将数组数据中的每个元素都转为字符串
,用自定义的连接符分割
- 返回值是字符串
const arr = ['h', 'e', 'l', 'l', 'o'];
console.log(arr.join(''));
// 结果如下:
// hello
// join('')中间是空字符串,所以每一个元素中间没有分隔符
join()
将数组转化为页面元素
const arr = ['第一次', '第二次', '第三次', '第四次'];
document.querySelector('body').innerHTML = '<li>' + arr.join('</li><li>') + '</li>';
// 相当于
// <li>第一次</li><li>第二次</li><li>第三次</li><li>第四次</li>
查找数组元素
array.find()
语法:
array.find(callback(element[, index[, array]])[, thisArg])
代码如下:
const arr = [
{ name: 'Tom', age: 18 },
{ name: 'Jack', age: 20 },
{ name: 'dog', age: 3 }
]
const Tom = arr.find(item => item.name === 'Tom')
console.log(Tom); // {name: 'Tom', age: 18}
检测数组元素(全部满足)
array.every()
语法:
array.every(callback(element[, index[, array]])[, thisArg])
代码如下:
const arr = [6, 8, 9, 7];
const boole = arr.every(item => item > 5);
console.log(boole); // true
注意:
-
every() 不会对空数组进行检测
-
every() 不会改变原始数组
检测数组元素(部分满足)
array.some()
语法:
array.some(callback(element[, index[, array]])[, thisArg])
代码如下:
const arr = [1, 5, 6, 8];
const boole = arr.some(item => item < 5)
console.log(boole); // true
注意:
-
some() 不会对空数组进行检测
-
some() 不会改变原始数组
合并数组
array.concat(内容)
返回一个新的数组 / 也可用于字符串
代码如下:
const arr = [1, 2, 3];
const arr_2 = arr.concat('tom');
console.log(arr_2);
结果如下:
[1,2,3,'tom']
数组排序
array.sort()
- sort():按元素首个数字大小升序
- sort(function(a,b){}):传入比较器函数,判断升序或降序
- a - b < 0: 代表后一个比前一个大,就是升序
- b - a < 0: 代表前一个比后一个大,就是降序
- a - b = 0 或 b - a = 0: 前后两个数相等
代码如下:
let array = [123,78,45,96,54,12,6841,1245,231,54,78];
array.sort();
console.log(array);
array.sort(function(a,b){
return b-a;
});
console.log(array)
array.sort(function(a,b){
return a-b;
});
console.log(array)
结果如下:
(11) [12, 123, 1245, 231, 45, 54, 54, 6841, 78, 78, 96] //元素首个数字排序
(11) [6841, 1245, 231, 123, 96, 78, 78, 54, 54, 45, 12]
(11) [12, 45, 54, 54, 78, 78, 96, 123, 231, 1245, 6841]
反转数组
array.reverse()
反转整个数组
代码如下:
const arr = [1, 2, 3];
arr.reverse();
console.log(arr);
结果如下:
[3,2,1]
查找元素的索引值
array.findIndex()
代码如下:
const arr = [1, 2, 3, 4, 5, 6, 7];
const o = arr.findIndex(item => item > 5);
console.log(o); // 5
提取数组元素
格式:
array.slice([begin[,end]])
begin
:- 该索引处开始提取原数组的元素
- 如果该元素为负数,则表示从原数组中的倒数第几个开始,例如:slice(-2),代表着从倒数第二个开始到最后
- 如果省略begin,则默认从索引0开始
end
:- 在该索引结束处停止提取元素
- slice 会提取原数组中索引从 begin 开始到 end 结束的所有元素(包含 begin 但不包含 end)
- 例如:slice(1,4)代表从索引1开始到索引3结束
- 如果省略 end ,则slice会一直提取到原数组末尾,如果 end 大于原数组长度,slice也会提取到原数组末尾
基础的用法
-
简单的复制
代码如下:
const arr = [1, 2, 3]; const arr2 = arr.slice(); console.log(arr2);
结果如下:
[1,2,3]
-
获取从N开始的子数组
console.log(arr.slice(N));
-
从末尾N开始的子数组
console.log(arr.slice(-N));
-
获取数组的前N个
console.log(arr.slice(0,N));
判断是否为数组
Array.isArray(array)
:判断是否为数组
代码如下:
const fruits = ["apples","oranges","pears",10];
console.log(Array.isArray(fruits));
console.log(Array.isArray("Hello World!"));
结果如下:
true
false
伪数组转换真数组
Array.from(array)
:伪数组转换真数组
代码如下:
const lis = document.querySelectorAll('ul li')
console.log(lis);
const liss = Array.from(lis);
console.log(liss)
String
String
是内置的构造函数,用于创建字符串
const arr = new String()
创建字符串建议使用字面量,不用 String 构造函数创建
重点内置方法:
方法 | 作用 | 说明 |
---|---|---|
length | 获取字符串长度 | 获取字符串的长度,返回数字 |
split | 拆分字符串成数组 | 将字符串拆分成数组,返回新数组 |
substring | 分割字符串 | 字符串截取,返回新字符串 |
startsWith | 字符串开头 | 检测是否以某字符开头,根据情况返回 true 或 false |
includes | 包含字符串 | 判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false |
字符串长度
str.length
:获取字符串的长度
语法:
str.length
代码如下:
const str = 'Tom';
console.log(str.length) // 3
字符串拆分成数组
str.split()
:将字符串拆分成数组
语法:
str.split('分隔符')
举例:
-
每个字符都转换为数组元素
代码如下:
const s = "Hello World!"; console.log(s.split(""));
结果如下:
(12) ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!']
-
以","为分隔线转换为数组元素
代码如下:
const s = "my,name,is,john!"; console.log(s.split(","));
结果如下:
(4) ['my', 'name', 'is', 'john!']
分割字符串
str.substring()
:
语法:
str.substring(需要截取的第一个字符的索引[,结束的索引号])
其在起始下标开始,结束于终止下标的前一个,即 [a,b)
如果只是单个参数,即str.substring(a),相当于 str.substring(a,最后),取值范围为 [a,∞)
代码如下:
const str = 'Hello World'
console.log(str.substring(2)); // llo World
console.log(str.substring(0, 2)); // He
console.log(str.substring(5, 9)); // Wor
字符串开头
str.startsWith()
:检测是否以某字符开头,开头则返回 true ,不开头则返回 false
语法:
str.startsWith(要查找的字符串[,查找开始的位置])
代码如下:
console.log(str.startsWith('He')); // true
console.log(str.startsWith('He', 2)); // false
console.log(str.startsWith('llo', 2)); // true
包含字符串
str.includes()
:判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false ,区分大小写
语法:
str.includes(搜索的字符串[,检测位置索引号])
代码如下:
const str = 'My name is Tom,Where is your name';
console.log(str.includes('name')); // true
console.log(str.includes('name', 10)); // true
console.log(str.includes('zoo')); // false
其它方法
方法 | 作用 | 说明 |
---|---|---|
toUpperCase | 大写转换 | 将字母转换成大写,返回新字符串 |
toLowerCase | 小写转换 | 将字母转换成小写,返回新字符串 |
indexOf | 包含字符串 | 检测是否包含某字符,返回索引号,没有找到返回 -1 |
endsWith | 字符串结尾 | 检测是否以某字符结尾,根据情况返回 true 或 false |
replace | 替换字符串 | 用于替换字符串,支持正则匹配,返回新的字符串 |
match | 查找字符串 | 用于查找字符串,支持正则匹配,返回数组或对象 |
concat | 连接字符串 | 将两个字符串串联起来,返回新字符串 |
search | 匹配字符串 | 检索字符串中与指定的子字符串或正则表达式相匹配的子字符串,并总是从开头找起 |
大小写转换
str.toUpperCase()
:大写
str.toLowerCase()
:小写
代码如下:
const str = 'Hello World';
const strUpper = str.toUpperCase();
const strLower = str.toLowerCase();
console.log(strUpper);
console.log(strLower);
结果如下:
HELLO WORLD!
hello world!
包含字符串
str.indexOf()
:检测是否包含某字符,返回索引号,没有找到返回 -1
语法:
str.indexOf(搜索的字符串[,检测位置索引号])
代码如下:
const str = 'Hello World'
console.log(str.indexOf('ll')); // 2
console.log(str.indexOf('l', 8)); // 9
console.log(str.indexOf('z')); // -1
字符串结尾
str.endsWith()
:检测是否以某字符串结尾,根据情况返回 true 或 false
语法:
str.endsWith(要查找的字符串[,查找的长度])
代码如下:
const str = "Hello World";
console.log(str.endsWith('d')); // true
console.log(str.endsWith('rld')); // true
console.log(str.endsWith('z')); // false
console.log(str.endsWith('o', 5)); // true
console.log(str.endsWith('o', 8)); // true
console.log(str.endsWith('o', 9)); // false
字符串替换
str.replace()
:用于替换字符串,支持正则匹配,返回一个新的字符串
语法:
str.replace(被替换的文本,替换的文本)
- 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串
- 该函数接收两个参数,第一个参数可以是字符串或者正则表达式,描述了需要替换的子字符串
- 第二个参数则可以是一个字符串或者一个回调函数,用来描述替换值
- 字符串是不可变对象,该函数不改变原字符串,返回值为替换后的字符串
举例:
把字符串中的’ab’子串替换为’xy’
console.log('abcd'.replace('ab', 'xy')); // xycd
console.log('abcdab'.replace('ab', 'xy')); // xycdab
可以看出,默认情况下仅第一个匹配参数的子串会被替换。如果想替换所有子字符串,则replace的第一个参数应该接收一个正则表达式,并且正则表达式需包含 g 标志
console.log('abcdab'.replace('ab', 'xy')); // xycdab
console.log('abcdab'.replace(/ab/g, 'xy')); // xycdxy
使用通配符
replace的第二个参数是字符串的时候,可以在字符串中使用一些通配符,这些通配符都是以特殊字符$开头:
变量名 | 说明 |
---|---|
$$ | 插入一个"$" |
$& | 插入匹配的子串 |
$` | 插入当前匹配的子串左边的内容 |
$’ | 插入当前匹配的子串右边的内容 |
$n | 假如第一个参数是 RegExp 对象,并且n是个小于100的非负整数,那么在第n个括号匹配的字符串后插入n后面的字符串。提示:索引是从1开始 |
举例:金额格式化
let sText = '123456789.10'
// * 匹配一个 四位数[,.] 或 四位数结尾
const reg = /(\d)(\d{3})([,.]|$)/
// * 如果可以满足这个条件,说明可以加逗号
while (reg.test(sText))
// * 第一次匹配到了 6789.
// 则对应的第一个括号选择的内容 6 后面加上
// , 第二个括号选择的内容 789 第三个括号选择的内容 .
// 所以第一次循环将 6789. 替换成 6,789.
// 则 sText 内容改变为 123456,789.10
// 第二次循环匹配上 3456, 替换成 3,456,
// 第三次没有匹配上,结束循环
sText = sText.replace(reg, "$1,$2$3");
console.log(sText);
结果如下:
123,456,789.10
查找字符串
str.match()
在字符串内查找一个或多个与正则表达式匹配的字符串,返回一个对象
- 若没开启"g"标志,返回一个对象,其中第一个元素是匹配到的整个子串,后续元素是每个括号内的匹配结果
- 对象包含下标0、index、input,其中下标0等价于index,input是String的引用
- 开启"g",返回一个数组,数组的length是匹配的字符串个数,每个元素是每个匹配的起始字符位置
代码如下:
const regexp_1 = /a/g;
const regexp_2 = /a/;
const str = 'bbcadsaadsa';
console.log(str.match(regexp_1));
console.log(str.match(regexp_2));
结果如下:
(4)['a','a','a','a']
['a', index: 3, input: 'bbcadsaadsa', groups: undefined]
连接字符串
str.concat()
方法返回一个新的字符串 / 也可用于连接数组
代码如下:
let str = "TOM";
const str_2 = str.concat('JACK');
console.log(str_2);
结果如下:
TOMJACK
匹配字符串
str.search()
-
检索字符串中与指定的子字符串或正则表达式相匹配的子字符串
-
返回找到的第一个字符的位置,如果未找到返回-1
-
该方法忽略了正则表达式 g 标志和正则对象 lastIndex 属性(即总是从开头找起)
代码如下:
const regexp = /a/g;
const str = 'abcabc';
console.log(str.search(regexp));
console.log(str.search(regexp));
结果如下:
0
0
Number
Number
是内置的构造函数,用于创建数值
常用方法:
toFixed()
设置保留小数位的长度
const price = 12.645;
// 参数为空,则只保留整数
console.log(price.toFixed()); // 13
// 保留两位小数,四舍五入
console.log(price.toFixed(2)); // 12.65
原型
-
构造函数通过原型分配的函数是所有对象所 共享的
-
JavaScript 规定,每一个构造函数都有一个
prototype
属性,指向另一个对象,所以我们也称为原型对象
-
这个对象可以挂载函数,对象实例化不会多次创建原型上的函数,节约内存
-
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法
-
构造函数和原型对象中的
this
都指向 实例化的对象 -
所有的对象里面都有 _proto_ 对象原型 指向原型对象
-
所有的原型对象里面有 constructor 指向创造该原型对象的构造函数
语法:
function Star(uname, age) {
this.name = name;
this.age = age;
}
// 不变的方法
Star.prototype.方法 = function(){};
举例:
function Star(uname, age) {
this.name = name;
this.age = age;
this.dance = () => console.log('跳舞');
}
Star.prototype.sing = () => console.log('唱歌');
const ldh = new Star('刘德华', 60);
const zxy = new Star('张学友', 55);
console.log(ldh.dance === zxy.dance); // false
console.log(ldh.sing === zxy.sing); // true
构造函数和原型对象中的 this
都指向 实例化的对象
let that;
function Star(uname) {
this.uname = uname;
that = this;
}
const ldh = new Star('刘德华');
Star.prototype.sing = function () {
that = this;
}
// 构造函数的 this
console.log(that === ldh); // true
// 原型的 this
ldh.sing();
console.log(that === ldh); // true
constructor 属性
constructor
属性是指向该原型对象的构造函数
代码如下:
function Star() { }
const ldh = new Star();
console.log(Star.prototype.constructor); // ƒ Star() { }
注意:
创建多个原型函数时,注意重新指回创造这个原型对象的 构造函数
function Star() { }
Star.prototype = {
// 注意重新指回创造这个原型对象的 构造函数
constructor: Star,
sing: function () {
console.log('唱歌');
},
dance: function () {
console.log('跳舞');
}
}
对象原型
对象都会有一个属性 __proto__
指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 _proto_ 原型的存在
代码如下:
function Star() { }
const ldh = new Star();
console.log(ldh.__proto__ === Star.prototype); // true
console.log(ldh.__proto__.constructor === Star); // true
原型继承
继承
是面向对象编程的另一个特征,通过继承进一步提升代码封装的成都,JavaScript 中大多数是借助原型对象实现继承的特征
// 男人 函数
function Man() {
this.head = 1;
this.eyes = 2;
this.legs = 2;
this.say = function () { };
this.eat = function () { };
}
// 实例化 男人 函数
const pink = new Man();
// 女人 函数
function Woman() {
this.head = 1;
this.eyes = 2;
this.legs = 2;
this.say = function () { };
this.eat = function () { };
this.baby = function () { };
}
// 实例化 女人 函数
const red = new Woman();
观察上述代码,可以看见男人函数和女人函数有极多的相似处,所以可以封装函数,提取公共部分
- 封装
// 封装函数 人类 函数
const Person = {
head: 1,
eyes: 2,
legs: 2,
say: function () { },
eat: function () { },
}
// 男人函数
function Man() { }
// 女人函数
function Woman() {
this.baby = function () { };
}
- 继承
// 男人函数 继承 人类函数
Man.prototype = Person;
// 指回原来的构造函数
Man.prototype.constructor = Man;
// 女人函数 继承 人类函数
Woman.prototype = Person;
// 指回原来的构造函数
Woman.prototype.constructor = Woman;
- 问题
在上述代码中,男人函数和女人函数都同时使用了同一个对象
根据引用类型的特点,它们指向同一个对象,修改一个就会都影响
console.log(Man.prototype.eyes); // eyes: 2
console.log(Woman.prototype.eyes); // eyes: 2
Man.prototype.eyes.eyes = 6;
console.log(Man.prototype.eyes); // eyes: 6
console.log(Woman.prototype.eyes); // eyes: 6
- 解决
通过 new
创建对象来继承 封装函数
利用的是 实例化同一个函数 但它们之间不会互相影响的原理
// 封装函数
function Person() {
this.head = 1;
this.eyes = 2;
this.legs = 2;
this.say = function () { };
this.eat = function () { };
}
function Man() { }
function Woman() {
this.body = function () { };
}
// 通过 new 继承封装函数
Man.prototype = new Person();
Woman.prototype = new Person();
console.log(Man.prototype.eyes); // eyes: 2
console.log(Woman.prototype.eyes); // eyes: 2
Man.prototype.eyes = 6;
console.log(Man.prototype.eyes); // eyes: 6
console.log(Woman.prototype.eyes); // eyes: 2
原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链
查找规则
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型(也就是 _proto_ 指向的 prototype 原型对象
- 如果还没有就查找原型对象的原型(Object的原型对象)
- 依次类推一直找到 Object 为止(null)
- _proto_ 对象原型的意义就在于为对象成员查找机制提供一个方向,后者说一条路线
- 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
function Person() { }
const red = new Person();
console.log(red.__proto__.__proto__.constructor); // Object
console.log(red instanceof Person); // true
JS高级技巧
深浅拷贝
开发中我们经常需要复制一个对象,如果直接用赋值会有以下问题:
// 对象
const obj = {
name: 'tom',
age: 18
}
const o = obj;
o.age = 20;
console.log(o); // {name: 'tom', age: 20}
console.log(obj); // {name: 'tom', age: 20}
// 数组
const arr = [1, 2, 3];
const a = arr;
a[2] = 8;
console.log(a); // [1, 2, 8]
console.log(arr); // [1, 2, 8]
修改任意一个变量,其它同样指向地址一样的变量也会发生改变
所以我们需要拷贝
浅拷贝
首先浅拷贝和深拷贝只针对引用类型
- 只拷贝最外面一层的数据;更深层次的对象,只拷贝引用
- 拷贝的是地址
{…Object} […Array]
运用展开运算符,将对象或数组进行 拷贝
// 对象
const obj = {
name: 'tom',
age: 18,
}
const o = { ...obj };
o.age = 20;
console.log(o); // {name: 'tom', age: 20}
console.log(obj); // {name: 'tom', age: 20}
// 数组
const arr = [1, 2, 3];
const a = [...arr];
a[2] = 8;
console.log(a); // [1, 2, 8]
console.log(arr); // [1, 2, 8]
Object.assign() 对象拷贝
代码如下:
const obj1 = {
name: 'tom',
age: 18,
}
const obj2 = {
name: 'jack',
sex: '男'
}
Object.assign(obj2, obj1)
obj2.name = 'nan'
console.log(obj1); // name: 'tom', age: 18}
console.log(obj2); // {name: 'nan', sex: '男', age: 18}
解释:将obj1
拷贝给 obj2
。执行完毕后,obj2 的值会被更新。
作用:将 obj1 的值追加到 obj2 中。如果对象里的属性名相同,会被覆盖。
array.concat() 数组拷贝
代码如下:
const arr1 = [1, 2, 3];
const arr2 = arr1.concat();
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3]
arr2[1] = 8;
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 8, 3]
问题
当复杂数据类型内部还有复杂数据类型,则仍是引用,会随其它变量改变而全部变量改变
代码如下:
// 对象
const obj1 = {
name: 'tom',
info: {
age: 18
},
}
const obj2 = Object.assign({}, obj1);
obj2.info.age = 20;
console.log(obj1.info); // {age: 20}
console.log(obj2.info); // {age: 20}
// 数组
const arr1 = [1, [2, 3]];
const arr2 = arr1.concat();
arr2[1][1] = 8;
console.log(arr1[1]); // [2, 8]
console.log(arr2[1]); // [2, 8]
只有表层数据被拷贝,深层数据仍然是引用
深拷贝
首先浅拷贝和深拷贝只针对引用类型
- 拷贝的是对象,不是地址
通过递归实现深拷贝
- 方法
function deepCopy(newObj, oldObj) {
for (let k in oldObj) {
// console.log(k);
if (oldObj[k] instanceof Array) {
// console.log(1);
newObj[k] = [];
deepCopy(newObj[k], oldObj[k]);
}
else if (oldObj[k] instanceof Object) {
// console.log(2);
newObj[k] = {};
deepCopy(newObj[k], oldObj[k]);
}
else {
// console.log(3);
// console.log(oldObj[k]);
newObj[k] = oldObj[k];
}
}
}
实验
const obj1 = {
name: 'tom',
arr: [12, 13, 14],
object: {
age: 18,
},
}
let obj2 = {};
deepCopy(obj2, obj1);
console.log(obj1);
console.log(obj2);
obj2.arr[1] = 20;
console.log(obj1.arr); // [12, 13, 14]
console.log(obj2.arr); // [12, 20, 14]
obj2.object.age = 20;
console.log(obj1.object); // {age: 18}
console.log(obj2.object); // {age: 20}
JS库lodash里面的cloneDeep内部实现深拷贝
下载lodash.js库
代码如下:
<script src="./js/lodash.min.js"></script>
<script>
const obj1 = {
name: 'tom',
arr: [12, 13, 14],
object: {
age: 18,
},
}
const obj2 = _.cloneDeep(obj1);
console.log(obj1);
console.log(obj2);
</script>
通过JSON.stringify()实现深拷贝
将对象转换成字符串,再将字符串转换为对象,以此拷贝
const obj1 = {
name: 'tom',
arr: [12, 13, 14],
object: {
age: 18,
},
}
const obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1);
console.log(obj2);
obj2.object.age = 20;
console.log(obj1.object); // {age: 18}
console.log(obj2.object); // {age: 20}
异常处理
异常处理 是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序
抛出异常
- throw 抛出异常信息,程序也会终止执行
- throw 后面跟的是错误提示信息
- Error 对象配合 throw 使用,能够设置更详细的错误信息
简单
代码如下:
function fun(x, y) {
if (!x || !y) {
throw '没有传递参数';
}
return x + y;
}
console.log(fun());
结果如下:
精确:更详细,写出来报错的位置
代码如下:
function fun(x, y) {
if (!x || !y) {
// throw '没有传递参数';
throw new Error('没有传递参数');
}
return x + y;
}
console.log(fun());
结果如下:
捕获异常
我们可以通过 try / catch 捕获错误信息(浏览器提供的错误信息) try
试试 catch
拦住 finally
最后
- try…catch 用于捕获错误信息
- 将预估可能发生错误的代码写在 try 代码段中
- 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息
- finally 不管是否有错误,都会执行
代码如下:
function fun() {
try {
// 可能发送错误的代码 要写到 try
const p = document.querySelector('.p');
p.style.color = 'red';
}
catch (err) {
// 拦截错误,提示浏览器提供的错误信息,但是不中断程序的执行
console.log(err.message);
throw new Error('错误信息');
// 如果想中断程序 加 return
// return
}
finally {
// 不管你的程序对不对,一定会执行的代码
console.log(111);
}
}
fun();
结果如下:
debugger
其实就是断点
可以在调试时停在那个位置
const arr = [1, 2, 5];
const newArr = arr.map((item, index) => {
debugger;
console.log(item);
console.log(index);
return item + 10;
})
console.log(newArr);
处理this
this指向
普通函数this的指向
- 普通函数没有明确调用者时 this 值为
window
,有调用者时 this 指向调用者
function sayHi() {
console.log(this);
}
const sayHello = function () {
console.log(this);
}
sayHi(); // window
sayHello(); // window
const user = {
uname: '小明',
walk: function () {
console.log(this);
}
}
user.sayHi = sayHi;
user.sayHello = sayHello;
user.sayHello(); // 对象user
user.sayHi(); // 对象user
user.walk(); // 对象user
- 严格模式下没有调用者时 this 的值为 undefined
// 严格模式下
'use strict'
function fn() {
console.log(this); // undefined
}
fn();
箭头函数this的指向
箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在this
- 箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的
- 箭头函数中的 this 引用的就是最近作用域中的 this
- 向外层作用域中,一层一层查找 this ,直到有 this 的定义
const user = {
walk: () => console.log(this),
}
user.walk(); // window
function fun() {
this.name = 'tom';
this.sayHi = () => console.log(this);
}
const fn = new fun();
fn.sayHi(); // fun
改变this
call()
使用 call 方法调用函数,同时指定被调用函数中 this 的值
语法:
fun.call(thisArg,arg1,arg2,...)
- thisArg:在 fun 函数运行时指定的 this 值
- arg1,arg2:传递的其它 参数
- 返回值就是函数的返回值,因为它就是调用函数
const obj = {
name: 'tom',
}
function fun(a, b) {
console.log(this);
return a + b;
}
console.log(fun.call(obj, 1, 2)); // obj / 3
console.log(fun(1, 2)); // window / 3
apply()
使用 apply 方法调用函数,同时指定被调用函数中 this 的值
语法:
fun.apple(thisArg,[argsArray])
- thisArg:在 fun 函数运行时指定的 this 值
- argsArray:传递的值,必须包含在数组里面
- 返回值就是函数的返回值,因为它就是调用函数
- 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
const obj = {
name: 'tom'
}
function fun(a, b) {
console.log(this);
return a + b;
}
console.log(fun.apply(obj, [1, 2])); // obj / 3
console.log(fun(1, 2)); // window / 3
console.log(Math.max.apply(Math, [1, 2, 3])); // 3
console.log(Math.max.apply(null, [1, 2, 3])); // 3
bind()
bind()
方法不会调用函数,但是能改变函数内部 this 指向
语法:
let fun2 = fun1.bind(thisArg);
- 返回由指定的 this 值和初始化参数改造的 原函数拷贝(新函数)
- 因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind,比如改变定时器内部的 this 指向
const obj = {
name: 'tom'
}
function fun1(a, b) {
console.log(this);
return a + b;
}
const fun2 = fun1.bind(obj, 1, 7);
console.log(fun2()); // obj / 8
console.log(fun2(1, 2)); // obj / 8
防抖
防抖(debounce)
:单位时间内,频繁触发事件,只执行最后一次
使用场景:
- 搜索框输入。只需用户最后一次输入完,再发送请求
- 手机号、邮箱验证输入检测
lodash提供
运用 lodash 提供的 _.debounce(func,[wait=0],[option=])
防抖来处理
- **func:**调用的方法
- **wait:**延迟时间
- **option:**option.leading || option.trailing决定延迟前后如何触发,是先调用后等待还是先等待后调用
代码如下:
<div class="box"></div>
<script src="/应用包/JS/lodash.min.js"></script>
<script>
const box = document.querySelector('.box')
let i = 1;
function mouseMove() {
box.innerHTML = i++;
}
// 利用 lodash库 实现 防抖 -> 500毫秒 之后采取 +1
box.addEventListener('mousemove', _.debounce(mouseMove, 500))
防抖函数
核心思路:
- 声明一个定时器变量
- 当鼠标每次滑动都先判断是否有定时器
- 如果没有定时器则开启定时器,记得存到变量里面
- 在定时器里面调用要执行的函数
代码如下:
function debounce(func, wait) {
// console.log(this); --> window
let time;
return function () {
if (time) clearTimeout(time);
time = setTimeout(func, wait);
}
}
节流
节流(throttle)
:单位时间内,频繁触发事件,只执行一次
**使用场景:**鼠标移动 mousemove、页面尺寸缩放 resize、滚动条滚动 scroll等等
lodash提供
利用 lodash 库中的 _.throttle(func,[wait=0])
节流来处理
<script src="/应用包/JS/lodash.min.js"></script>
<script>
const box = document.querySelector('.box');
let i = 1;
function mouseMove() {
box.innerHTML = i++;
}
// 利用 lodash库 实现 防抖 -> 500毫秒 之后采取 +1
box.addEventListener('mousemove', _.throttle(mouseMove, 500));
节流函数
核心思路:
- 声明一个定时器变量
- 当鼠标每次滑动都先判断是否有定时器了,如果有定时器则不开启新定时器
- 如果没有定时器则开启定时器,记得存到变量里面
- 定时器里面调用执行的函数
- 定时器里面要把定时器清空
代码如下:
function throttle(func, wait) {
let time = null;
return function () {
if (!time) {
time = setTimeout(() => {
func();
// 清空定时器
// 不能用 clearTimeout
// clearTimeout(time);
// console.log(time); --> 3
time = null;
}, wait);
}
}
}
常见错误
声明和赋值错误
-
常量未赋值
**翻译:**Uncaught SyntaxError:const声明中缺少初始值设定项
代码如下:
const age; console.log(age);
分析:为常量定义const未赋值,变量let不报错
-
标识符未定义
**翻译:**未捕获的ReferenceError:未定义age
代码如下:
console.log(age);
分析:变量age未被声明就输出
-
标识符已声明
**翻译:**未捕获的SyntaxError:标识符“age”已声明
代码如下:
let age = 18; let age = 20; console.log(age);
分析:重复声明变量age
-
常量再赋值
**翻译:**ncaught TypeError:赋值给常量变量
代码如下:
const age = 18; age = 20; console.log(age);
分析:常量不能修改
其它
注释
单行注释:Ctrl + /
多行注释:Shift + Alt + a
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>prompt</title>
</head>
<body>
<script>
// 单行注释使用 Ctrl + /
/* 多行注释使用 Shift + Alt + a */
</script>
</body>
</html>
NaN
表示不是一个数字,但它是数字类型
代码如下:
console.log("str"*1);
console.log(typeof "str"*1);
结果如下:
NaN
number
JSON
JSON
是一种轻量级(Light-Meight)、基于文本的(Text-Based)、可读的(Human-Readable)格式
将代码转换为JSON格式有两种方法
-
在其中将代码转换
-
运用JavaScript转换将
Object类型
转化为字符串
JSON.stringify()
代码如下
const todos = [ { id: 1, text: "Take out trash", isCompleted: true, }, { id: 2, text: "Meeting with boss", isCompleted: true, }, { id: 3, text: "Dentist appt", isCompleted: false, }, ] const todoJSON = JSON.stringify(todos); console.log(todoJSON);
结果如下:
[{"id":1,"text":"Take out trash","isCompleted":true}, {"id":2,"text":"Meeting with boss","isCompleted":true}, {"id":3,"text":"Dentist appt","isCompleted":false}]
-
运用JavaScript转换将
字符串
转换为Object类型
JSON.parse()
代码如下:
const array = [1, 2, 3]; const arrayJSON = JSON.stringify(array) console.log(JSON.parse(arrayJSON));
结果如下:
(3) [1, 2, 3] 0: 1 1: 2 2: 3 length: 3
数字补0
可以利用三元运算符进行补0操作
代码如下:
let num1 = +prompt(); //输入9
console.log(num1<10?'0'+num1:num1);
let num2 = prompt(); //输入9
console.log(num2<10?0+num2:num2);
结果如下:
09
09
逻辑中断
是指逻辑判断中,判断成功则终止,不在进行后续判断
&&(与)
- 判断为真,则返回最后一个真值
- 判断为假,则返回第一个假值
代码如下:
let num = 10;
if(false && num++);
console.log(num);
if(true && num++);
console.log(num);
console.log(11 && 22);
console.log(false && 22);
结果如下:
10
11
22
false
| |(或)
- 判断为真,则返回第一个真值
- 判断为假,则返回最后一个假值
代码如下:
let num = 10;
if(true || num++);
console.log(num);
if(false || num++);
console.log(num);
console.log(11 || 0);
console.log(0 || false);
结果如下:
10
11
11
false
展开运算符
- 展开运算符(…),将一个数组进行展开
const arr = [1, 2, 5, 3, 5, 6];
console.log(...arr);
// 1 2 5 3 5 6
说明不会修改原数组
典型运用场景:求数组最大值(最小值)、合并数组等
const arr1 = [1, 5, 3, 8, 2]
const arr2 = [5, 96, 5, 42, 5, 4, 4]
// 求数组最大值(最小值)
console.log(Math.max(...arr1));
console.log(Math.min(...arr2));
// 合并数组
const arr3 = [...arr1, ...arr2];
console.log(arr3);
处理小数算法精度问题
问题:
0.1+0.2= ?
所以
(0.1*10 + 0.2*10 )/10 = 0.3
属性
视频
属性名 | 说明 |
---|---|
currentTime | 当前时间 |
(); // fun
是指逻辑判断中,判断成功则终止,不在进行后续判断
**&&(与)**
- 判断为真,则返回最后一个真值
- 判断为假,则返回第一个假值
代码如下:
```javascript
let num = 10;
if(false && num++);
console.log(num);
if(true && num++);
console.log(num);
console.log(11 && 22);
console.log(false && 22);
结果如下:
10
11
22
false
| |(或)
- 判断为真,则返回第一个真值
- 判断为假,则返回最后一个假值
代码如下:
let num = 10;
if(true || num++);
console.log(num);
if(false || num++);
console.log(num);
console.log(11 || 0);
console.log(0 || false);
结果如下:
10
11
11
false
展开运算符
- 展开运算符(…),将一个数组进行展开
const arr = [1, 2, 5, 3, 5, 6];
console.log(...arr);
// 1 2 5 3 5 6
说明不会修改原数组
典型运用场景:求数组最大值(最小值)、合并数组等
const arr1 = [1, 5, 3, 8, 2]
const arr2 = [5, 96, 5, 42, 5, 4, 4]
// 求数组最大值(最小值)
console.log(Math.max(...arr1));
console.log(Math.min(...arr2));
// 合并数组
const arr3 = [...arr1, ...arr2];
console.log(arr3);
处理小数算法精度问题
问题:
0.1+0.2= ?
所以
(0.1*10 + 0.2*10 )/10 = 0.3
属性
视频
属性名 | 说明 |
---|---|
currentTime | 当前时间 |