第一天(作用域&函数进阶&解构赋值)
作用域规定了变量能够被访问的"范围",离开了这个"范围"变量便不能被访问。
作用域
局部作用域------>1.函数作用域 2.块作用域
全局作用域
1.1 局部作用域
1.函数作用域:
2.块作用域
1.2 全局作用域

1.3 作用域链
作用域链本质上是底层的变量查找机制。
在函数执行时,会优先查找当前函数作用域中的变量
如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
总结嵌套关系的作用域串联起来形成了作用域链
相同作用域链中按着从小到大的规则查找变量
子作用域能访问父作用域,父级作用域无法访问子作用域。
1.4 垃圾回收机制(Garbage Collection)GC
js中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
引用计数缺点
核心思路:
从根部扫描对象,能查找到的就是使用的,查找不到的就要回收。
1.5 闭包(closure)
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域。
简单理解:闭包=内层函数+外层函数的变量
简单例子:
javascript
function outer(){
const a=1;
function f(){
console.log(a);
}
f()
}
outer()
闭包作用:封闭数据,提供操作,外部也可以访问函数内部的变量
常见的闭包的形式 【外部可以访问使用 函数内部的变量】
javascript
function outer(){
const a=1;
function f(){
console.log(a);
}
return f
}
const fun = outer();
fun(); // 外部使用函数内的变量
闭包应用:实现数据的私有
javascript
// 统计函数被调用次数
let i=0;
function fun (){
i++;
console.log(`函数被调用了${i}次`);
}
javascript
// i是全局变量,有被修改的风险
function fn(){
let i=0;
function fun(){
i++;
console.log(`函数被调用了${i}次`);
}
return fun
}
const fun=fn();

1.6 变量提升
javascript
<script>
// 1.把所有var声明的变量提升到 当前作用域的最前面
// 2.只提升声明,不提升赋值
var num
console.log(num+"件")
num=10;
// 等价于下面
console.log(num+"件")
var num=10;
</script>
2. 函数进阶
2.1函数提升
javascript
<script>
// 1.把所有函数声明提升到 当前作用域的最前面
// 2.只提升函数声明,不提升函数调用
fn();
function fn(){
console.log("函数提升")
}
// 函数表达式 必须先声明和赋值 后调用,否则报错
var fun =function(){
console.log("函数表达式")
}
</script>

2.2 函数参数

javascript
function getSum(){
// arguments 动态参数 只存在于 函数里面
// 是个伪数组
console.log(arguments)
let sum=0;
for(let i=0;i<arguments.length;i++){
sum+=arguments[i];
}
return sum;
}
方法二:剩余参数(将一个不定数量的参数表示为一个数组)
javascript
function getSum(...arr){
// ...是语法符号,置于最末函数形参之前,用于获取多余的参数
// 借助 ...获取的剩余实参,是个真数组
console.log(arr)
let sum=0;
for(let i=0;i<arr.length;i++){
sum+=arr[i];
}
return sum;
}
开发中提倡使用剩余参数。
展开运算符
javascript
function getSum(...arr){
// ...是语法符号,置于最末函数形参之前,用于获取多余的参数
// 借助 ...获取的剩余实参,是个真数组
console.log(arr)
let sum=0;
for(let i=0;i<arr.length;i++){
sum+=arr[i];
}
return sum;
}
const arr1=[1,2,3]
// 展开运算符,可以展开数组
console.log(...arr1) // 1,2,3
// 应用一:求数组最大值
console.log(Math.max(...arr1)) // 输出3
console.log(Math.min(...arr1)) // 1
// 应用二:合并数组
const arr2=[4,5,6]
console.log([...arr1,...arr2])
展开运算符 or 剩余参数
剩余参数:函数参数使用,得到真数组
展开运算符:数组中使用,数组展开
2.3 箭头函数(重要)
目的 :引入箭头函数的目的是,更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁。
使用场景 :箭头函数更适用那些本来需要匿名函数的地方
基本语法 :
javascript
<script>
const fn1 = ()=>{
console.log("123")
}
// 1.有参数
const fn2 = (x)=>{
console.log(x)
}
fn2('111')
// 2.只有一个形参,可以省略小括号
const fn = x=>{
console.log(x)
}
// 3.只有一行代码的时候,可以省略大括号
const fn = x => console.log(x)
// 4.只有一行代码的时候,可以省略return
const fn = x => x + x
// 等价于 x=>{ return x + x }
// 5.箭头函数可以直接返回一个对象,小括号包起来
const fn = (uName)=>({name;uName})
fn("marry")
</script>
总结:
箭头函数参数
1.普通函数有arguments动态参数
2.箭头函数没有arguments动态参数,但是有剩余参数 ...args
箭头函数 this
传统函数this
javascript
<script>
// 之前函数的this
console.log(this) // -> window
function fn(){
console.log(this) // -> window
}
window.fn(); // 所以执行window
// 对象方法里面的this
const obj={
name:"tom",
sayHi:function(){
console.log(this) // -> obj
}
}
obj.sayHi()
</script>
箭头函数的this
javascript
// 箭头函数的this,是上一层作用域的this指向
const fn = ()=>{
console.log(this); // ->window
}
fn()
// 对象方法箭头函数 this
const obj={
uname:"tom",
sayHi:()=>{
console.log(this) // ->window obj的上一级是window
}
}
obj.sayHi()
const obj={
uname:"tom",
sayHi:function(){
console.log(this);
let i=10
const count = ()=>{
console.log(this); // ->obj 相当于在箭头函数上一级打印this
}
count()
}
}
obj.sayHi()

