JS基础
基础语法
JS注释
单行注释//
,快捷键:ctrl + /
块注释/**/
,快捷键:shift + alt + A
JS结束语
- 为了风格统一,结束符要么每句都写,要么每句都不写
JS输入输出语法
- 向body内输出内容:
document.write('输出内容')
- 页面弹出警告对话框:
alert('输出内容')
- 控制台输出语法,程序员调试使用:
console.log('输出内容')
- 显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字:
prompt('提示内容')
- 注意:JS代码是按HTML文档流顺序进行执行,alert() 和 prompt() 会跳过页面渲染先被执行
变量
- 变量是计算机中用来存储数据的"容器"
- 声明变量并赋值:
let 变量名 = 值
(let 不允许多次声明同一个变量) - 命名规则:
- 不能用关键字,只能用下划线、字母、数字、$组成,且数字不能开头,字母区分大小写
- 起名要有意义,遵守小驼峰命名法
常量
- 使用 const 声明的变量称为"常量"
- 声明常量并赋值:
const 变量名 = 值
- 注意:
- 当某个变量永远不会改变的时候,就可以使用 const 来声明,而不是let
- 常量不允许重新赋值,声明的时候必须赋值
数据类型
number(数字型)
- 算术运算符执行的优先级顺序:先乘除取余,后加减,有小括号先算小括号里面的
- NaN 代表一个计算错误,它是一个不正确的或者一个未定义的数学操作所得到的结果
- NaN 是粘性的,任何对 NaN 的操作都会返回 NaN
string(字符串型)
- 通过单引号(‘’) 、双引号(“”)、反引号(``)包裹的数据都叫字符串
- +运算符可以实现字符串的拼接,但是比较麻烦。
- 模板字符串:在反引号中通过 ${变量名} 也可以实现字符串的拼接
boolean(布尔型)
- 表示肯定或否定时在计算机中对应的是布尔类型数据,它有两个固定的值 true 和 false
undefined(未定义型)
- 只声明变量,不赋值的情况下,变量的默认值为 undefined
null(空类型)
- JavaScript 中的 null 仅仅是一个代表“无”、“空”或“值未知”的特殊值
- null和undefined区别:undefined表示没有赋值,null表示赋值了但是内容为空
typeof运算符
- 返回被检测的数据类型:
typeof 变量 / typeof(变量)
- 返回被检测的数据类型:
//通过+运算符实现字符串拼接
document.write('大家好,我叫' + name + ',今年' + age + '岁')
//通过模板字符串实现字符串拼接
document.write(`大家好,我叫${name},今年${age}岁`)
类型转换
- 隐式转换:
- 某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换
- 转换规则:
-
- 号两边只要有一个是字符串,都会把另外一个转成字符串
- 除了+以外的算术运算符 比如 - * / 等都会把数据转成数字类型
-
- 号作为正号解析可以转换成数字型
-
- 显式转换:
- 为了避免因隐式转换带来的问题,通常根据逻辑需要对数据进行显示转换
- 转换语法:
- 将数据转成数字类型:
Number(数据)
- 将数据转换成整数:
parseInt(数据)
- 将数据转换成浮点型:
parseFloat(数据)
- 将数据转换成字符型:
String(数据)
- 将变量转换成字符型:
变量.toString(进制)
- 将数据转成数字类型:
- 注意:如果字符串里有非数字,转换失败时结果为NaN,NaN也是number类型的数据,代表非数字
运算符
- 赋值运算符:
+= -= *= /= %=
,对变量进行赋值的运算符 - 一元运算符:
++(自增) --(自减)
,前置自增(先自加再使用)后置自增(先使用再自加) - 比较运算符:
>
左边是否大于右边
<
左边是否小于右边=
左边是否大于或等于右边<=
左边是否小于或等于右边==
左右两边值是否相等===
左右两边是否类型和值都相等!==
左右两边是否不全等,(比较结果为boolean类型,即只会得到 true 或 false)=
:单等是赋值==
:是判断===
:是全等
注意:
1. 开发中判断是否相等,强烈推荐使用===
2. 字符串比较,是比较的字符对应的ASCII码
3. NaN不等于任何值,包括它本身
4. 不同类型之间比较会发生隐式转换
- 逻辑运算符:
&&
(逻辑与):符号两边都为true结果才为true,一假则假||
(逻辑或):符号两边有一个true就为true,一真则真!
(逻辑非):true变false,false变true,真变假,假变真
- 运算符优先级:
- 小括号>一元运算符>算术运算符>关系运算符>相等运算符>逻辑运算符>赋值运算符>逗号运算符
- 注意:一元运算符里面的逻辑非优先级很高,逻辑与比逻辑或优先级高
语句
- 表达式和语句:表达式是可以被求值的代码,语句是一段可以执行的代码
- 分支语句:让我们有选择性的执行想要的代码
- If分支语句:
if(条件){ 满足条件要执行的代码 }
- 三元运算符 :
条件 ? 满足条件执行的代码 : 不满足条件执行的代码
- switch 语句:
switch(数据) { case 值1: 代码1 break default: 代码n break }
- while循环:
while(循环条件) { 循环体 }
- for循环:
for(变量起始值; 终止条件; 变量变化量) { 循环体 }
- 循环退出:
break(退出循环)、continue(结束本次循环,继续下次循环)
- 注意:明确循环的次数时使用for循环,不明确循环的次数时使用while循环
数组
数组是一种可以按顺序保存数据的数据类型。
- 声明数组:
let 数组名 = [数据1, 数据2, …, 数据n]
- 取值数组:
数组名[下标]
- 遍历数组:
for (let i = 0; i < 数组名.length; i++) { 数组名[i] }
- 操作数组:
数组.push(新增内容)
:将一个或多个元素添加到数组的末尾,并返回该数组的新长度数组.unshift(新增内容)
:将一个或多个元素添加到数组的开头,并返回该数组的新长度数组.pop(删除内容)
:从数组中删除最后一个元素,并返回该元素的值数组.shift(删除内容)
:从数组中删除第一个元素,并返回该元素的值数组.splice(起始位置, 删除的个数)
:删除指定元素
函数
函数是被设计为执行特定任务的代码块。
函数语法:
- 函数声明:
function 函数名() { 函数体 }
- 函数调用:
函数名()
- 函数声明传参:
function 函数名(参数列表) { 函数体 }
- 函数调用传参:
函数名(传递的参数列表)
作用域:
- 全局作用域:函数外部或者整个script有效。
- 局部作用域:也称为函数作用域,函数内部有效。
匿名函数:
function() { 函数体 }
,匿名函数是没有名字的函数, 无法直接使用
立即执行函数:
( function() { 函数体 } )();
- 作用:
- 避免全局变量之间的污染,多个立即执行函数要用 ; 隔开,要不然会报错
- 立即执行且只执行一次,也称为立即调用函数和自执行函数
注意:
- 形参是声明中的参数,实参是调用中的参数
- 参数默认值:如果用户不输入实参,可以给形参默认值,可以默认为 0, 这样程序更严谨
- 函数返回值:当函数需要返回数据出去时,用return关键字,调用该函数会返回一个结果
- 命名规范:与变量命名基本一致,尽量小驼峰式命名法,前缀应该为动词,常用动词约定
- 函数内部的局部变量或者块级变量,在没有声明直接赋值时当作全局变量看
- 在能够访问到的情况下,先找局部变量,局部没有再找全局
对象
对象是一种无序的数据集合。
- 对象声明:
let 对象名 = {属性名:属性值, 方法名:函数}
- 获取对象属性:
对象名.属性
- 修改对象属性:
对象名.属性 = 新值
- 新增对象属性:
对象名.新属性 = 新值
- 删除对象属性:
delete 对象名.属性
- 获取对象属性的另一种写法:
对象['属性']
- 添加对象方法:
方法名:function(){方法体}
- 遍历对象:
for(let k in 对象名){循环体}
注意:
- 修改和新增语法一样,判断标准就是对象有没有这个属性,没有就是新增,有就是改
- 在获取对象时对于多词属性或者带-等属性,点操作就不能用了,可以使用另一种写法
- for in语法中的k是一个变量, 在循环的过程中依次代表对象的属性名
- k 是获得对象的属性名,对象名[k]是获得属性
内置对象-Math:
random
:生成0-1之间的随机数(包含0不包括1)ceil
:向上取整floor
:向下取整max
:找最大数min
:找最小数pow
:幂运算abs
:绝对值Math.floor(Math.random() * (M - N + 1)) + N
:生成N-M之间的随机数
术语
- 关键字(在JavaScript中有特殊意义的词汇):
let、var、function、if、else、switch、case、break
- 保留字(在目前的JavaScript中没意义,但未来可能会具有特殊意义的词汇):
int、short、long、char
- 标识符(变量名、函数名的另一种叫法):
n、m、show
- 表达式(能产生值的代码,一般配合运算符出现):
10 + 3 age >= 1
- 语句(一段可执行的代码):
if() for()
DOM
声明变量
const
:用于不会发生变化的常量let
:用于会发生变化的变量- 注意:建议数组和对象使用const声明
//声明常量
const names = [1, 2, 3]
//声明变量
let num = 18
获取DOM元素
- 语法:
document.querySelector('css选择器')
- 作用:返回CSS选择器匹配到的第一个元素
- 语法:
document.querySelectorAll('css选择器')
- 作用:返回CSS选择器匹配的NodeList(对象集合)
- 注意:得到的是一个有长度有索引号但是没有数组方法需要通过遍历获得每个对象的伪数组
//返回CSS选择器匹配到的第一个元素
document.querySelector('.box')
//返回CSS选择器匹配的NodeList(对象集合)
document.querySelectorAll('h2')
操作元素内容
- 语法:
元素.innerText = 属性
- 作用:将文本内容添加/更新到任意标签位置(只识别文本,不解析标签)
- 语法:
元素.innerHTML = 属性
- 作用:将文本内容添加/更新到任意标签位置(能识别文本,解析标签)
- 注意:如果还在纠结到底用谁,可以选择innerHTML
//只识别文本,不解析标签
username.innerText = '刘德华'
//能识别文本,解析标签
username.innerHTML = '<strong>刘德华</strong>'
操作元素属性
- 操作元素常用属性:
- 语法:
元素.属性 = 值
- 作用:通过JS新增或修改元素属性
- 操作元素样式属性:
- 语法:
元素.style.样式属性 = 值
- 作用:通过style修改元素的样式属性
- 注意:如果属性有-连接符,转换为小驼峰命名法;赋值的时候,需要的时候记得加css单位
- 操作元素类名:
- 语法:
元素.className = '类名'
- 作用:通过className修改元素的类名
- 注意:className是使用新值换旧值, 如果需要添加一个类,需要保留之前的类名
- 语法:
元素.classList.add('类名')
:追加一个类元素.classList.remove('类名')
:删除一个类元素.classList.toggle('类名')
:切换一个类元素.classList.contains('类名')
:查看是否包含某个类
- 作用:通过classList操作元素类名
- 注意:为了解决className容易覆盖以前的类名,我们可以通过classList方式追加和删除类名
//操作元素常用属性
pic.title = '标题'
//操作元素样式属性
box.style.width = '200px'
//操作元素类名
box.className = 'active'
//通过classList操作元素类名
box.classList.add('active')
操作表单元素属性
- 语法:
表单元素.属性名 = 值
- 作用:通过JS新增或修改表单元素属性
<input></input>
//通过JS新增或修改表单元素属性
doucument.querySelector('input').value = '用户名'
doucument.querySelector('input').type = 'password'
自定义属性
- 语法:
<div data-自定义属性 = "值"></div>
- 作用:通过data-自定义属性来设置自定义属性
- 语法:
对象.dataset.自定义属性
- 作用:通过dataset来获取自定义属性的值
- 注意:H5中推出data-自定义属性,标签一律以data-开头,在DOM对象上一律以dataset对象方式获取
//通过data-自定义属性来设置自定义属性
<div class="box" data-id="10">盒子</div>
//通过dataset来获取自定义属性的值
console.log(document.quertSelector('.box').dataset.id)
定时器-间歇函数
- 语法:
setInterval(函数, 间隔时间)
- 作用:开启定时器,每隔一段时间调用这个函数,单位是毫秒
- 语法:
clearInterval(间歇函数名)
- 作用:关闭定时器,一般不会刚创建就停止,而是满足一定条件再停止
- 注意:函数名字不需要加括号,定时器返回的是一个id数字
//定义一个函数
function repeat() {
console.log('我是一个函数')
}
//开启定时器
let timer = setInterval(repeat, 1000)
//关闭定时器
clearInterval(timer)
事件监听
- 语法:
元素对象.addEventListener('事件类型', 要执行的函数)
- 作用:检测是否有事件产生,一旦有事件触发就立即调用一个函数做出响应,也称为注册事件
- 事件监听三要素:
事件源
、事件类型
、事件处理程序
事件类型
- 鼠标事件:
click(鼠标点击)
、mouseenter(鼠标经过)
、mouseleave(鼠标离开)
- 焦点事件:
focus(获得焦点)
、blur(失去焦点)
- 键盘事件:
keydown(键盘按下触发)
、keyup(键盘抬起触发)
- 文本事件:
input(用户输入事件)
- tips:属性选择器:
[属性名 = 属性值]
例:[type = password]
- tips:opacity属性:不透明度(0~1)
事件对象
- 语法:
元素.addEventListener('click', function(e)){ }
- 作用:事件对象里有事件触发时的相关信息,它在事件绑定的回调函数的第一个参数
- 常用属性:
- type:获取当前的事件类型
- clientX/clientY:获取光标相对于浏览器可见窗口左上角的位置
- offsetX/offsetY:获取光标相对于当前DOM元素左上角的位置
- key:用户按下的键盘键的值
- tips:去除字符串两端的空格:
trim()
例:text.value.trim()
//控制台输出事件对象e的key值
console.log( e.key )
环境对象
- 环境对象是this,它代表着当前函数运行时所处的环境
- 函数的调用方式不同,this 指代的对象也不同
- 直接调用函数,其实相当于是
window.函数
,所以 this 指代 window
//使当前环境对象的颜色样式为红色
this.style.color = 'red'
回调函数
- 当一个函数被当做参数来传递给另外一个函数的时候,这个函数就是回调函数
- 回调函数本质还是函数,只不过把它当成参数使用
- 使用匿名函数做为回调函数比较常见
- tips:CSS复选框选择器:
input:checked
选择含有checked属性的对象
//这个function就是一个匿名的回调函数
box.addEventListener('click', function(){
'匿名回调函数'
})
事件流
事件流指的是事件完整执行过程中的流动路径,经过的两个阶段为:捕获阶段、冒泡阶段
- 事件捕获:
- 从DOM的根元素开始去执行对应的事件 (从外到里)
- 语法:
DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
- 作用:addEventListener第三个参数传入 true 代表是捕获阶段触发(很少使用)
- 事件冒泡:
- 当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
- 语法:
DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
- 作用:若第三个参数传入false代表冒泡阶段触发,默认就是false
- 阻止冒泡:
- 语法:
e.stopPropagation()
- 作用:此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
- 语法:
e.preventDefault()
- 作用:此方法可以阻止元素默认行为的发生,比如阻止链接的跳转或表单域跳转
- 语法:
- 解绑事件:
- L0语法:
btn.onclick = null
- 作用:on事件方式,直接使用null覆盖就可以实现事件的解绑
- L2语法:
DOM.removeEventListener(事件类型, 事件处理函数, [获取捕获或者冒泡阶段])
- 作用:使用removeEventListener方法就可以实现事件的解绑
- 注意:匿名函数无法被解绑
- L0语法:
- 鼠标经过事件的区别:
- mouseover 和 mouseout 会有冒泡效果
mouseenter
和mouseleave
没有冒泡效果 (推荐)
- 两种注册事件的区别:
- 传统on注册(L0):
1. 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
2. 直接使用null覆盖就可以实现事件的解绑
3. 都是冒泡阶段执行的 - 事件监听注册(L2):
1. 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
2. 后面注册的事件不会覆盖前面注册的事件(同一个事件)
3. 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
4. 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)解绑
5. 匿名函数无法被解绑
- 传统on注册(L0):
事件委托
- 给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件。
- 语法:
e.target.tagName
- 作用:可以获得真正触发事件的元素的名字,e.target是我们点击的对象
- 注意:通过事件委托减少注册次数,提高了程序性能
//如果点击的对象是ul中的li标签,颜色变为红色
document.querySelector('ul').addEventListener('click', function (e) {
if (e.target.tagName === 'LI') {
e.target.style.color = 'red'
}
})
其他事件
页面加载事件:
- 事件名:
load
- 作用:加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
- 注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件
- 事件名:DOMContentLoaded
- 作用:当初始的HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完全加载
//load事件,加载外部资源完毕时触发
window.addEventListener('load', function(){})
//DOMContentLoaded事件,初始的HTML文档被加载和解析完后触发
document.addEventListener('DOMContentLoaded', function(){})
元素滚动事件:
- 事件名:
scroll
- 作用:滚动条在滚动的时候持续触发的事件
- 注意:监听某个元素的内部滚动直接给某个元素加即可
- 滚动事件方法:
- 获取位置:
- 语法:
元素.scrollTop
- 作用:获取往上滚动被卷去的大小
- 语法:
元素.scrollLeft
- 作用:获取往左滚动被卷去的大小
- 注意:尽量在scroll事件里面获取被卷去的距离,两种属性可以读取也可以修改
- 注意:获取html元素:
document.documentElement
- 语法:
- 滚动位置:
- 语法:
元素.scrollTo(x, y)
- 作用:可把内容滚动到指定的坐标
- 语法:
- 获取位置:
//scroll事件,滚动条在滚动时持续触发
window.addEventListener('scroll', function(){
//获取往上滚动被卷去的大小
cosnt n = document.documentElement.scrollTop
console.log(n)
})
//把内容滚动到指定坐标
window.scrollTo(0, 1000)
页面尺寸事件:
- 事件名:
resize
- 作用:会在窗口尺寸改变的时候触发事件
- 尺寸事件方法:
- 获取宽高:
- 语法:
元素.clientWidth
- 作用:获取元素的可见部分宽(不包含边框,margin,滚动条等)
- 语法:
元素.clientHeight
- 作用:获取元素的可见部分高(不包含边框,margin,滚动条等)
- 语法:
- 获取宽高:
//resize事件,会在窗口尺寸改变的时候触发
window.addEventListener('resize', function(){
//获取元素的可见部分宽高
let w = document.documentElement.clientWidth
let h = document.documentElement.clientHeight
})
元素尺寸和位置
获取宽高:
- 语法:
元素.offsetWidth
- 作用:获取元素的自身宽(包含元素自身设置的宽、padding、border)
- 语法:
元素.offsetHeight
- 作用:获取元素的自身高(包含元素自身设置的高、padding、border)
- 注意:获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0
获取位置:
- 语法:
元素.offsetLeft
- 作用:获取元素距离自己定位父级元素的左距离
- 语法:
元素.offsetTop
- 作用:获取元素距离自己定位父级元素的上距离
- 注意:offsetLeft和offsetTop是只读属性,如果没有定位父级元素则以文档左上角为准
- 语法:
元素.getBoundingClientRect()
- 作用:方法返回元素的大小及其相对于视口的位置
//获取元素的自身宽
document.querySelector('.box').offsetWidth
//获取元素的自身高
document.querySelector('.box').offsetHeight
//获取元素距离自己定位父级元素的左距离
document.querySelector('.box').offsetLeft
//获取元素距离自己定位父级元素的上距离
document.querySelector('.box').offsetTop
//返回元素的大小及其相对于视口的位置
document.querySelector('.box').getBoundingClientRect()
日期对象
实例化:
- 语法:
const date = new Date()
- 作用:创建一个时间对象并获得当前时间
- 注意:在代码中发现了 new 关键字时,一般将这个操作称为实例化
日期对象方法:
日期对象.getFullYear()
获得年份(获取四位年份)日期对象.getMonth()
获得月份(取值为 0 ~ 11)日期对象.getDate()
获取月份中的每一天(不同月份取值也不相同)日期对象.getDay()
获取星期(取值为 0 ~ 6)日期对象.getHours()
获取小时(取值为 0 ~ 23)日期对象.getMinutes()
获取分钟(取值为 0 ~ 59)日期对象.getSeconds()
获取秒(取值为 0 ~ 59)- 注意:因为日期对象返回的数据我们不能直接使用,所以需要转换为实际开发中常用的格式
时间戳:
- 时间戳是指1970年01月01日00时00分00秒起至现在的毫秒数,它是一种特殊的计量时间的方式
- 语法:
日期对象.getTime()``+new Date()``Date.now()
- 作用:获取时间戳
- 注意:Data.now无需实例化,但是只能得到当前的时间戳, 而前面两种可以返回指定时间的时间戳
节点操作
DOM节点:DOM树里每一个内容都称之为节点
节点类型:元素节点、属性节点、文本节点
查找节点:
- 父节点查找:
- 语法:
子元素.parentNode
- 作用:返回最近一级的父节点,找不到返回为null
- 语法:
- 子节点查找:
- 语法:
父元素.childNodes
- 作用:获得所有子节点、包括文本节点(空格、换行)、注释节点等
- 语法:
父元素.children
- 作用:仅获得所有元素节点,返回的还是一个伪数组
- 语法:
- 兄弟关系查找:
- 语法:
元素.nextElementSibling
- 作用:获得下一个兄弟节点
- 语法:
元素.previousElementSibling
- 作用:获得上一个兄弟节点
- 语法:
操作节点:
- 创建节点:
- 语法:
document.createElement( '标签名' )
- 作用:创造出一个新的网页元素节点
- 语法:
- 追加节点:
- 语法:
父元素.appendChild(要插入的元素)
- 作用:插入到父元素的最后一个子元素
- 语法:
父元素.insertBefore(要插入的元素,在哪个元素前面)
- 作用:插入到父元素中某个子元素的前面
- 语法:
- 克隆节点:
- 语法:
元素.cloneNode(布尔值)
- 作用:克隆出一个跟原标签一样的元素,括号内传入布尔值
- 注意:true代表克隆时会包含后代节点一起克隆,false代表克隆时不包含后代节点,默认false
- 语法:
- 删除节点:
- 语法:
父元素.removeChild(要删除的元素)
- 作用:通过父元素删除节点
- 注意:如不存在父子关系则删除不成功
- 注意:删除节点和隐藏节点(display:none)的区别:隐藏节点还是存在,但是删除则从html中删除节点
- 语法:
//创建一个li标签节点
const li = document.createElement('li')
//插入li标签到ul的最后一个子元素后
ul.appendChild(li)
//插入li标签到ul的第一个子元素前
ul.insertBefore(li, ul.children[0])
//将ul的第一个子元素克隆给li1
const li1 = ul.children[0].cloneNode(true)
//将ul的第一个子元素删除
ul.removeChild(ul.children[0])
M端事件
触摸事件:
- 事件名:
touchstart
- 作用:手指触摸到一个DOM元素时触发
- 事件名:
touchmove
- 作用:手指在一个DOM元素上滑动时触发
- 事件名:
touchend
- 作用:手指在一个DOM元素上移动时触发
JS插件
JS插件就是别人写好的一些代码,我们只需要复制对应的代码,就可以直接实现对应的效果。
学习插件的基本过程:
- 熟悉官网,了解这个插件可以完成什么需求 https://www.swiper.com.cn/
- 看在线演示,找到符合自己需求的demo https://www.swiper.com.cn/demo/index.html
- 查看基本使用流程 https://www.swiper.com.cn/usage/index.html
- 查看APi文档,去配置自己的插件 https://www.swiper.com.cn/api/index.html
- 注: 多个swiper同时使用的时候, 类名需要注区分
BOM
介绍BOM
BOM(Browser Object Model)是浏览器对象模型
- window对象是一个全局对象,也可以说是JavaScript中的顶级对象
- 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的
- 所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法
- window对象下的属性和方法调用的时候可以省略window
定时器-延时函数
- 语法:
setTimeout(回调函数,等待的毫秒数)
- 作用:用来让代码延迟执行的函数
- 注意:setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window
- 语法:
clearTimeout(延时函数)
- 作用:清除延时函数
- 注意:延时器需要等待,所以后面的代码先执行。每一次调用延时器都会产生一个新的延时器
- 两种定时器对比:
- 延时函数:执行一次
- 间歇函数:每隔一段时间就执行一次,除非手动清除
//开启定时器,2秒后控制台输出内容
let timer = setTimeout(function() {
console.log('时间到了')
}, 2000)
//清除定时器
clearTimeout(timer)
JS执行机制
概念:
- JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。
- 这是因为 Javascript 这门脚本语言诞生的使命所致—JavaScript 是为处理页面中用户的交互,以及操作DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。
- 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
- 为了解决这个问题,利用多核CPU的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。
同步和异步:
- 同步:前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的
- 异步:在做这件事的同时,还可以去处理其他事情
- 他们的本质区别: 这条流水线上各个流程的执行顺序不同
- 同步任务:同步任务都在主线程上执行,形成一个执行栈
- 异步任务:JS 的异步是通过回调函数实现的
- 一般而言,异步任务有以下三种类型:
- 普通事件,如 click、resize 等
- 资源加载,如 load、error 等
- 定时器,包括 setInterval、setTimeout 等
- 异步任务相关添加到任务队列中(任务队列也称为消息队列)
执行机制:
- 先执行执行栈中的同步任务。
- 异步任务放入任务队列中。
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(event loop)
location对象
location 的数据类型是对象,它拆分并保存了 URL 地址的各个组成部分
常用属性和方法:
- 语法:
location.href
- 作用:获取完整的 URL 地址,对其赋值时用于地址的跳转
- 语法:
location.search
- 作用:获取地址中携带的参数,即URL中符号 ?后面部分
- 语法:
location.hash
- 作用:获取地址中的啥希值,即URL中符号 # 后面部分
- 语法:
location.reload(true)
- 作用:用来刷新当前页面,传入参数 true 时表示强制刷新
navigation对象
navigator的数据类型是对象,该对象下记录了浏览器自身的相关信息
常用属性和方法:
- 通过
navigator.userAgent
检测浏览器的版本及平台
// 检测 userAgent(浏览器信息)
!(function () {
const userAgent = navigator.userAgent
// 验证是否为Android或iPhone
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone,则跳转至移动站点
if (android || iphone) {
location.href = '[http://m.itcast.cn](http://m.itcast.cn/)' }
})()
history对象
history 的数据类型是对象,主要管理历史记录,该对象与浏览器地址栏的操作相对应,前进、后退、历史记录等
常用属性和方法:
- 语法:
history.back()
- 作用:后退功能
- 语法:
history.forward()
- 作用:前进功能
- 语法:
history.go(参数)
- 作用:前进后退功能,参数如果是1前进一个画面,如果是-1后退一个画面
- 注意:history 对象一般在实际开发中比较少用,但是会在一些 OA 办公系统中见到
本地存储介绍
随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经常性在本地存储大量的数据,HTML5规范提出了相关解决方案。
- 数据存储在用户浏览器中
- 设置、读取方便、甚至页面刷新不丢失数据
- 容量较大,sessionStorage和localStorage约 5M 左右
本地存储分类
localStorage:
- 可以将数据永久存储在本地(用户的电脑), 除非手动删除,否则关闭页面也会存在。
- 语法:
localStorage.setItem(key, value)
- 作用:存储数据
- 语法:
localStorage.getItem(key)
- 作用:获取数据
- 语法:
localStorage.removeItem(key)
- 作用:删除数据
- 特性:
- 可以多窗口(页面)共享(同一浏览器可以共享)
- 以键值对的形式存储使用
- 注意:本地存储只能存储字符串类型数据
sessionStorage:
- 数据存储生命周期为关闭浏览器窗口,关闭页面就不存在了
- 语法:
sessionStorage.setItem(key, value)
- 作用:存储数据
- 语法:
sessionStorage.getItem(key)
- 作用:获取数据
- 语法:
sessionStorage.removeItem(key)
- 作用:删除数据
- 特性:
- 生命周期为关闭浏览器窗口
- 在同一个窗口(页面)下数据可以共享
- 以键值对的形式存储使用
- 用法跟localStorage基本相同
//存储数据('uname', '张三')
localStorage.setItem('uname', '张三')
//获取数据, 都加引号
const name = localStorage.getItem('uname')
//删除数据, 只删除名字
localStorage.removeItem('uname')
//修改数据, 如果原来有这个键是改, 如果没有这个键则是增
localStorage.setItem('uname', '李四')
存储复杂数据类型
本地只能存储字符串,无法存储复杂数据类型
- 语法:
JSON.stringify(复杂数据类型)
- 作用:将复杂数据转换成JSON字符串,再存储到本地存储中
本地存储里面取出来的是字符串,不是对象,无法直接使用
- 语法:
JSON.parse(JSON字符串)
- 作用:把JSON字符串转换为对象
- 注意:JSON对象的属性和值都有引号,而且引号统一为双引号
//定义一个复杂数据类型
const goods = {name: '张三', age: 18}
//把复杂数据类型转换成JSON字符串, 存入本地存储
localStorge.setItem('goods', JSON.stringify(goods))
//取出本地存储, 把JSON字符串转换成对象
const obj = JSON.parse(localStorge.getItem('goods'))
数组方法
map方法:
- 语法:
数组.map(function (ele, index) { return ele })
- 作用:map可以遍历数组处理数据,并且返回新的数组
join方法:
- 语法:
数组.join('参数')
- 作用:把数组中的所有元素转换成一个字符串
- 注意:数组元素里通过参数里面指定的分隔符进行分隔,空字符串(‘’)则所有元素之间没有字符
//定义一个数组
const arr = [10, 20, 30]
//利用map方法遍历数组, 对数组中每个元素加10并且拼接一个字符'元'
const newArr = arr.map(function (item) {
return item + 10 + '元'
})
//把数组中所有元素转换成一个字符串, 并且元素之间以*分隔
console.log(arr.join('*'))
正则
介绍正则
正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式,在 JavaScript中,正则表达式也是对象
正则表达式通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式
作用:
- 表单验证(匹配)
- 过滤敏感词(替换)
- 字符串中提取我们想要的部分(提取)
正则语法
-
语法:
const 变量名 = /表达式/
-
作用:定义正则表达式
-
注意:其中/ /是正则表达式字面量
-
语法:
regObj.test(被检测字符串)
-
作用:判断是否有符合规则的字符串,返回的是布尔值
-
注意:其中regObj是定义的正则表达式
-
语法:
regObj.exec(被检测字符串)
-
作用:检索符合规则的字符串,找到返回数组,否则返回null
//定义一个字符串
const str = 'javascript'
// 定义正则表达式规则
const reg = /java/
// 判断是否有符合规则的字符串
console.log(reg.test(str)) // 返回布尔值
// 检索符合规则的字符串
console.log(reg.exec(str)) // 返回数组
元字符
元字符是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能
元字符分类:
- 边界符:正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符
^
:表示匹配行首的文本(以谁开始)$
:表示匹配行尾的文本(以谁结束)- 注意:如果^和$在一起,表示必须是精确匹配
- 量词:设定某个模式出现的次数
*
:重复零次或更多次+
: 重复一次或更多次?
:重复零次或一次{n}
:重复n次{n,}
:重复n次或更多次{n,m}
:重复n到m次- 注意:逗号左右两侧千万不要出现空格
- 字符类:
[]
:匹配字符集合(使用连字符 - 表示一个范围)[^]
:取反符号(对范围取反,注意要写到中括号里面).
:匹配除换行符之外的任何单个字符
- 预定义类:
\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]
//边界符:精确匹配'哈哈哈'
/^哈哈哈$/
//量词:2~5个哈
/^哈{2,5}$/
//字符类-匹配字符集合:第一个字符为1~9, 后面的4个以上字符为0~9
/^[1-9][0-9]{4,}$/
//字符类-取反符号:一个除了a~z的字符
/^[^a-z]$/
//字符类-除换行符:2个以上除换行符之外的任意字符
/^.{2,}$/
//预定义类:四个0~9的数字 - 1~2个0~9的数字 - 1~2个0~9的数字
/^\d{4}-\d{1,2}-\d{1,2}/
修饰符
修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等
- 语法:
/表达式/修饰符
- 修饰符类型:
i
是单词 ignore 的缩写,正则匹配时字母不区分大小写g
是单词 global 的缩写,匹配所有满足正则表达式的结果
替换方法:
- 语法:
字符串.replace(/正则表达式/, '替换文本')
- 作用:将字符串里符合正则表达式规则的文本替换成替换文本
//修饰符i, 不区分大小写
//修饰符g, 匹配所有结果
//替换方法replace, 把符合正则表达式的文本替换成替换文本
str.replace(/java/ig, 'Python') //把str中所有java或JAVA文本替换成Python
JS进阶
作用域
局部作用域Local
作用域(scope)规定了变量能够被访问的"范围",离开了这个"范围"变量便不能被访问
局部作用域分为:
- 函数作用域:在函数内部声明的变量只能在函数内部被访问,外部无法直接访问
- 函数内部声明的变量,在函数外部无法被访问
- 函数的参数也是函数内部的局部变量
- 不同函数内部声明的变量无法互相访问
- 函数执行完毕后,函数内部的变量实际被清空了
- 块作用域:在JavaScript中使用{ }包裹的代码称为代码块,代码块内部声明的变量外部将有可能无法被访问
- let 声明的变量会产生块作用域,var 不会产生块作用域
- const 声明的常量也会产生块作用域
- 不同代码块之间的变量无法互相访问
- 推荐使用 let 或 const
全局作用域Global
const o = .cloneDeep(obj) //通过.cloneDeep()方法实现深拷贝
通过JSON.stringify()实现:
```javascript
const o = JSON.parse(JSON.stringify(obj)) //把对象转换成JSON数据再转换成对象
异常处理
throw抛异常
异常处理是指预估代码执行过程中可能发生的错误
if (!x || !y) { throw new Error('没有参数传递过来') }
- throw 抛出异常信息,程序也会终止执行
- throw 后面跟的是错误提示信息
- Error 对象配合 throw 使用,能够设置更详细的错误信息
try/catch捕获异常
通过try/catch
捕获错误信息(浏览器提供的错误信息),try试试、catch拦住、finally最后
try {
const p = document.querySelector('.p')
p.style.color = 'red'
} catch (err) {
// 拦截错误,提示浏览器提供的错误信息,但是不中断程序的执行
console.log(err.message)
throw new Error('你看看,选择器错误了吧')
//终止代码继续执行
return
}finally {
//不管程序对不对,一定会执行的代码
alert('一定会执行的代码')
}
- try…catch 用于捕获错误信息
- 将预估可能发生错误的代码写在 try 代码段中
- 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息
- finally 不管是否有错误,都会执行
debugger
在程序中添加debugger代码,在运行的时候会直接跳转到debugger位置进行断点调试
使用场景:程序员进行调试时使用
处理this
this指向
普通函数:
普通函数的调用方式决定了this的值,即谁调用this的值指向谁
普通函数没有明确调用者时this值为window,严格模式下没有调用者时this的值为 undefined
箭头函数:
箭头函数中的this与普通函数不同,也不受调用方式的影响,事实上箭头函数中并不存在this
- 箭头函数会默认帮我们绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的
- 箭头函数中的this引用的就是最近作用域中的this
- 向外层作用域中,一层一层查找this,直到有this的定义
注意:
- 对象里面没有this,函数作用域中才有this
- DOM事件回调函数如果需要DOM对象的this,因为它指向window,不推荐使用箭头函数
- 基于原型的面向对象也不推荐采用箭头函数
改变this
JavaScript中还允许指定函数中this的指向,有3个方法可以动态指定普通函数中this的指向
语法:fun.call(thisArg, arg1, arg2, ...)
作用:使用 call 方法调用函数,同时指定被调用函数中 this 的值
说明:
- thisArg:在 fun 函数运行时指定的 this 值
- arg1, arg2:传递的函数实参
- 返回值就是函数的返回值,因为它就是个调用函数,只是可以用thisArg改变this
语法:fun.apply(thisArg, [argsArray])
作用:使用 apply 方法调用函数,同时指定被调用函数中 this 的值
说明:
- thisArg:在fun函数运行时指定的 this 值
- argsArray:传递的值,必须包含在数组里面
- 返回值就是函数的返回值,因为它就是个调用函数,只是可以用thisArg改变this
- 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
语法:fun.bind(thisArg, arg1, arg2, ...)
作用:bind 方法不会调用函数,但是能改变函数内部this 指向
说明:
- thisArg:在 fun 函数运行时指定的 this 值
- arg1, arg2:传递的其他参数
- 返回由指定的 this 值和初始化参数改造的原函数拷贝(新函数)
- 因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用bind
相同点:
- 都可以改变函数内部的this指向
区别点:
- call 和 apply 会调用函数,并且改变函数内部this指向
- call 和 apply 传递的参数不一样,call 传递的参数是arg1, arg2…形式 apply 必须是数组形式[arg]
- bind 不会调用函数,可以改变函数内部this指向
主要应用场景:
- call 调用函数并且可以传递参数
- apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值
- bind 不调用函数,但是还想改变this指向,比如改变定时器内部的this指向
性能优化
防抖debounce
防抖就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
利用Lodash库的_.debounce()
方法实现防抖:
<script src="./lodash.min.js"></script>
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
box.addEventListener('mousemove', _.debounce(mouseMove, 500))
box.addEventListener('mousemove', _.throttle(mouseMove, 500))
利用定时器实现防抖:
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = ++i
}
function debounce(fn, t) {
let timeId
return function () {
// 如果有定时器就清除
if (timeId) clearTimeout(timeId)
// 开启定时器
timeId = setTimeout(function(){fn()}, t)
}
}
box.addEventListener('mousemove', debounce(mouseMove, 200))
节流
节流就是指连续触发事件,但是在 n 秒中只执行一次函数
利用lodash的_.throttle()
方法实现节流:
<script src="./lodash.min.js"></script>
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
box.addEventListener('mousemove', _.throttle(mouseMove, 500))
利用定时器实现节流:
const box = document.querySelector('.box')
let i = 1
function mouseMove() {
box.innerHTML = i++
}
function throttle(fn, t) {
let timer = null
return function () {
if(!timer){
timer = setTimeout(function() {
fn()
// 清空定时器
timer = null
}, t)
}
}
box.addEventListener('mousemove', throttle(mouseMove, 500))