介绍
- html:框架
- CSS:装饰
- JavaScript:行为、交互
JavaScript是一种弱数据类型的编程语言,实现人机交互的效果
功能:1.网页特效 2.表单验证 3.数据交互 4.服务器编程
组成:1.ECMAscript:变量、数据类型、分支、循环、对象 2.Webapi(基础语法):DOM操作文档(页面的元素)、BOM操作文档(浏览器)
JS的执行机制
JS是单线程的:同一时刻,只能执行一个任务
JS将任务分为同步任务和异步任务
- 同步任务:自上到下,上一个任务执行结束后执行下一个任务
- 异步任务:不被浏览器引擎放在主线程,而是进入任务队列,等待主线程同步任务执行完毕后再执行
异步任务又分为:宏任务、微任务
常见的异步任务:setTimeout、setInterval、事件(load,error等)
<!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>
console.log("1111");
setTimeout(function(){
console.log("2222");
})
console.log("3333");
</script>
</body>
</html>
如下图,即使将timeout设置为0,也会让setTimeout函数进入任务队列
基础
//单行注释
/*多行注释*/
语句结束符:
在JS中以 ; 作为语句结束符。也默认将 换行符 识别为一个结束符,所以 ; 可写可不写,但最好保持代码书写风格统一,保持同一的结束符
输入输出:
输入:
promot("提示语")
输出:
1.docoment.write("输出内容") 向body中输出内容,能够识别标签,内容会被解析成网页元素
2.console.log("输出内容") 向控制台输出内容。一般在调试时使用
3.alret("输出内容") 向弹出框输出内容
变量
变量:
1.概念:用来存储数据的容器
2.声明:
let 变量名
var 变量名
声明多个变量: let 变量1,变量2·····
3.赋值:变量名 = 变量值
4.命名规范:
-字母数值下划线 + $
-不能以数字开头
-不能使用关键字保留字
-严格区分大小写
-最好具有意义
-最好遵循小驼峰命名法(第二个单词开始大写)
5.变量的初始化:let 变量名 = '值'
6.var与let区别:
1.var允许重复声明变量,let不允许重复声明变量
2.let声明的变量只有在声明后才能使用,会报错;var声明的变量在声明前可以使用,但若没有赋值,会返回none,不会报 错,即存在变量提升问题
3.var不存在块级作用域的概念
常量
常量
1.声明
const 常量名 = value
2.命名规范
3.注意:
在声明常量时,必须指定它的值。
常量的值在声明后不可被重新赋值。
JS的引入
JavaScript的引入:
1.内联:
<body>
<button onclick=""></button>
</body>
2.内部:<head></head> <body></body> 都可以写,但前期建议写在 <body></body> 的末尾
<body>
<script></script>
</body>
3.外链:
<body>
<script src="链接"></script>
</body>
注意:通过外链引入JS后,不要再在<script></script>便签内写内容
JS的数据类型
数据类型:
JavaScript是一种弱数据类型的语言,它的变量类型只有在变量赋值之后才能确定。
也就是说,变量的值是什么数据类型,变量就是什么数据类型
一、基本数据类型:
1.number:整型、浮点型、负数
2.string:单引号、双引号、反引号包起来的内容
反引号内的值可以换行并保留换行格式
字符串拼接:
1.+
2.document.write(`年龄是${变量名},姓名是${变量名}`)
3.bool:true、false
4.underfined:
未定义类型:变量已声明,但没赋值
5.null:值为空。主要用于创建未确定值的对象
数据类型转换:
promote获得的值,默认是str类型
1.隐性转换(自动转换):
字符串+任意变量 变量会自动转换为string
数字(- * /)任意变量 变量自动转换为number
+字符串 字符串会转换为number
2.显示转换
number() 若选中的变量中出现了字符串,不会报错,会返回NaN NaN也是数字类型
pareInt() 尽可能将值转换为整型。例:123abc——>123
pareFlaot() 尽可能将值转换为浮点型。
String()
变量.toString(值) 不给值就单纯将变量转换为String。也可以给进制,将变量转换为进制再以string输出
二、引用数据类型
1.object
2.function
3.array
运算符
运算符
一、算术运算符
+ - * / %
优先级:() */ +-
二、赋值运算符
= += -= /= *= %=
三、一元运算符
i++ i-- 先赋值,在自增自减
++i --i 先自增自减,在赋值
!
四、比较运算符
> < >= <= != !== == ===
==:只会判断值是否相同
===:会判断值和数据类型是否都相同
string根据ASCII码大小进行比较
NaN == NaN 结果为 False
五、逻辑运算符
&& 与
|| 或
! 非
undefined/null && 10 == undefined/null
null undefined/null || 10 == 10
六、位移运算符
| 或
& 与
^ 异或
>> 右移
<< 左移
七、三元运算符
条件 ? 为真时执行的代码 : 为假时执行的代码
八、展开运算符
... : 将数组或对象展开
let arr = [1,2,3]
console.log(...arr) //会将arr内的元素以单个变量的形式打印
表达式与语句
表达式:一组代码的集合
语句:JS命令
三大流程控制语句:
- 顺序:自上到下,从左往右
- 选择:不同条件,执行不同代码
- 循环:某段代码在条件为真时循环执行
选择
if(条件){语句块}
else if(条件){语句块}
else{语句块}
条件只要最终返回的是布尔值就可以
switch(变量){
case 值1:
语句块
break
case 值2::
语句块
break
·······
default:
语句块
若不写break,会执行多行case直到执行到break或default才停止
}
循环
break:终止距离break最近的一层循环
continue:跳出本次循环
while(循环条件){
循环体
}
do{
循环体
}
while(循环条件)
do while 与 while 不同,会先执行一次do中的循环体语句再判断是否继续执行循环体语句
for(临时变量;循环条件;临时变量的变化值){
循环体
}
数组
- 定义:可以存储多条不同数据类型的数据
- 声明:
1. let a = new Array()
2. 自变量 let a = [ ]- 数组长度:数组名.length
- 查看元素:数组名[下标] 没有负数下标
- 遍历数组:
1. for i of shuzu
2. for(let i=0;i<a.length;i++)- 修改数组:数组名[下标]=值
- 增加元素:
1. .push(“元素1”,“2”,······) 追加至数组末尾
2. .unshift(“元素1”,“元素2”,·····) 追加至开头
3. .splice(start,0,“元素1”,“元素2”,·····) 追加至指定下标开始的位置- 删除元素:
1. .pop() 删除尾部元素,同时返回被删除的元素
2. .shift() 删除首部元素
3. splice(start,删除的个数) 删除从指定下标开始指定个数的元素
函数
- 概念:实现某一改变的代码块。实现代码的高度复用
- 定义:
function 函数名(形参){
函数体
}- 调用:函数名(实参)
- 函数的返回:只能返回一个值
引用传递与值传递
- 值传递:传递变量的值
- 引用传递:传递变量的地址
函数作为值传递
先定义一个形参为函数的函数
function ab(fun){
fun()
}
1.无参函数
function a(){
alert("a()")
ab(a) //只需要填函数名
}
2.有参函数
function b(num){
alert("b()"+num)
}
1.ab(b,12) //将函数引用和传参分开
2.ab(function(){
let num = 值;
b(num);
})
箭头函数
箭头函数表达式的语法比传统函数表达式更简洁,但在语义上有差异,在用法上也有限制:
- 箭头函数没有独立的 this、arguments 和 super 绑定,并且不可被用作方法。
1. this:意义视情况而定
2. argument:表示形参,是所有(非箭头)函数中都可用的局部变量,是一个对应于传递给函数的参数的类数组对象。类数组表示其有长度属性且索引是从0开始的,但是其没有Array的内置方法。
3. super:有两种使用方式:作为“函数调用”(super(…args))调用父类的构造函数,或作为“属性查询”(super.prop 和 super[expr])。- 箭头函数不能用作构造函数。使用 new 调用它们会引发 TypeError。它们也无法访问 new.target 关键字。
- new 运算符允许开发人员创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。- 箭头函数不能在其主体中使用 yield,也不能作为生成器函数创建。
- yield:用于暂停和恢复生成器函数。
语法
() => expression
//只有一个形参可以不用()
param => expression
(param) => expression
(param1,param2,····,paramN) => expression
只有当函数直接返回表达式时,才可以省略大括号。
param => {
statements
return XXX
}
(param1,param2,····,paramN) => {
statements
return XXX
}
如果函数体有额外的处理,则大括号是必需的,return 关键字也是必需的。
形参部分支持剩余参数、默认参数和解构赋值,并且始终需要使用括号
箭头函数总是未命名的。也可以将箭头函数赋值给一个变量,这样它就有了名字。
默认值参数
拥有初始值的形参,一般写在正常形参的后面
初始值是可以被修改的,并不是固定的
function area(r,pi=3.14){
}
可变参数
将传入的值全部以类数组的形式储存起来
function getSum(num){
let sum = 0
for(let i in arguments){
sum += arguments[i]
}
console.log(sum);
}
getSum(1,2,3,4,5,6,7,8,9)
剩余参数
将多余的传参以真数组的形式存起来
function test(a,b,...arg){
console.log(a,b,arg)
}
test(1,2,3,4,5,6,7)
1 2 Array(5)
作用域
- 概念:变量名在某一个范围内生效,这个范围就是它的作用范围
- 分类:全局作用域、局部作用域、块级作用域
- 全局作用域:在项目文件第一层声明的变量
- 局部作用域:在函数内部声明的变量
- 块级作用域:在{}之间声明的变量,for/if
- 有相对作用域的分类的变量
对象
- 原来储存数据,由属性和方法构成,函数在对象内就被称为方法
- 定义:
let li = {
name:'zhangsan',
age:24,
sing:function(){
}
}
- 查看属性
对象名.属性
console.log(li.name);
对象名[属性]
console.log(li['age']);
- 查看方法
li.sing()
- 遍历
for(let i in li){
}
闭包
让外部内访问函数内部的变量
闭包拥有自己的作用域
缺点:会让该变量不再被释放,内容造成内存溢出
内层函数使用了外层函数的变量
function outer(){
let i = 0
function inner(){
console.log(i);
}
return inner
}
使用情景
希望变量只能在函数内部被修改,但又希望该变量能被外部访问
function outer(){
let count = 1
function inner(){
count++
console.log(`内部自增第${count}次`);
}
return inner
}
let a = outer()
a()
a()
a()
a()
构造函数
对比对象与构造函数
对象每次只能创建单个,每个对象都需要创建对应的属性和方法,对于需要创建多个拥有相同属性和方法的情况非常麻烦
let preson1 = {
"name" : "zhangsan",
"age" : "24",
"hobby" : function(){
console.log("hobyy")
}
}
构造函数的名称首字母需要大写
function Preson(name,age){
this.name = name
this.age = age
this.hobby = function(){
console.log("hobby")
}
}
对于需要创建多个拥有相同属性和方法对象的情况,可以通过new关键字构造函数(参数)实例化对象来创建
但需要注意,虽然构造函数中hobby方法是固定的,但用实例化创建的对象中的hobby方法,其实是两个不同对象的方法,开辟了两个hobby函数的空间
let p1 = new Preson("zhangsan","24")
let p2 = new Preson("limou","20")
可以通过prototype方法将不变的方法绑定在构造函数上,使所有通过该构造函数实例化的对象都可以共享该方法
Preson.prototype.hobby = function(){
console.log("hobby")
}
原型链
- 构造函数的prototype方法会指向这个构造函数的原型
- 对象实例和原型的construct方法会指向自身的构造函数
- 对象实例的__proto__方法会指向自身的原型
- 原型是这个构造函数的实例对象都可以使用的方法。原型的本质也是一种原型对象,即原型1也可能指向原型2,原型2又会找到自身的构造函数,这样一层层往外层寻找,直到该原型指向null,表示其是最后一个环节
JS的垃圾回收机制
概念
内存中的生命周期
- 内存分配:系统自动进行分配
- 内存使用:读或写
- 内存回收:由垃圾回收机制完成
内存泄露
未被释放/释放失败/不能被释放的内存
栈中的内存会自动被释放
堆中的内存只能通过垃圾回收机制释放
JS的垃圾回收机制
引用计数法
如果一个对象没有指向它的引用,那么就认为其不再被需要,就会被释放
作用机制
1.记录被引用的次数
2.被引用时,计数器++;不再被引用时,计数器–
3.当引用次数为0时释放缺点
1.内存消耗(本身就占用内存)
2.循环引用问题(如果有两变量相互引用,那么两变量都无法被释放)
标记清除法
作用机制
对从根本查找不到的对象进行标记,之后进行释放
拷贝
浅拷贝
简单数据类型会拷贝值,复合数据类型还是会复制地址
let a1 = [1,2,3,4]
let a2 = [...a1]
let a3 = [1,2,3,4,[5,6,7,8]]
let a4 = [...a3]
console.log(a2)
console.log(a4)
深拷贝
原生JS中没有关于深拷贝的方法,自己写吧
一些内置对象
Math
- 欧拉常数:Math.E
- 圆周率:Math.PI
- 向下取整:Math.floor()
- 向上取整:Math.ceil()
- 取绝对值:Math.abs()
- 取最大值:Math.max()
- 取最小值:Math.min()
- 取[0,1)之间的随机自然数:Math.random()
取[n,m]之间的自然数
function random(n,m){
let num = (Math.floor(Math.random()*((m - n) + 1) + n));
return num
}
- 四舍五入:Math.round()
- 开平方根:Math.sqrt()
- 幂次方:Math.pow(数,次方)
Date
- 必须先实例化对象
let date = new Date()
- 年:date.getFullYear()
- 月(范围[0,11]所以需+1):getMonth() + 1
- 月日:date.getDate()
- 时:date.GetHours()
- 分:date.getMintes()
- 秒:date.getSeconds()
- 星期:date.getDay()
- 毫秒:date.getMillseconds()
- 时间戳:date.getTime()
this 关键字
this表示方法的对象
this在事件中表示事件源,在对象中表示对象本身
箭头函数没有this的作用域
this的指向可以通过函数绑定
普通函数中的this
谁调用,this指向谁
function test(){
console.log(this) // this为window,因为windoe.function
}
箭头函数中的this
- 箭头函数实际上是不存在this的作用域的
- 箭头函数的this会一层层往外找,绑定距离最近的,有this的作用域的this
- 凡是需要使用 this 关键字的场景,都不要轻易使用箭头函数
- 构造函数不要轻易使用箭头函数,因为其的 this 要指向实例化的对象
改变this的方向
call函数
call(this,arg1,arg2) 在改变this方向的同时调用了函数
const obj1 = {
"name":"lisi"
}
function test(n1,n2){
console.log(this)
console.log(n1+n2)
}
test.call(obj1,1,2)
apply函数
apply(this,[arg1,arg2]) 在调用函数的同时改变了this的方向,传参需要使用数组
const obj1 = {
"name":"lisi"
}
function test(n1,n2){
console.log(this)
console.log(n1+n2)
}
test.call(obj1,[1,2])
bind函数
bind(this) 不会调用函数,而是返回新的函数
function test(){
console.log(this)
}
const obj1 = {
"name":"lisi"
}
new_obj = test.bind(obj1)
new_obj()
异常
function t(n1,n2){
console.log(n1+n2);
if (!n1 || !n2) {
throw new Error("输入不能为空")
}
}
// 捕获异常
try{
const box = document.querySelector("div")
}
catch(erroe){
console.log(error.message);
console.log("error");
}
// finally的优先级比return还高
finally{
console.log("不论是否报错,都会执行");
}