3.解构赋值
3.1数组解构
数组解构是
将数组的单元值 快速批量赋值 给一系列变量 的简洁语法
。
基本语法:
1.赋值运算符=左侧的[] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
2.变量的顺序对应数组单元值的位置依次进行赋值操作
javascript
const arr=[100,60,80]
// 数组解构赋值
const [max,min,avg]=arr
// const max = arr[0]
// const min = arr[1]
// const avg = arr[2]
典型应用:交换两个变量
javascript
// 交换两个变量
const a=1
const b=2; // 必须加分号
[a,b]=[b,a]
加分号的情况
javascript
// 1.立即执行函数
(function(){})();
// 2.使用数组的时候
// 有数组的时候会理解为赋值操作,数组前加分号,代表分号前代码已经结束
// const arr=[1,2,3]
const str='2';
[1,2,3].map(function(item){
console.log(item)
})
// 3.模板字符串开头时
一些细节:
javascript
// 1.变量多,单元值少,多的变量是underfined
// 2.变量少,单元值多,多的单元值不接
// 3.使用剩余参数解决变量少,单元值多的情况
const [a,b,...c]=[1,2,3,4]
console.log(c) // [3,4] 真数组
// 4.防止传递,添加默认值
const [a=0,b=0]=[1]
// 5.按需导入赋值,忽略了某个元素值
const [a,b, ,d]=[1,2,3,4]
// 6.支持多维数组
const [a,b,c]=[1,2,[3,4]]
console.log(c) .. [3,4]
3.2 对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
基本语法:
1.赋值运算符=左侧的 { } 用于批量声明变量,右侧数对象的属性值将被赋值给左侧的变量
2.对象属性的值将被赋值给与属性名相同的变量
3.注意解构的变量名不要和外面的变量名冲突
4.对象中找不到与变量名一致的属性时,变量值为undefined
javascript
const obj={
name:"tom",
age:18
}
const {name,age} = obj
// 等价于 const name = obj.name
一些细节:
javascript
const obj={
name:"tom",
age:18
}
// 1.对象解构的变量名,可以重新改名 旧变量名:新变量名
// 冒号表示 什么值:赋值给谁
const {name:userName,age} = obj
// 2.数组对象解构
const arr = [{
name:"tom",
age:18
}]
const [{name:userName,age}] = obj
// 3.多级对象解构
const obj = {
name:"tom",
family:{
mother:"mom",
father:"father"
},
age:18
}
const {name,family:{mother,father},age}=obj
// 4.数组多级解构
const obj = [{
name:"tom",
family:{
mother:"mom",
father:"father"
},
age:18
}]
const [{name,family:{mother,father},age}]=obj
foreach:不返回数组,就是遍历,加强版for循环,适合遍历数组对象
(map返回一个新数组)
javascript
const arr=[1,2,3]
arr.foreach(function(item,index){
console.log(item)
})

filter方法:判断大于小于,没有加减
map方法:有加减,大于小于是返回true或false

第二天
1.深入对象
1.1创建对象的三种方式
javascript
// 1.对象字面量方式
const obj = {
name:"tom"
}
// 2. new Object()方式
const obj = new Object()
obj.name="tom"
const obj = new Object({
name:"tom"
})
// 3.构造函数方式
1.2 构造函数

