原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链

原型链查找规则
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型
- 如果还没有就查找原型对象的原型
- 以此类推一直找到Object为止(null)
- _proto_对象原型的意义就在于为对象成员查找机制提供方向
- 可以使用instanceof运算符用于检测函数的prototype属性是否出现在某个实例对象的原型链上
js
console.log(ldh instanceof Person)
Array在Object的原型链上
高级技巧
浅拷贝和深拷贝只针对引用类型。开发当中我们需重复制一个对象。直接赋值会有问题:堆和栈
深浅拷贝
浅拷贝
拷贝的是地址
- 常见方法
-
拷贝对象:Object.assgin()/展开运算符{...Object}
-
拷贝数组:Array.prototype.concat()或[...arr]
只能拷贝到外面一层
深拷贝
拷贝的是对象,不是地址。
- 常见方法:
- 通过递归实现深拷贝
- lodash/clonelDeep
- JSON.stringify()实现
- 递归函数:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
- 作用与循环效果类似
- 容易发生"栈溢出",必须写退出条件
-
newObj[k] 属性名 = oldObj[k] 属性值
如果有变量,使用中括号
一定先写数组在写对象
-
JS库lodash里面cloneDeep内部实现了深拷贝
写法: _.cloneDeep(obj)
异常处理
异常处理是指预估代码可能出现的问题,然后最大程度的避免问题的方法
throw抛异常
- throw抛出异常信息,程序也会终止执行
- throw后面跟的是错误信息提示
- 配合Error使用
js
<script>
function fn(x, y) {
if (!x || !y) {
throw new Error('没有参数传递')
}
return x + y
}
console.log(fn())
</script>
try/catch捕获异常
try试试 catch拦截 finally最后
拦截错误,提示错误信息,但不中断程序,如果需要中断则加入return
- try...catch用于捕获错误信息
- 将预估可能发生错误的代码写在try中
- 如果try中发生错误后,会执行catch代码,截取错误信息
- finally不管是否有错,都会执行
js
function fn() {
try {
const p = document.querySelector('p')
p.style.color = 'red'
} catch (err) {
//拦截错误,提示错误信息,但不中断程序
console.log(err.message)
}
//不管程序对不对,都会执行
finally {
alert('嘻嘻')
}
}
fn()
debugger
js
<script>
h = {
uname: 'pink',
age: 18,
hobby: ['乒乓球', '足球'],
family: {
baby: '小pink'
}
}
const o = {}
function deepCopy(newObj, oldObj) {
debugger
for (let k in oldObj) {
if (oldObj[k] instanceof Array) {
newObj[k] = []
deepCopy(newObj[k], oldObj[k])
} else if (oldObj[k] instanceof Object) {
newObj[k] = {}
deepCopy(newObj[k], oldObj[k])
}
else {
newObj[k] = oldObj[k]
}
}
}
deepCopy(o, h)
console.log(o)
</script>
处理this
this指向
普通函数:谁调用指向谁,严格模式下指向undefined
箭头函数:指向上一层作用域
- 如果需要DOM对象的this,则不推荐箭头函数
- 原型对象也不推荐
总结:
- 函数内不存在this沿用上一级
- 不适用于构造函数,原型函数,dom事件函数
- 适用于需要使用上层this的地方

