Java Script 进阶
变量声明有3个 ver、let 和 const。建议const优先,其次为let。
const arr = ['red', 'pink']
arr.push('blue')
arr = [1, 2, 4]
arr.push(5) // 错误,arr为const
1、Web Api 基本认知
1.1、作用和分类
作用:就是使用js去操作html和浏览器。
分类:DOM(文档对象模型)、BOM(浏览器对象模型)。
DOM的作用:开发网页内容特效和实现用户交互。
1.2、DOM树
DOM树是什么:
- 将HTML文档以树状结构直观地表现出来,我们称为文档树或DOM树;
- 描述网页内容关系的名词;
- 文档树直观地体现了标签与标签之间的关系。
1.3、DOM对象
const div = document.querySelector('div') // dom对象
// 打印对象
console.dir(div)
DOM对象:浏览器根据HTML标签生成的JS对象。
- 所有的标签属性都可以在这个对象上面找到;
- 修改这个对象的属性会自动映射到标签身上。
DOM核心思想:把网页内容当作对象来处理。
document对象:
- 是DOM里提供的一个对象;
- 所以它提供的属性和方法都是用来访问和操作网页内容的;
- 网页所有内容都在document里面。
2、获取DOM元素
2.1、根据CSS选择器来获取DOM元素
2.1.1、选择匹配的第一个元素
语法:document.querySelector('css选择器')
参数:包含一个或多个有效的CSS选择器字符串。
返回值:CSS选择器匹配的第一个元素,一个HTMLElement对象。
<body>
<div class="box">123</div>
<div class="box">abc</div>
<p id="nav">导航栏</p>
<ul>
<li>测试</li>
<li>测试</li>
<li>测试</li>
<li>测试</li>
</ul>
<script>
// 获取匹配的第一个元素
const box = document.querySelector('div')
// const box = document.querySelector('.box')
console.log(box) // 123
const nav = document.querySelector('#nav')
console.log(nav)
// 获取第一个小ul.li
const li = document.querySelector('ul li:first-child')
console.log(li)
</script>
</body>
2.1.2、选择匹配的多个元素
语法:document.querySelectorAll('css选择器')
参数:包含一个或多个有效的CSS选择器字符串。
返回值:CSS选择器匹配的多个元素,得到的是一个伪数组(有长度有索引号,但是没有pop()、push()等数组方法,想要得到里面的每一个对象,则需要遍历(for)的方式获得)。
<body>
<div class="box">123</div>
<div class="box">abc</div>
<p id="nav">导航栏</p>
<ul>
<li>测试</li>
<li>测试</li>
<li>测试</li>
<li>测试</li>
</ul>
<script>
const lis = document.querySelectorAll('ul li')
console.log(lis)
for(let i = 0; i < lis.length; i++) {
console.log(lis[i]) // 每一个li对象
}
const p = document.querySelectorAll('#nav')
p[0].style.color = 'red'
</script>
</body>
2.1.3、其他获取DOM元素方法
<script>
// 根据id获取一个元素
document.getElementById('nav')
// 根据标签获取一类元素,获取页面所有div
document.getElementsByTagName('div')
// 根据类名获取元素,获取页面所有类名为w的
document.getElementsByClassName('w')
</script>
3、操作元素内容
- DOM对象都是根据标签生成的,所以操作标签,本质上都是操作DOM对象;
- 就是操作对象使用的点语法;
- 如果想要修改标签元素里面的内容,则可以使用如下几种方式。
3.1、元素.innerText 属性
- 将文本内容添加、更新到任意标签位置;
- 显示纯文本,不解析标签。
<body>
<div class="box">我是文字的内容</div>
<script>
// 获取元素
const box = document.querySelector('.box')
// 修改文字内容 对象.innerText 属性
console.log(box.innerText) // 获取文字内容
box.innerText = "我是一个盒子" // 修改文字内容
box.innerText = "<strong>我是一个盒子</strong>" // 不会加粗
</script>
</body>
3.2、元素.innerHTML 属性
<body>
<div class="box">我是文字的内容</div>
<script>
// 获取元素
const box = document.querySelector('.box')
// 修改文字内容 对象.innerText 属性
console.log(box.innerHTML) // 获取文字内容
box.innerHTML = "<strong>我是一个盒子</strong>" // 会加粗
</script>
</body>
4、操作元素属性
4.1、操作元素常用属性
通过js设置、修改标签元素属性,比如通过src更换图片;
最常见的属性比如:href、title、src等。
语法:对象.属性=值
<body>
<img src="./images/1.webp" alt="" />
<script>
// 获取图片元素
const img = document.querySelector('img');
// 修改图片对象的属性
img.src = '/images/2.webp'
</script>
</body>
4.2、操作元素样式属性
通过js设置、修改标签元素的样式属性,比如:
- 通过轮播图小圆点自动更换颜色样式;
- 点击按钮可以滚动图片,移动的图片的位置等等。
4.2.1、通过style属性操作css
语法:对象.style.样式属性=值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=dege" />
<meta name="viewpoint" content="width=device-width, initial-scale=1.0">
<title>年会抽奖</title>
<style>
.box {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
// 获取元素
const box = document.querySelector('.box')
// 修改样式属性 对象.style.样式属性='值'
box.style.width = '300px'
// 多组单词的采取小驼峰命名法
box.style.backgroundColor = 'hotpink'
box.style.border = '2px solid blue'
box.style.borderTop = '2px solid red'
</script>
</body>
</html>
注意:
- 修改样式通过style属性引出;
- 如果属性有 - 连接符,需要转换为小驼峰命名法;
- 赋值的时候,需要的时候不要忘记加css单位。
4.2.2、通过类名(className)修改样式
如果修改的样式比较多,直接通过style属性修改比较繁琐,我们可以通过借助于css类名的形式。
语法:元素.className = 'active' // active是一个css类名
注意:由于class是关键字,所以使用className去代替,className是使用新值换旧值,如果需要添加一个类,需要保留之前的类名。
4.2.3、通过classList操作类控制css
为了解决className容易覆盖以前的类名,我们可以通过classList方式追加和删除类名。
语法:
// 追加一个类
元素.classList.add('类名')
// 删除一个类
元素.classList.remove('类名')
// 切换一个类
元素.classList.toggle('类名')
4.3、操作表单元素属性
表单很多情况,也需要修改属性,比如点击眼睛,可以看到密码,本质是把表单类型转换为文本框。正常的有属性有取值的,跟其他的标签属性没有任何区别。
获取:DOM对象.属性名
设置:DOM对象.属性名 = 新值
表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示,如果为true代表添加了该属性,如果是false代表移除了该属性。
<body>
<input type="text" value="电脑">
<script>
// 获取元素
const uname = document.querySelector('input')
// 获取值
console.log(uname.value)
uname.type = 'password'
</script>
</body>
<body>
<input type="checkbox" name="" id="">
<button disabled="disabled">点击</button>
<script>
// 获取元素
const ipt = document.querySelector('input')
console.log(ipt.checked) // false 只接受布尔值
ipt.checked = true
// ipt.checked = 'true' // 能选中,但不提倡,会隐式转换
// 获取按钮
const button = document.querySelector('button')
console.log(button.disabled) // 默认false,不禁用
button.disabled = true // 禁用
</script>
</body>
4.4、自定义属性
标准属性:标签天生自带的属性,比如class id title等,可以直接使用点语法操作,比如:disabled、checked、selected。
自定义属性:
- 在html5中推出来了专门的data-自定义属性;
- 在标签上一律以 data- 开头;
- 在DOM对象上一律以dataset对象方式获取。
<body>
<div1 data-id="1">1</div1>
<div2 data-id="2">2</div2>
<div3 data-id="3">3</div3>
<div4 data-id="4">4</div4>
<div5 data-id="5">5</div5>
<script>
const one = document.querySelector('div')
console.log(one.dataset.id)
</script>
</body>
4.5、定时器-间歇函数
目标:能够使用定时器函数重复执行代码。
定时器函数可以开启和关闭定时器。
4.5.1、开启定时器
let n = setInterval(函数, 间隔时间)
作用:每隔一段时间调用这个函数。
- 间隔时间单位是毫秒。
- 函数名字不需要加括号;
- 定时器返回的是一个id数字。
4.5.2、关闭定时器
clearInterval(变量名)
5、事件监听
5.1、事件监听
目标:能够给DOM元素添加事件监听。
什么是事件:事件是在编程时系统内发生的动作或者发生的事情,比如用户在网页上单击一个按钮。
什么是事件监听:就是让程序检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应,也称为绑定事件或者注册事件。比如鼠标经过显示下拉菜单,比如点击可以播放轮播图等等。
语法:元素对象.addEventListener('事件类型', 要执行的函数)
事件监听三要素:
- 事件源:哪个dom元素被事件触发了,要获取dom元素;
- 事件类型:用什么方式触发,比如鼠标单击click,鼠标经过mouseover等;
- 事件调用的函数:要做什么事。
注意:
- 事件类型要加引号;
- 函数是点击之后再去执行,每次点击都会执行一次。
5.2、事件监听版本
- DOM L0:事件源.on事件 = function() {}
- DOM L2:事件源.addEventListener(事件, 事件处理函数)
区别:on方式会被覆盖,addEventListener方式可绑定多次,拥有事件更多特性,推荐使用。
5.2、事件类型
- 鼠标事件(鼠标触发):click(鼠标点击)、mouseenter(鼠标进入)、mouseleave(鼠标离开);
- 焦点事件(表单获得光标):focus(获得焦点)、blur(失去焦点);
- 键盘事件(键盘触发):keydown(键盘按下触发)、keyup(键盘抬起触发);
- 文本事件(表单输入触发):input(用户输入事件)。
5.3、事件对象
事件对象也是个对象,这个对象里有事件触发时的相关信息。例如,鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息。
使用场景:可以判断用户按下了哪个键,比如按下回车键可以发布新闻,可以判断鼠标点击了哪个元素,从而做相应的操作。
5.3.1、获取事件对象
如何获取:在事件绑定的回调函数的第一个参数就是事件对象,一般命名为event、ev、e。
元素.addEventListener('click', function(e) {})
部分常用属性:
- type:获取当前的事件类型;
- clientX、clientY:获取光标相对于浏览器可见窗口左上角的位置;
- offsetX、offsetY:获取光标相对于当前DOM元素左上角的位置;
- key:用户按下的键盘键的值,现在不提倡使用keyCode。
5.4、环境对象
环境对象指的是函数内部特殊的变量this,它代表着当前函数运行时所处的环境。
作用:弄清楚this的指向,可以让我们的代码更简洁。
- 函数的调用方式不同,this指代的对象也不同;
- 谁调用,this就是谁,这是判断this指向的粗略规则;
- 直接调用函数,其实相当于是window函数,所以this知道window。
5.5、回调函数
如果将函数A作为参数传递给函数B时,我们称函数A为回调函数。
简单理解:当一个函数当作参数来传递给另外一个函数的时候,这个函数就是回调函数。
5.6、事件流
事件流指的是事件完整执行过程中的流动路径。捕获阶段是父到子,冒泡阶段是子到父。
5.6.1、事件捕获
事件捕获:从DOM的根元素开始去执行对应的事件(从外到里)。
代码:DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
说明:addEventListener 第三个参数传入true代表是捕获阶段触发(很少使用),若传入false代表冒泡阶段触发,默认就是false。若是用L0事件监听,则只有冒泡阶段,没有捕获。
5.6.2、事件冒泡
事件冒泡:当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。
说明:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件,事件冒泡是默认存在的,L2事件监听第三个参数是false,或者默认都是冒泡。
5.6.3、阻止捕获/冒泡
因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素,若想把事件就限制在当前元素内,就需要阻止事件冒泡。
语法:事件对象.stopPropagation()
说明:此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效。
5.6.4、解绑事件
on事件 方式,直接使用null覆盖就可以实现事件的解绑。
语法:
btn.onclick = function() {
alert('点击了')
}
// 解绑事件
btn.onclick = null
addEventListener 方式 语法:元素.addEventListener(事件类型, 事件处理函数, 是否使用捕获)
。
5.6.5、鼠标经过事件的区别
- mouseover 和 mouseout 会有冒泡效果;
- mouseenter 和 mouseleave 没有冒泡效果(推荐)。
5.6.6、两种注册事件的区别
传统on注册(L0):
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件);
- 直接使用null覆盖就可以实现事件的解绑;
- 都是冒泡阶段执行的。
事件监听注册(L2):
- 后面注册的事件不会覆盖前面注册的事件(同一个事件);
- 可以通过第三个参数去确定是在冒泡或者捕获阶段执行;
- 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡杰顿);
- 匿名函数无法被解绑。
5.7、事件委托
事件委托:是利用事件流的特征解决一些开发需求的技巧。
优点:减少注册次数,可以提高程序性能。
原理:事件委托其实是利用事件冒泡的特点。给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件。
5.8、阻止元素默认行为
语法:e.preventDefault()
5.9、其他事件
5.9.1、页面加载事件
1、有时候需要等页面资源全部处理完了做一些事情,老代码喜欢把script写在head中,这时候直接找dom元素找不到。
加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件。
事件名:load。
监听页面所有资源加载完毕:给window添加load事件:window.addEventListener('load', function() {})
注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件。
2、当初始的HTML文档被完全加载和解析完成之后,DOMContentLoaded事件被触发,而无需等待样式表、图像等完全加载。
事件名:DOMContentLoaded。document.addEventListener('DOMContentLoaded', function(){})
5.9.2、元素滚动事件
滚动条在滚动的时候持续触发的事件。
事件名:scroll。监听整个页面滚动:window.addEventListener('scroll', functio(){})
5.9.2.1、页面滚动事件 - 获取位置
scrollLeft 和 scrollTop(属性):
- 获取被卷去的大小;
- 获取元素内容往左、往上滚出去看不到的距离;
- 这两个值是可读写的。
5.9.2.2、页面滚动事件 - 滚动到指定的位置
scrollTo()方法可把内容滚动到指定的坐标。
// 让页面滚动到y轴1000像素的位置
window.scrollTo(0, 1000)
5.9.3、页面尺寸事件
会在窗口尺寸改变的时候触发事件。
事件名:resize。window.addEventListener('resize', function(){})
获取宽高:获取元素的可见部分宽高(不包含边框、margin、滚动条等),clientWidth 和 clientHeight 。
5.9.4、元素尺寸与位置
获取宽高:
- 获取元素的自身宽高,包含元素自身设置的宽高、padding、border;
- offsetWidth 和 offsetHeight;
- 获取出来的是数值,方便计算。
- 注意:获取的是可视宽高,如果盒子是隐藏的,获取的结果是0。
获取位置:
- 获取元素距离自己定位父级元素的左、上距离;
- offsetLeft 和 offsetTop,注意是只读属性。
6、日期对象
日期对象:用来表示事件的对象。
作用:可以得到当前系统时间。
6.1、实例化
// 获得当前时间
const date = new Date()
// 获得指定时间
const date = new Date('2022-5-1 08:30:00')
6.2、日期对象方法
// 获取年份(4位)
getFullYear()
// 获得月份,取值为0-11
getMonth()
// 获取月份中的每一天,不同月份取值也不相同
getDate()
// 获取星期,取值为0-6
getDay()
// 获取小时,取值为0-23
getHours()
// 获取分钟,取值为0-59
getMinutes()
// 获取秒,取值为 0-59
getSeconds()
6.3、时间戳
如果计算倒计时效果,则需要借助于时间戳完成。
时间戳是指1970年1月1日0时0分0秒起至现在的毫秒数,它是一种特殊的计量时间的方式。
算法:
- 将来的时间戳 - 现在的时间戳 = 剩余时间毫秒数;
- 剩余时间毫秒数转换为剩余时间的年月日时分秒就是倒计时时间;
- 比如 将来时间戳 2000ms - 现在时间戳1000ms = 1000ms;
- 1000ms转换为秒就是0时0分1秒。
7、节点操作
7.1、DOM节点
DOM节点:DOM树里每一个内容都称之为节点。
节点类型:
- 元素节点:所有的标签比如body、div,html是根节点;
- 属性节点:所有的属性比如href;
- 文本节点:所有的文本;
- 其他。
7.2、查找节点
节点关系:针对的找亲戚返回的都是对象。
7.2.1、父节点
查找:子元素.parentNode
,返回最近一级的父节点,找不到返回为null。
7.2.2、子节点
查找:父元素.childNodes
,获得所有子节点,包括文本节点(空格、换行)、注释节点等。
查找:父元素.children
,重点,仅获得所有元素节点,返回的还是一个伪数组。
7.2.3、兄弟节点
查找:元素.nextElementSibling
,下一个兄弟节点。
查找:元素.previousElementSibling
,上一个兄弟节点。
7.3、增加节点
一般来说,增加一个节点,按照如下操作:
- 创建一个新的节点;
- 把创建的新的节点放入到指定的元素即可。
1、创建节点
即创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点,创建元素节点方法:document.createElement('标签名')
。
2、追加节点
要想在界面看到,还得插入到某个父元素中,插入到父元素的最后一个子元素:父元素.appendChild(要插入的元素)
,插入到父元素中某个子元素的前面。
7.4、克隆节点 & 删除节点
克隆节点:元素.cloneNode(布尔值)
,cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值。若为true,则代表克隆时会包含后代节点一起克隆;若为false,则代表克隆时不包含后代节点。默认为false。
若一个节点在页面中不再需要时,可以删除它,在JavaScript原生DOM操作中,要删除元素必须通过父元素删除。
语法:父元素.removeChild(要删除的元素)
。
注意:
- 如不存在父子关系则删除不成功;
- 删除节点和隐藏节点(display:none)有区别:隐藏节点还是存在的,但是删除,则从html中删除节点。
8、M端事件(移动端事件)
移动端也有自己独特的地方,比如触屏事件(touch,也称触摸事件),Android 和 IOS 都有。
touch对象代表一个触摸点,触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。
常用的触屏事件如下:
// 手指触摸到一个DOM元素时触发
touchstart
// 手指在一个DOM元素上滑动时触发
touchmove
// 手指从一个DOM元素上移开时触发
touchend
9、JS插件
插件:就是别人写好的一些代码,我们只需要复制对应的代码,就可以直接实现对应的效果。
10、Window对象
10.1、BOM(浏览器对象模型)
BOM(Brower Object Model)是浏览器对象模型。
- window对象是一个全局对象,也可以说是JavaScript中的顶级对象;
- 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的;
- 所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法;
- window对象下的属性和方法调用的时候可以省略window。
10.2、定时器 - 延时函数
JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout()。
语法:setTimeout(回调函数,等待的毫秒数)
setTimeout()仅仅执行一次,所以可以理解为就是把一段代码延迟执行,平时省略window。
清除延时函数:clearTimeout(timer)
两种定时器对比:延时函数执行一次,间歇函数每隔一段时间就执行一次,除非手动清除。
10.3、JS 执行机制
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这是因为JavaScript这门脚本语言诞生的使命所致——JavaScript是为处理页面中用户的交互,以及操作DOM而诞生的。比如我们对某个DOM元素进行添加和删除操作,不能同时进行。应该先进行添加,之后再删除。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:如果JS执行时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
为了解决这个问题,利用多核CPU的计算能力,HTML提出Web Worker 标准,允许JavaScript 脚本创建多个线程,于是JS中出现了同步和异步。
- 同步:前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。
- 异步:在做这件事的同时,还可以去处理其他事情。
同步任务:都在主线程上执行,形成一个执行栈。
异步任务:通过回调函数实现。一般而言,异步任务有以下三种类型:① 普通事件:如click、resize等;② 资源加载:如load、error等;③ 定时器:如setInterval、setTimeout等。异步任务相关添加到任务队列中(任务队列也称为消息队列)
JS执行机制:① 先执行执行栈中的同步任务;② 异步任务放入任务队列中;③ 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
由于主线程不断地重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(eventloop)。
10.4、location 对象
location 的数据类型是对象,它拆分并保存了URL地址的各个组成部分。
window.location
常用属性和方法:
// 完整的URL地址,对其赋值时用于地址的跳转
window.location.href
// 获取地址中的hash值,符号后面部分
window.location.hash
// 用来刷新当前页面,传入参数true时表示强制刷新
window.location.reload
10.5、navigator 对象
10.6、history 对象
history 的数据类型是对象,主要管理历史记录,该对象与浏览器地址栏的操作相对应,如前进、后退、历史记录等。
// 可以后退功能
window.history.back()
// 前进功能
window.history.forward()
// 前进后退功能,参数如果是1前进一个页面,如果是-1后退一个页面
window.history.go()
history 对象一般在实际开发中比较少用,但是会在一些OA办公系统中见到。
11、本地存储
11.1、介绍
随着互联网的快速发展,基于网页的应用越来越普遍,同时也变得越来越复杂,为了满足各种各样的需求,会经常性在本地存储大量的数据,HTML5规范提出了相关解决方案。
- 数据存储在浏览器中;
- 设置、读取方便,甚至页面刷新不丢失数据;
- 容量较大,sessionStorage 和 localStorage 约 5m 左右。
11.2、分类 - localStorage
作用:可以将数据永久存储在本地(用户的电脑),除非手动删除,否则关闭页面也会存在。
特性:① 可以多窗口(页面)共享(同一浏览器可以共享);② 以键值对的形式存储使用。
语法:localStorage.setItem(key, value) localStorage.getItem(key, value)
特性:
- 生命周期为关闭浏览器窗口;
- 在同一个窗口(页面)下数据可以共享;
- 以键值对的形式存储使用;
- 用法跟localStorage基本相同。
11.3、存储复杂数据类型
本地只能存储字符串,无法存储复杂数据类型。
解决:需要将复杂数据类型转换成JSON字符串,再存储到本地。
语法:
// 将复杂数据转换成JSON字符串,存储在本地中。
JSON.stringfy(复杂数据类型)
// 把取出来的字符串转换为对象
JSON.parse(JSON字符串)
12、数组 map() 和 join() 方法
map 也称为映射。映射是个术语,指两个元素的集之间元素相互‘对应’的关系,map 重点在于有返回值,forEach 没有返回值。
// map 使用场景:可以遍历数组处理数据,并返回新的数组
const arr = ['red', 'blue', 'green']
const newArr = arr.map(function (ele, index) {
console.log(ele)
console.log(index)
return ele + '颜色'
})
console.log(newArr)
// join() 方法把数组中所有元素转换成一个字符串
// 参数:数组元素是通过参数里面指定的分隔符进行分隔的
const arr = ['red颜色', 'blue颜色', 'green颜色']
console.log(arr.join('')) // red颜色blue颜色green颜色
13、正则表达式
13.1、介绍 & 语法
正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。
通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式。
语法:
// 1、定义正则表达式语法
const regObj = /表达式/
// 2、用于判断是否有符合规则的字符串
regObj.test(被检测的字符串) // 匹配为true,不匹配的false
// 3、用于检索(查找)符合规则的字符串
regObj.exec(被检测的字符串) // 找到返回数组,否则为null
13.2、元字符
普通字符:大多数的字符仅能够描述它们本身,这些字符称为普通字符,例如所有的字母和数字。也就是说普通字符只能够匹配字符串中与它们相同的字符。
元字符:是一些具有特殊含义的字符,可以极大提高灵活性和强大的匹配功能。比如,规定用户只能输入英文26个英文字母,普通字符写法abcde...,但是换成元字符写法:[a-2]
元字符的分类:
13.2.1、边界符
边界符:表示位置,开头和结尾,必须用什么开头,用什么结尾;
- ^ :表示匹配行首的文本(以谁开始)
- $ :表示匹配行尾的文本(以谁结束)
13.2.2、量词
量词:表示重复次数;
量词 | 说明 |
* | 重复0次或更多次 |
+ | 重复1次或更多次 |
? | 重复0次或1次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
13.2.3、字符类
字符类:比如 \d 表示 0-9。
// [] 匹配字符集合 后面的字符串只要包含abc中任意一个字符,都返回true
console.log(/[abc]/.test('andy'))
// [] 里面加上 - 连接符,使用连接符 - 表示一个范围
console.log(/^[a-z]$/.test('c')) // true
比如:
[a-z] 表示a到z的26个英文字母都可以;
[a-zA-Z] 表示大小写都可以;
[0-9] 表示 0-9 的数字都可以。
// 预定义类:指的是某些常见模式的简写形式
比如:
\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]
13.3、修饰符
修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等。语法:/表达式/修饰符
- i是单词ignore的缩写,正则匹配时字母不区分大小写;
- g是单词global的缩写,匹配所有满足正则表达式的结果。
// 替换
字符串.replace(/正则表达式/, '替换的文本')
14、作用域
作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”,变量便不能被访问。
14.1、局部作用域
局部作用域分为函数作用域和块作用域。
14.1.1、函数作用域
- 在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
- 函数的参数也是函数内部的局部变量;
- 不同函数内部声明的变量无法互相访问;
- 函数执行完毕后,函数内部的变量实际被清空了。
14.1.2、块作用域
在JavaScript中使用{}包裹的代码称为代码块,代码块内部声明的变量外部将 【有可能】 无法被访问。
- let 声明的变量会产生块作用域,var 不会产生块作用域;
- const 声明的常量也会产生块作用域;
- 不同代码块之间的变量无法互相访问;
- 推荐使用 let 和 const。
for (let t = 1; t <= 6;t++) {
// t 只能在该代码块中被访问
console.log(t) // 正常
}
// 超出了t的作用域
console.log(t) // 报错
14.2、全局作用域
<script> 标签和 .js 文件的最外层就是所谓的全局作用域,在此声明的变量在函数内部也可以被正常访问。全局作用域中声明的变量,任何其他作用域都可以被访问。
<script>
// 全局作用域
// 全局作用域下声明了 num 变量
const num = 10
function fn() {
// 函数内部可以使用全局作用域的变量
console.log(num)
}
// 此处全局作用域
</script>
注意:
- 为window对象动态添加的属性默认也是全局的,不推荐;
- 函数中未使用任何关键字声明的变量为全局变量,不推荐;
- 尽可能少声明全局变量,防止全局变量被污染。
14.3、作用域链
作用域链本质上是底层的变量查找机制。
- 在函数被执行时,会优先查找当前函数作用域中查找变量;
- 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域。
总结:
- 嵌套关系的作用域串联起来形成了作用域链;
- 相同作用域链中按着从小到大的规则查找变量;
- 子作用域能够访问父作用域,父级作用域无法访问子级作用域。
14.4、垃圾回收机制
垃圾回收机制(Garbage Collection)简称GC。JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
14.4.1、内存声明周期
JS环境中分配的内存,一般有如下生命周期:
- 内存分配:当我们声明变量、函数、对象的时候,系统会自动为它们分配内存;
- 内存使用:即读写内存,也就是使用变量、函数等;
- 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存。
说明:
- 全局变量一般不会回收(关闭页面回收);
- 一般情况下局部变量的值,不用了,会被自动回收掉。
内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏。
14.4.2、JS 垃圾回收机制 - 算法说明
堆栈空间分配区别:
- 栈(操作系统):由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面;
- 堆(操作系统):一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面。
下面介绍2种常见的浏览器垃圾回收算法:引用计数法 和 标记清除法。
1、引用计数法
IE采用引用计数算法,定义 “内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象。
算法:
- 跟踪记录被引用的次数;
- 如果被引用了依次,那么就记录次数1,多次引用会累加++;
- 如果减少一个引用就减1 --;
- 如果引用次数是0,则释放内存。
但是它会存在一个致命的问题:嵌套引用(循环引用)。如果两个对象相互引用,尽管它们已不再使用,垃圾回收器不会进行回收,导致内存泄漏。
function fn() {
let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
return '引用计数无法回收'
}
fn()
因为它们的引用次数永远不会是0,这样的相互引用如果说很大量的存在就会导致大量的内存泄漏。
2、标记清除法
现代的浏览器已经不再使用引用计数算法了。现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想是一致的。
核心:
- 标记清除算法将“不再使用的对象”定义为“无法达到的对象”;
- 就是从根部(在JS中就是全局对象)出发时扫描内存中的对象,凡是能从根部到达的对象,都是还需要使用的;
- 那些无法由根部触发触及到的对象被标记为不再使用,稍后进行回收。
14.5、闭包
闭包:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外围函数的作用域。
作用:封闭数据,提供操作,外部也可以访问函数内部的变量。
简单理解:闭包=内层函数+外层函数的变量。
先看个简单的代码:
function outer() {
const a = 1
function f() {
console.log(a)
}
return f
}
const fun = outer();
fun()
闭包应用:实现数据的私有。
14.6、变量提升
变量提升是JavaScript中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)。
// 访问变量 str
console.log(str + 'world!')
// 声明变量str
var str = 'hello'
注意:
- 变量在未声明即被访问时会报语法错误;
- 变量在var声明之前即被访问,变量的值为undefined;
- let/const声明的变量不存在变量提升;
- 变量提升出现在相同作用域当中;
- 实际开发中推荐先声明再访问变量。
15、函数进阶
15.1、函数提升
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。
<script>
// 会把所有函数声明提升到当前作用域的最前面
fn()
function fn() {
console.log('函数提升')
}
</script>
总结:
- 函数提升能够使函数的声明调用更灵活;
- 函数表达式不存在提升的现象;
- 函数提升出现在相同作用域当中。
15.2、函数参数
15.2.1、动态参数
arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。
// 求和函数,计算所有参数的和
function sum() {
// console.log(arguments)
let s = 0
for(let i = 0; i < arguments.length; i++) {
s += arguments[i]
}
console.log(s)
}
// 调用求和函数
sum(5,10)
sum(1, 2, 3)
15.2.2、剩余参数
剩余参数允许我们将一个不定数量的参数表示为一个数组。
// ... 是语法符号,置于最末函数形参之前,用于获取多余的实参
// 借助 ... 获取的剩余实参,是个真数组
function config(baseURL, ...other) {
console.log(baseURL)
console.log(other)
}
// 调用函数
config('http://baidu.com', 'get', 'json')
15.2.3、展开运算符
展开运算符(...)将一个数组进行展开。
典型运用场景:求数组最大值(最小值)、合并数组等。
语法:
// 方式1
function getSum(...other) {
// other 得到 [1, 2, 3]
console.log(other)
}
// 方式2
const arr = [1, 2, 3]
console.log(...arr)
15.3、箭头函数
15.3.1、概念
引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁。
使用场景:箭头函数更适用于那些本来需要匿名函数的地方。
// 1、基本写法
const fn = function() {}
fn()
// 2、如果函数体只有一行代码,可以写到一行上,并且无需写return直接返回值
const fn = (x,y) => x + y
fn(1, 2)
// 3、加括号的函数体返回对象字面量表达式
const fn = uname => ({uname:uname})
console.log(fn('aaa')) // uname: 'aaa'
15.3.2、箭头函数参数
- 普通函数有 arguments 动态参数;
- 箭头函数没有 arguments 动态参数,但是有剩余参数 ...args。
15.3.3、箭头函数 this
在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值,非常令人讨厌。
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
16、解构赋值
16.1、数组结构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。
基本语法:
赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量;
变量的顺序对应数组单元值的位置依次进行赋值操作。
// 立即执行函数
(function t() {})();
// 或者
;(function t() {})()
// 普通的数组
const arr = [1, 2, 3]
// 批量声明变量 a b c
// 数组解构:同时将数组单元值 1 2 3 依次赋值给变量 a b c
const [a, b, c] = arr
console.log(a)
console.log(b)
console.log(c)
// 交换2个变量
let a = 1
let b = 2
[b, a] = [a, b]
console.log(a)
console.log(b)
// 为防止有undefined传递单元值的情况,可以设置默认值
const [a = '手机', b = '华为'] = ['小米']
console.log(a) // 小米
console.log(b) // 华为
// 按需导入,忽略某些值
const [a, , c, d] = ['小米', '苹果', '华为', '格力']
// 支持多维数组的解构
const [a, b] = ['苹果', ['小米', '华为']]
console.log(a) // 苹果
console.log(b) // ['小米', '华为']
// 如果想拿到小米和华为
const [a, [b, c]] = ['苹果', ['小米', '华为']]
console.log(a) // 苹果
console.log(b) // 小米
console.log(c) // 华为
16.2、对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法。
16.2.1、基本语法
- 赋值运算符 = 左侧的{}用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量;
- 对象属性的值将被赋值与属性名相同的变量;
- 注意解构的变量名不要和外面的变量名冲突,否则报错;
- 对象中找不到与变量名一致的属性时变量值为 undefined。
const {uanme, age} = {uname: 'aaa', age: 18}
17、forEach() 遍历数组
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。
主要使用场景:遍历数组的每个元素。
语法:
被遍历的数组.forEach(function(当前数组元素, 当前元素索引号) {
// 函数体
})
注意:
- forEach() 主要是遍历数组;
- 参数当前数组元素是必须要写的,索引号可选。
18、构造函数
18.1、创建对象的3种方式
// 1、利用对象字面量创建对象
const o = {
name: '佩奇'
}
// 2、利用 new Object 创建对象
const o = new Object({name: '佩奇'})
console.log(o)
// 3、利用构造函数创建对象
18.2、构造函数
构造函数:是一种特殊的函数,主要用来初始化对象。
使用场景:常规的 {...} 语法允许创建一个对象。比如我们创建了佩奇的对象,继续创建乔治的对象还需要重新写一遍,此时可以通过构造函数来快速创建多个类似的对象。
构造函数在技术上是常规函数,不过有2个约定:
- 它们的命名以大写字母开头;
- 它们只能由“new”操作符来执行。
function Pig(name, age) {
this.name = name
this.age = age
}
const george = new Pig('佩奇', 6, '女')
说明:
- 使用 new 关键字调用函数的行为被称为“实例化”;
- 实例化构造函数时没有参数时可以省略;
- 构造函数内部无需写return,返回值即为创建的对象;
- 构造函数内部的return返回值无效,所以不要写return;
- new Object() new Date() 也是实例化构造函数。
实例化执行过程:
- 创建新对象;
- 构造函数this指向新对象;
- 执行构造函数代码,修改this,添加新的属性;
- 返回新对象。
18.3、实例成员 & 静态成员
实例成员:通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)。
说明:
- 为构造函数传入参数,创建结构相同但值不同的对象;
- 构造函数创建的实例对象彼此独立互不影响。
静态成员:构造函数的属性和方法被称为静态成员(静态属性和静态方法)。
// 构造函数
function Person(name, age) {
// 省略实例成员
}
// 静态属性
Person.eyes = 2
Person.arms = 2
// 静态方法
Person.walk = function() {
console.log('aaaaa')
// this 指向 Person
console.log(this.eyes)
}
说明:
- 静态成员只能构造函数来访问;
- 静态方法中的this指向构造函数。
比如 Date.now() Math.PI Math.random()
18.4、内置构造函数
在JavaScript中最主要的数据类型有6种:
- 基本数据类型:字符串、数值、布尔、undefined、null;
- 引用类型:对象。
但是,我们会发现有些特殊情况:
// 普通字符串
const str = 'andy'
console.log(str.length) // 4
其实,字符串、数值、布尔等基本类型也都有专门的构造函数,这些我们称为包装类型。JS中几乎所有的数据都可以基于构造函数创建。
- 引用类型:Object、Array、RegExp、Date等;
- 包装类型:String、Number、Boolean等。
18.4.1、Object
Object 是内置的构造函数,用于创建普通对象。
// 通过构造函数创建普通对象
const str = new Object({name:'小明', age: 15})
推荐使用字面量方式声明对象,而不是Object构造函数。
// 获取对象中所有属性(键)
const o = {name: '佩奇', age: 6}
const arr = Object.keys(o) // 获得对象的所有键,并且返回是一个数组
console.log(arr) // ['name', 'age']
// 获取对象中所有属性(值)
const arr = Object.values(o)
// 对对象进行拷贝
const oo = Object.assign(o)
18.4.2、Array
Array 是内置的构造函数,用于创建数组:
const str = new Array(3, 5)
console.log(arr)
创建数组建议使用字面量创建,不用Array构造函数创建。
数组常见实例方法 - 核心方法:
方法 | 作用 | 说明 |
forEach | 遍历数组 | 不返回数组,经常用于查找遍历数组元素 |
filter | 过滤数组 | 返回新数组,返回的是筛选满足条件的数组元素 |
map | 迭代数组 | 返回新数组,返回的是处理之后的数组元素,想要使用返回的新数组 |
reduce | 累计器 | 返回累计处理的结果,经常用于求和等 |
数组常见方法 - 其他方法:
- join:数组元素拼接为字符串,返回字符串;
- find:查找元素,返回符合测试条件的第一个数组元素值,如果没有符合条件的则返回 undefined;
- every:检测数组所有元素是否都符合指定条件,如果所有元素都通过检测返回true,否则返回false;
- some:检测数组中的元素是否满足指定条件,如果数组中有元素满足条件返回true,否则返回false;
- concat:合并两个数组,返回生成新数组;
- sort:对原数组单元值排序;
- splice:删除或替换原数组单元;
- reverse:反转数组;
- findIndex:查找元素的索引值。
18.4.3、String
在 JavaScript 中的字符串、数值、布尔具有对象的使用特征,如具有属性和方法。之所以有对象特征的原因是字符串、数值、布尔类型数据是JavaScript底层使用Object构造函数“包装”来的,被称为包装类型。
String 常见实例方法:
- length(属性):用来获取字符串的长度;
- split('分隔符'):用来将字符串拆分成数组;
- substring(需要截取的第一个字符的索引[, 结束的索引号]):用于字符串截取;
- startsWith(检测字符串[, 检测位置索引号]):检测是否以某字符开头;
- includes(搜索的字符串[, 检测位置索引号]):判断一个字符串是否包含在另一个字符串中,根据情况返回true或false;
- toUpperCase:用于将字母转换成大写;
- toLowerCase:用于将字母转换成小写;
- indexOf:检测是否包含某字符;
- endsWith:检测是否以某字符结尾;
- replace:用于替换字符串,支持正则匹配;
- match:用于查找字符串,支持正则匹配。
19、编程思想
19.1、面向过程 & 面向对象
19.1.1、面向过程
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个地依次调用就可以。
优点:性能比面向对象高,适合跟硬件联系很紧密地东西,例如单片机就采用面向过程编程;
缺点:没有面向对象易维护、易复用、易扩展。
19.1.2、面向对象(oop)
面向对象就是把事务分解成一个个对象,然后对象之间分工与合作。
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性地特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
缺点:性能比面向过程低。
19.2、构造函数
封装是面向对象思想中比较重要的一部分,js面向对象可以通过构造函数实现的封装。同样的将变量和函数组合到了一起并能通过this实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。
总结:
- 构造函数体现了面向对象的封装性;
- 构造函数实例创建的对象彼此独立,互不影响。
20、原型
20.1、概述
- 构造函数通过原型分配的函数是所有对象所共享的;
- JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象,所以我们也称为原型对象;
- 这个对象可以挂在函数,对象实例化不会多次创建原型上的函数,节约内存;
- 我们可以把那些不变的方法,直接定义在prototype上,这样所有对象的实例就可以共享这些方法;
- 构造函数和原型对象中的this都指向实例化的对象。
20.2、constructor 属性
每个原型对象里面都有个constructor属性(constructor构造函数)。
作用:该属性指向该原型对象的构造函数。
使用场景:如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个constructor指向原来的构造函数。
20.3、对象原型
构造函数可以创建实例对象,构造函数还有一个原型对象,一些公共的属性或者方法放到这个原型对象身上。
对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。
注意:
- __proto__ 是JS非标准属性;
- [[prototype]] 和 __proto__ 意义相同;
- 用来表明当前实例对象指向哪个原型对象 prototype;
- __proto__ 对象原型里面也有一个 constructor 属性,指向创建该实例对象的构造函数。
20.4、原型继承
继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承的特性。
20.5、原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链。
原型链的查找规则:
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性;
- 如果没有就查找它的原型(也就是__proto__指向的 prototype 原型对象);
- 如果还没有就查找原型对象的原型(Object的原型对象);
- 依此类推一直找到Object为止(null);
- __proto__ 对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线;
- 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
21、深浅拷贝
首先浅拷贝和深拷贝只针对引用类型。
21.1、浅拷贝
浅拷贝:拷贝的只是地址。
常见方法:
- 拷贝对象:Object.assign() // 展开运算符 {...obj} 拷贝对象;
- 拷贝数组:Array.prototype.concat() 或者 [...arr]。
21.2、深拷贝
深拷贝:拷贝的是对象,不是地址。
常见方法:
- 通过递归实现深拷贝;
- lodash / cloneDeep;
- 通过JSON.stringfy()实现。
22、异常处理
22.1、throw 抛异常
异常处理是指预估代码执行过程中可能发生的错误,然后最大程度地避免错误的发生导致整个程序无法运行。
function counter(x, y) {
if (!x || !y) {
// throw '参数不能为空!'
throw new Error('参数不能为空')
}
return x + y
}
counter()
总结:
- throw 抛出异常信息,程序也会终止执行;
- throw 后面跟的是错误提示信息;
- Error 对象配合 throw 使用,能够设置更详细的错误信息。
22.2、try / catch 捕获异常
我们可以通过 try /catch 捕获错误信息(浏览器提供的错误信息),try 试试 catch 拦住 finally 最后。
try {
} catch (err) {
} finally {
}
总结:
- try...catch 用于捕获错误信息;
- 将预估可能发生错误的代码写在try代码块中;
- 如果try代码段中出现错误后,会执行catch代码段,并截获错误信息;
- finally不管是否有错误,都会执行。
22.3、debugger
23、处理this
23.1、this 指向
this 是 JavaScript 最具“魅惑”的知识点,不同的应用场合 this 的取值可能会有意想不到的结果。
23.1.1、this 指向普通函数
普通函数的调用方式决定了this的值,没有明确调用者时this值为window,严格模式下没有调用者时this的值为undefined。
23.1.2、this 指向箭头函数
箭头函数中的this与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在this。
- 箭头函数会默认帮我们绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的;
- 箭头函数中的this引用的就是最近作用域的this;
- 向外层作用域中,一层一层查找this,直到有this的定义。
23.2、改变this
JavaScript 中还允许指定函数中 this 的指向,有3个方法可以动态指定普通函数中this的指向。
23.2.1、call()
使用call方法调用函数,同时指定被调用函数中this的值。
语法:fun.call(thisArg, arg1, arg2, ...)
- thisArg:在fun函数运行时指定的this值;
- arg1,arg2:传递的其他参数;
- 返回值就是函数的返回值,因为它就是调用函数。
23.2.2、apply()
使用apply()方法调用函数,同时指定被调用函数中this的值。
语法:fun.apply(thisArg, [argsArray])
- thisArg:在fun函数运行时指定的this值;
- argsArray:传递的值,必须包含在数组里面;
- 返回值就是函数的返回值,因为它就是调用函数;
- 因此aooly主要跟数组有关系,比如使用Math.max()求数组的最大值。
23.2.3、bind()
bind()方法不会调用函数,但是能改变函数内部ths指向。
语法:fun.bind(thisArg, arg1, arg2, ...)
- thisArg:在fun函数运行时指定的this值;
- arg1,arg2:传递的其他参数;
- 返回由指定的this值和初始化参数改造的 原函数拷贝(新函数);
- 因此当我们只是想改变this指向,并且不想调用这个函数的时候,可以使用bind,比如改变定时器内部的this指向。
24、性能优化
24.1、防抖
防抖:单位时间内,频繁触发事件,只执行最后依次。
24.2、节流 - throttle
节流:单位时间内,频繁触发事件,只执行依次。