约定
1.函数命名首字母大写
2.使用new操作符来执行
javascript
// 构造函数创建对象
function Pig(uname,age){
this.uname=uname;
this.age=age
}
const p = new Pig("pei",6)
console.log(p)

实例化执行过程 (new构造函数)
1.创建一个空对象
2.构造函数中的this指向空对象
3.执行构造函数代码,修改this,添加新的属性
4.返回新对象
1.3 实例成员&&静态成员
实例成员
静态成员
2. 内置构造函数&数据常用函数
2.1基本数据类型

基本数据类型会有属性和方法:
javascript
const str = "tom"
console.log(str.length)
// 等价于 const str = new String("tom")
// js底层完成,把简单数据类型包装成了引用数据类型
const str = new String("tom")
2.2 引用类型 包装类型
引用类型
Object,Array,RegExp,Date等
包装类型
String,Number,Boolean等
2.2.1 Object
javascript
// 想要获得对象里的属性和值怎么做?
const obj = {name:"Tom",age:7}
for(let k in obj){
console.log(k)
console.log(obj[k])s
}
1.Object.keys()
2.Object.values()
3.Object.assign()
2.2.2 Array

核心方法

reduce方法
reduce方法例子
javascript
const arr=[1,3,5]
// 1.没有初始值
arr.reduce(function(prev, current){
return prev+current
})
// 2.有初始值
arr.reduce(function(prev, current){
return prev+current
},10)
reduce执行过程
薪资例子:
javascript
const arr=[
{
id:1,
salary:100000
},
{
id:1,
salary:2100000
},
]
const total=arr.reduce((prev, current)=>{
return prev+current.salary
},0)
其他方法
从多个对象里查找符合我的条件的对象------ find方法
每个是否都符合条件,符合返回true---------- every方法
splice方法
javascript
// 删除元素
const arr = [1, 2, 3, 4, 5];
const removed = arr.splice(1, 2); // 从索引1开始,删除2个元素
console.log(arr); // [1, 4, 5]
console.log(removed); // [2, 3]
// 插入元素(不删除)
const arr = [1, 2, 3];
arr.splice(1, 0, 'a', 'b'); // 从索引1开始,删除0个,插入 'a', 'b'
console.log(arr); // [1, 'a', 'b', 2, 3]
// 替换元素(删除 + 插入)
const arr = ['x', 'y', 'z'];
arr.splice(1, 1, 'A', 'B'); // 从索引1开始,删除1个,插入 'A', 'B'
console.log(arr); // ['x', 'A', 'B', 'z']
// 从后往前删除(负数索引)
const arr = [10, 20, 30, 40];
arr.splice(-2, 1); // 从倒数第2个开始,删除1个
console.log(arr); // [10, 20, 40]
数组常见方法--伪数组转为真数组
静态方法Array.from()
html
<ul>
<li>1</li>
<li>1</li>
<li>1</li>
</ul>
<script>
const lis=document.querySelectorAll('ul li')
// lis.pop() // 报错
const lili=Array.from(lis)
lili.pop() // 转为真数组
</script>
2.2.3 String

常见实例方法:
举例:
javascript
// 1.split--把字符串转为数组。 和join相反
const str = "Tom,Jerry"
const arr=str.split(",")
console.log(arr) // ["Tom", "Jerry"]
// 2.字符串截取。substring(开始的索引号【,结束的索引号】)
const str = "Tom,Jerry"
const arr=str.substring(1,3)
console.log(arr) // ["om"]
// 3.startWith. 判断是不是以某个字符开头
const str = "Tom,Jerry"
const arr=str.startWith("T")
console.log(arr) // true
// 3.includes. 判断某个字符是不是包含在一个字符中
const str = "Tom,Jerry"
const arr=str.includes("Tom")
console.log(arr) // true
2.2.4 Number

小数的计算精度问题:
javascript
// 数字转为字符串
const num = 10
console.log(String(num)) // 1.String() 方法
console.log(num.toString()) // 2.toString() 方法
总结:(标红方法重要!)
Array方法名 | 说明 |
---|---|
forEach() |
遍历数组的每一项,无返回值 |
filter() |
返回满足条件的新数组 |
map() |
返回每项经过函数处理后的新数组 |
reduce() |
累加器,将数组归约为一个值 |
find() |
返回第一个满足条件的元素 |
every() |
是否所有元素都满足条件(返回布尔值) |
String方法名 | 说明 |
---|---|
split() |
将字符串拆分成数组 |
startsWith() |
判断字符串是否以指定内容开头 |
includes() |
判断字符串是否包含指定内容 |
substring() |
截取字符串(从索引开始到索引前) |
第三天
1.编程思想
1.1 面向过程