改变this指向
- call()
语法:fun.call(thisArg,1,2,...)
js
<script>
const obj = {
uname: 'pink'
}
function fn(x, y) {
console.log(this)
console.log(x + y);
}
fn.call(obj, 1, 2)
</script>
- apply()
语法:fun.apply(thisArg,[argsArray])
- thisArg:指定this的值
- argsArray:包含的值必须包含在数组里
- 返回值就是函数的返回值
- apply主要与数组有关,可以求数组最大值
js
const obj = {
age: 18
}
function fn(x, y) {
console.log(this)
console.log(x + y)
}
fn.apply(obj, [1, 2])
//求数组最值
const max = Math.max.apply(Math, [1, 2, 3])
const min = Math.min.apply(Math, [1, 2, 3])
console.log(max)
console.log(min)
- bind() 不会调用函数
语法:fun.bind(thisArg,1,2,...)
返回值是一个函数,但函数指向更改过
- 总结:
相同点:都可以改变this指向
不同点:
- call和apply会调用函数,并且改变指向
- call和apply传递参数不同,call传递常数参数,apply传递数组参数
- bind只能改变指向,不能调用函数
使用场景:
call改变指向,调用函数
apply可以实现数组的最值问题
bind只改变指向
性能优化
防抖
- 定义:单位时间内,频繁触发事件,只执行最后一次
- lodash可提供防抖函数 _.debounce(执行函数,延迟时间)
- 手写防抖函数

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>
<style>
.box {
width: 200px;
height: 200px;
margin: 100px auto;
background-color: pink;
font-size: 30px;
text-align: center;
color: gray;
}
</style>
<body>
<div class="box"></div>
<script src="../lodash.min.js"></script>
<script>
//使用lodash库实现防抖效果
const box = document.querySelector('.box')
let i = 1
function move() {
box.innerHTML = i++
}
//box.addEventListener('mousemove', move)
//使用lodash
//box.addEventListener('mousemove', _.debounce(move, 500)) */
//手写防抖函数实现防抖效果 利用setTimeout定时器
function debounce(fn, t) {
let timer
//返回一个匿名函数
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(function () {
fn()
}, t)
}
}
box.addEventListener('mousemove', debounce(move, 500))
</script>
</body>
</html>
节流-throttle
- 定义:单位时间内,频繁触发事件,只执行一次
- lodash提供节流函数 _.throttle(执行函数,时间)
- 手写节流函数

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>
<style>
.box {
width: 200px;
height: 200px;
margin: 100px auto;
background-color: pink;
font-size: 30px;
text-align: center;
color: gray;
}
</style>
<body>
<div class="box"></div>
<script src="../lodash.min.js"></script>
<script>
//使用lodash库实现节流效果
const box = document.querySelector('.box')
let i = 1
function move() {
box.innerHTML = i++
}
//box.addEventListener('mousemove', _.throttle(move, 500))
function throttle(fn, t) {
let timer = null
return function () {
if (!timer) {
timer = setTimeout(function () {
fn()
//用null的形式清空
timer = null
}, t)
}
}
}
box.addEventListener('mousemove', throttle(move, 500))
</script>
</body>
</html>
定时器函数中无法清空定时器,需要使用timer = null实现
总结
防抖会被打断,只执行最后一次
节流不会被打断

案例
模态框
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>
<style>
* {
margin: 0;
padding: 0;
}
button {
margin: 10px 5px;
}
.modal {
width: 300px;
height: 200px;
margin: 200px auto;
border: 1px solid grey;
box-shadow: 0 2px 10px;
}
.header {
margin: 30px;
}
.header i {
margin-left: 165px;
color: darkgrey;
cursor: pointer;
}
.body {
text-align: center;
}
</style>
<body>
<button id="delete">删除</button>
<button id="login">登录</button>
<button id="add">新增</button>
<!-- <div class="modal">
</div> -->
<script>
function Modal(title = '', message = '') {
this.modalBox = document.createElement('div')
this.modalBox.className = 'modal'
this.modalBox.innerHTML = `
<div class="header">${title}<i>x</i></div>
<div class="body">${message}</div>
`
console.log(this.modalBox)
}
//挂在open方法
Modal.prototype.open = function () {
//先判断是否有modal盒子 重点*******!!!!!!!!!
const box = document.querySelector('.modal')
box && box.remove()
//把创建的MOdalBOx创建到页面中
document.body.append(this.modalBox)
this.modalBox.querySelector('i').addEventListener('click', () => {
this.close()
})
}
//close方法
Modal.prototype.close = function () {
this.modalBox.remove()
}
//点击删除按钮
document.querySelector('#delete').addEventListener('click', () => {
//调用modal构造函数
const del = new Modal('温馨提示', '您没有权限删除操作')
//实例对象调用open方法
del.open()
})
//点击登录按钮
document.querySelector('#login').addEventListener('click', () => {
//调用modal构造函数
const login = new Modal('友情提示', '您还没有注册账号')
//实例对象调用open方法
login.open()
})
//点击新增按钮
document.querySelector('#add').addEventListener('click', () => {
//调用modal构造函数
const add = new Modal('提示', '您还没有新增权限')
//实例对象调用open方法
add.open()
})
</script>
</body>
</html>

视频节流
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>
<div class="container">
<div class="header">
<a href="">
<img src="" alt="">
</a>
</div>
<div class="video">
<video src="../766b28f4e90a8b86db4854f2fb648791.mp4" controls>
</video>
</div>
<div class="elevator">
<a href="javascript:;" data-ref="video">视频介绍</a>
<a href="javascript:;" data-ref="intro">课程简介</a>
<a href="javascript:;" data-ref="outline">评论列表</a>
</div>
</div>
<script src="../lodash.min.js"></script>
<script>
const video = document.querySelector('video')
video.ontimeupdate = _.throttle(() => {
localStorage.setItem('currentTime', video.currentTime)
}, 1000)
video.onloadeddata = () => {
video.currentTime = localStorage.getItem('currentTime') || 0
}
</script>
</body>
</html>
- ontimeupdate 事件在视频/音频当前的播放位置而安生改变时触发
- onloadeddate 事件在当前帧的数据加载完成且还没有足够数据播放的下一帧触发