1.2 面向对象

面向对象特性:
1.3 对比

2 构造函数
封装表达为构造函数,有一些公共属性和方法

构造函数存在问题:浪费内存
3. 原型(解决浪费内存)
3.1 原型
javascript
// 构造函数,包含公共属性和方法,封装到Star构造函数里了
// 公共属性写到构造函数
function Star(){
this.name=name,
// this.sing=function(){
// console.log('唱歌')
// }
}
// 共享属性和方法写到原型对象
Star.prototype.sing=function(){
console.log('唱歌')
}
// 原型对象
console.dir(Star.prototype) // dir:打印对象
总结:
数组扩展方法------求最大值 求和
javascript
// 自定义方法写到数组.prototype身上
// 1. 求最大值
Array.prototype.max= function(){
return Math.max(...this)
}
const arr=[1,2,3]
console.log(arr.max()) // 3
// 2.求和
Array.prototype.sum= function(){
return this.reduce((prev, item)=> prev+item,0)
}
arr.sum()
3.2 constructor属性
作用:指向该原型对象的构造函数
javascript
function Star(){
}
const ldh = new Star()
// 原型对象的constructor属性,指向构造函数
console.log(Star.prototype.constructor=== Star) // true
constructor使用场景:
3.3 对象原型
问题?

对象原型:(只读的,只能获取不能赋值)
javascript
function Star(){ }
const ldh = Star()
console.log(ldh) // [[Prototypy]]:Object
// 对象原型里面有constructor,指向构造函数Star
console.log(ldh.__proto__.constructor === Star) //
// 对象原型 __proto__指向原型对象
console.log(ldh.__proto__ === Star.prototype) // true

总结:

原型继承
javascript
// 继续抽取,公共的部分放到原型
const Person={
eays:2
head:1
}
// 女人构造函数,继承Person
function Woman(){
}
// 原型继承
Woman.prototype=Person
Woman.prototype.constructor=Woman
const red = new Woman()
console.log(red) // Woman{}
console.log(Woman.prototype) // {eays:2,head:1,constructor:f}
// 男人构造函数
function Man(){
this.eays=2
this.head=1
}

解决:
javascript
将
const Person={
eays:2
head:1
}
改为
function Person={
this.eays=2
this.head=1
}
// 说法。父构造函数(父类)。 子构造函数(子类)
3.5 原型链
尽头是null

核心:
- 只要是对象,就有__proto__,指向原型对象
- 只要是原型对象,就有constructor,指向创造该原型对象的构造函数

原型链其实是一种查找规则
先判断页面中是否有modal盒子,如果有先删除,否则继续添加。使用box&&box.remove()
第四天
1.深浅拷贝
1.1 直接赋值

1.2 浅拷贝

浅拷贝拷贝多层对象时:
浅拷贝适用于对象里面只有简单数据类型,没有复杂数据类型

总结:

1.3 深拷贝
深拷贝和浅拷贝都针对的是引用类型
深拷贝:拷贝的是对象,不是地址
常见方法:
- 通过递归实现深拷贝
- lodash/cloneDeep
- JSON.stringify()实现
1.递归方法
1.1 递归定义

javascript
// 递归例子
let i=1
function fn(){
console.log(`这是第${i}次调用`)
if(i<=6){ return }
i++
fn()
}
fn()
javascript
// 利用setTimeout 代替setInterval
// 页面每隔一秒输出当前时间
<body>
<div></div>
<script>
function getTime(){
document.querySelector('div').innerHTML=new Date().toLocaleString()
setTimeout(getTime,1000)
}
getTime()
</script>
</body>
1.2 递归实现深拷贝
javascript
const obj={
uname:"tom",
age:18,
hobby:["乒乓球","足球"],
family:{
mother:"jj"
}
}
const o={}
// 拷贝函数
function deepCopy(newObj,oldObj){
for(let k in oldObj){
// k:属性名 oldObj[k]:属性值
// 处理属性为数组的情况
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, obj)
深拷贝,如果是基本数据类型,直接赋值即可,如果是引用数据类型,一般用到递归,需要根据instanceof来处理,里面用到递归,先Array后Object
1.3 lodash的cloneDeep

1.4 JSON.stringify

2.异常处理
2.1 throw 抛出异常

2.2 try catch捕获错误信息

可以把catch中的return改成throw抛出异常,throw抛出异常自动中断程序
2.3 debugger
直接放在代码里,运行时自动打了断点

3.处理this
3.1 this指向
1.普通函数--- this指向

普通函数:谁调用指向谁
javascript
console.log(this) // window
window.setTimeout(function(){
console.log(this) // window
},1000)
document.querySelector().addEventListener('',function(){
console.log(this) // button对象
})
箭头函数this
注意情况1:

注意情况2:

3.2 改变this
动态指定普通函数中this的指向
- call()
- apply()
- bind()
1.call()------了解

javascript
const obj={
uname:'pink'
}
function fn(x, y){
console.log(this)
console.log(x + y)
}
// 1.调用函数
// 2.改变this指向
fn.call(obj, 1, 2) // this 指向 obj
2.apply()------理解

javascript
const obj={
uname:'pink'
}
function fn(x, y){
console.log(this)
console.log(x + y)
}
// 1.调用函数
// 2.改变this指向
fn.apply(obj, [1, 2]) // this 指向 obj
// 3.返回值,本身就是在调用函数,所以返回值就是函数的返回值
// 使用场景:求数组最大值
// const max=Math.max(1,2,3)
// console.log(max)
const arr=[1,2,3]
const max=Math.max(...arr)
// 使用场景:求数组最大值
const max =Math.max.apply(Math,arr)
console.log(max)
3.bind()------重点
javascript
const obj = {
age:18
}
function fn(){
console.log(this)
}
// 1.bind不会调用函数
// 2.能改变this指向
// 3.返回值是个函数,但是这个函数里面的this是更改过的
const fun = fn.bind(obj)
fun()
// 需求:有一个按钮,点击里面就禁用,2秒钟后开启
const btn=document.querySelector('button')
btn.addEventListener('click',function(){
// 禁用按钮
this.disabled=true
setTimeout(function(){
// 通过bind将this由原来的window改为btn
this.disabled=false
}.bind(this),2000)
})
call apply bind的区别

4.防抖与节流
4.1 防抖(debounce)

利用防抖实现性能优化
javascript
// 需求:鼠标在盒子上移动,里面的数字就会变化,+1操作
const box = document.querySelector('.box')
let i= 1
function mouseMove(){
box.innerHTML=i++
// 如果里面有大量消耗性能的代码,比如dom操作,比如数据处理,可能会造成卡顿
}
// 添加事件
box.addEventListener('mousemove',mouseMove)
方法
1.lodash库的防抖函数

2.自己手写一个防抖函数
核心思路:

javascript
const box = document.querySelector('.box')
let i= 1
function mouseMove(){
box.innerHTML=i++
// 如果里面有大量消耗性能的代码,比如dom操作,比如数据处理,可能会造成卡顿
}
// 核心是用setTimeout定时器实现
function debounce(fn,t){
let timer
// return 返回一个匿名函数
return function(){
if(timer) clearTimeout(timer)
timer=setTimeout(function(){
fn() // 加小括号调用fn函数
},t)
}
}
box.addEventListener('mousemove',debounce(mouseMove,500))
4.2 节流(throttle)

javascript
// 利用节流实现性能优化
// 需求:鼠标在盒子上移动,里面的数字就会变化,+1操作
const box = document.querySelector('.box')
let i= 1
function mouseMove(){
box.innerHTML=i++
// 如果里面有大量消耗性能的代码,比如dom操作,比如数据处理,可能会造成卡顿
}
// 添加事件
// box.addEventListener('mousemove',mouseMove)
// 利用lodash库实现节流------500毫秒之后采取+1
// 语法:_.throttle(fun,时间)
box.addEventListener('mousemove',_.throttle(mouseMove,500))

javascript
function throttle(fn, t){
// 声明一个定时器变量
let timer = null
return function(){
if(!timer){
timer = setTimeout(function(){
fn()
// 清空定时器,确保执行完后再生成一个新的定时器
timer = null
},500)
}
}
}
setTimeout中不能清除定时器

总结:
防抖和节流总结:
4.3 节流综合案例:

思路:

实现:
