第一天:作用域&解构&箭头函数
1.作用域
1.1 局部作用域
-
函数作用域
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问
-
块作用域
使用{}包裹的代码成为代码块,代码块内部声明的变量外部将[
有可能
]无法被访问(var声明的会被访问,let和const声明的不会被访问)
1.2 全局作用域
在函数体和代码块以外的地方声明的变量成为全局变量,可以被所有函数和代码块共享
1.3 作用域链
作用域链:本质上是底层的变量查找机制
1.在函数被执行时,会优先查找当前函数作用域中查找变量
2.如果查找不到会依次逐级查找父级作用域直到全局作用域
1.4 垃圾回收机制
1.
内存分配
:当我们声明变量、函数、对象时,系统会自动为他们分配内存2.
内存使用
:即读写内存,也就是使用变量、函数等3.
内存回收
:使用完毕,由垃圾回收器自动回收不再使用的内存
说明:全局变量一般不会回收(关闭页面回收)
内存泄漏
:指程序中分配的内存由某种原因程序未释放或无法释放
方法:1.引用计数法 2.标记清除法
1.5 闭包
指一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
闭包 = 内层函数 + 外层函数的变量 (可能造成内存泄漏)
-
闭包作用:封闭数据,提供操作,外部也可用于访问函数内部变量,实现数据的私有
function outer(){ let i = 1 function fn(){ console.log(i) } return fn //返回一个函数用于接收 } const fun = outer() fun() //1
1.6 变量提升
1.在代码执行前会先将var声明的变量提到当前函数作用域的最前面
2.只提升声明变量,不提升变量的赋值
2.函数进阶
2.1 函数提升
1.会把所有函数声明提升到当前作用域的最前面
2.只提升函数声明,比提升函数调用
3.函数表达式必须先声明后调用
2.2 函数参数
-
2.2.1 动态参数
-
arguments
:是函数内置的伪数组变量,它包含了调用函数时传入的所有实参//求和函数,计算所有参数的和 function sum(){ let s = 0 for(let i = 0;i<arguments.length;i++){ s + = arguments[i] } console.log(s) } //调用求和函数 sum(5,10) //两个参数 sum(1,2,4) //三个参数
-
-
2.2.2 剩余参数
允许我们将一个不定数量的参数表示为一个数组
function getSum(...other) { // other 得到[1,2,3] console.log(other) } getSum(1,2,3)
-
1.
…
是语法符号,置于最末函数形参之前,用于获取多余的实参 -
2.借助
…
获取的剩余实参,是个真数组function config(baseURL,...other) { console.log(baseURL) //baseURL 得到 'http://baidu.com' console.log(other) // other 得到['get','json'] } config('http://baidu.com','get','json')
-
-
2.2.3
展开运算符
:将一个数组进行展开const arr = [1,5,3,8,2] console.log(...arr) // 1,5,3,8,2
2.3 箭头函数
目的:更简短的函数写法并且不绑定this,箭头函数语法更简洁
-
2.3.1 语法:
-
1.基本语法:
const fn = () => { console.log('俺是箭头函数') } fn()
-
2.只有一个参数时可以省略小括号
const fn = x => { return x + x } console.log(fn(1)) // 2
-
3.如果函数体只有一行代码,可以写到一行上,并且无需写return直接返回值
const fn =(x,y) => x + y console.log(fn(1,2)) // 3
-
4.加括号的函数体返回对象字面量表达式
const fn = uname => ({uname:uname}) //第一个uname:属性名,第二个uname:属性值 console.log(fn('刘德华'))
-
-
2.3.2 箭头函数参数
1.无arguments动态参数
2.有剩余参数
const getSum(...args) => { let sum = 0 for(let i = 0;i<args.length;i++){ sum + = args[i] } return sum } console.log(getSum(1,2,3)) // 6
-
2.2.3 箭头函数this
箭头函数从自己的作用域链的上一层沿用this,不会自己创建
const user = { name:'小明', sleep:function(){ console.log(this) //指向user const fn = () =>{ console.log(this) //指向this } fn() } } user.sleep()
3.解构赋值
3.1数组解构
本质是将数组的单元值快速批量赋值给一系列变量的简洁语法
语法:
const [a,b,c] = [1,2,3] //等同于下面的代码
//普通数组
const arr = [1,2,3]
//批量声明变量a,b,c 同时将数组单元值依次赋值给变量a,b,c
const [a,b,c] = arr
-----------------------------------------------------------------------
//交换两个变量
let a = 1
let b = 2; //这里必须加分号
[b,a] = [a,b]
console.log(a) //2
console.log(b) //1
3.2对象解构
本质是将对象属性和方法快速批量赋值给一系列变量的简洁语法
注意:1.只会赋值给与属性名相同变量名(没有则返回undefined)
2.定义的变量名不能与前面的代码名相同,如果要相同则必须对变量进行改 名
方法:就变量名:新变量名
const uname = 'red老师' const {uname:username,age} = {uname:'pink老师',age:18}
-
多级对象解构
const pig = { name:'佩奇', family:{ mother:'猪妈妈', father:'猪爸爸', brother:'乔治' }, age:6 } //进行解构 const {name,family:{mother,father,brother}} = pig
第二天:构造函数&数据常用函数
1.深入对象
1.1 创建对象的三种方式
-
1.利用对象字面量创建对象
const obj = { name:'佩奇' }
-
2.利用new Object
const obj = new Object({name:'佩奇'}) console.log(obj) // {name:'佩奇'}
-
3.利用构造函数创建对象
function Pig(name,age,gender){ this.name = name this.age = age this.gender = gender } //创建对象 const Peppa = new Pig('佩奇',6,'女')
1.2 构造函数
是一种特殊的函数,主要用来初始化对象,可以通过构造函数快速创建多个类似的对象
注意:1.它们的命名以
大写字母
开头 2.它们只能由“new”操作符来执行
3.无参数时可以省略()
4.无return,返回值即为创建的对象
function Pig(name,age,gender){
this.name = name
this.age = age
this.gender = gender
}
//创建对象
const Peppa = new Pig('佩奇',6,'女')
1.3 实例成员&静态成员
-
1.实例成员
通过构造函数创建的对象成为实例对象,实例对象中的属性和方法称为
实例成员
-
2.静态成员
构造函数的属性和方法成为
静态成员
说明:1.静态成员只能构造函数来访问
2.静态方法中的this指向构造函数
2.内置构造函数
引用类型
:Object、Array、RegExp(正则)、Data等
包装类型
:String、Number、Boolean等
2.1 Object
-
1.Object.keys:静态方法获取对象中所有的属性(键)
const o = {name:'佩奇',age:6} //获得对象的所有键,并返回一个数组 const arr = Object.keys(o) console.log(arr) // ['name','age']
-
2.Object.values:静态方法获取对象中所有的属性值
const o = {name:'佩奇',age:6} //获得对象的所有键,并返回一个数组 const arr = Object.values(o) console.log(arr) // ['佩奇', 6]
-
3.Object.assign:静态方法常用于对象拷贝
// 拷贝对象 把 o 拷贝给 obj const o = {name:'佩奇',age:6} const obj = {} Object.assign(obj,o) console.log(obj) // {name:'佩奇',age:6}
2.2 Array
-
主要方法
方法 作用 说明 forEach 遍历数组 不返回数组,经常用于查找遍历数组元素 filter 过滤数组 返回新数组,返回的是筛选满足条件的数组元素 map 迭代数组 返回新数组,返回的是处理之后的数组元素 reduce 累计器 返回累计处理的结果,经常用于求和等 -
其他方法
方法 说明 join 数组元素拼接为字符串,并返回字符串 find 查找元素,返回符号测试条件的第一个数组元素值,没有则返回undefined every 检测数组所有元素是否都符和指定条件,都符号返回true,否则返回false some 检测数组所有元素是否满足指定条件,有元素满足返回true,否则返回false concat 合并两给数组,返回生成的新数组 sort 对原数组单元值排序 splice 删除或替换原数组单元 reverse 反转数组 findIndex 查找元素的索引号 Array.from 将伪数组转换成真数组
2.3 String
-
常用实例方法
方法和属性 说明 length 获取字符串的长度 split(‘分隔符’) 用来将字符串拆分成数组 substring(需要截取的第一个字符的索引号,结束的索引号) 用于字符串截取 startWith(检测字符串[,检测位置的索引号]) 检测是否以某字符开头 endWith() 检测是否以某字符结尾 includes(搜索的字符串[,检测位置的索引号]) 判断一个字符串是否包含在另一个字符串中,返回true或false toUpperCase() 将字母转换成大写 toLowerCase() 将字母转换成小写 indexOf() 检测是否包含某字符 replace() 用于替换字符串,支持正则匹配 match() 用于查找字符串,支持正则匹配 String()或toString() 强制转换成字符串
2.4 Number
-
toFixed():设置保留小数位的长度
// 数值类型 const price = 12.345 // 保留两位小数 四舍五入 console.log(price.toFixed(2)) // 12.34
第三天:深入面向对象
1.编程思想
1.1面向过程
指分析出解决问题所需的步骤,然后用函数把这些步骤一步一步实现,使用时在一个个的调用
1.2面向对象(oop)
指把事务分解成为一个个对象,然后由对象之间分工与合作
特性:封装性、继承性、多态性
2.原型
1.构造函数通过原型分配的函数是所有对象所
共享的
2.构造函数中有一个
prototype
属性,指向另一个对象,所以也称为原型对象
3.这个对象可以挂载函数,对象实例化不会多次创建原型上的函数,节约内存
4.构造函数和原型对象中的this都指向实例化的对象
5.把那些不变的方法,直接定义在prototype对象上,可以实现所有对象的实例的共享
2.1 constructor 属性
原型对象中的属性,指向构造函数
Star.prototype = {
// 重新指回创造这个原型对象的构造函数
constructor:Star,
sing:function(){
console.log('唱歌')
},
dance:function(){
console.log('跳舞')
}
}
2.2 对象原型
对象都会有一个属性
__proto__
指向构造函数prototype原型对象注意:1.
__proto__
是js非标准属性 2.
[[prototype]]
和__proto__
意义相同 3.用来表明当前实例对象指向哪个原型对象prototype
4.
__proto__
对象原型里也有一个constructor属性,指向创建该实例对象的构造函数
2.3 原型继承
继承是面向对象编程的另一个特征,可以进一步提升代码的封装程度,JavaScript中大多是借助原型对象实现继承
function Person(){
this.eyes = 2
this.head = 1
}
function Woman(){ }
Woman.prototype = new Person() // 实现原型继承
Woman.prototype.constructor = Woman // 重新使原型对象指向构造函数
2.4 原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,称为原型链
2.5 instanceof运算符
用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上
第四天:高阶技巧
1.深浅拷贝
注意:只针对引用类型
1.1 浅拷贝
拷贝的是地址,修改拷贝的对象的值不会改变原对象的值
const obj = {
uname:'pink',
age:18
}
// 方法一
const o = {...obj}
// 方法二
const o = {}
Object.assign(o,obj)
// 数组就用Array.prototype.concat() 和 [...arr]
1.2 深拷贝
拷贝的的是对象,不是地址
-
1.通过递归实现深拷贝
const obj = { uname:'pink', age:18, habby:['乒乓球','足球'] } const o = { } function deepCopy(newObj,oldObj){ for(let k in oldObj){ // k为属性名,oldObj为遍历的对象,oldObj[] if(oldObj[k] instanceof Array){ newObj[k] = [] // habby:[] deepCopy(newObj,oldObj) }else{ newObj[k] = oldObj{k} } } }
-
2.lodash / cloneDeep
<script src='./lodash.min.js'></script> // 先调用lodash库,才能使用cloneDeep() const obj = { uname:'pink', age:18, habby:['乒乓球','足球'], family:{ boby:'小pink' } } const o = _.cloneDeep(obj) // 实现深拷贝
-
3.通过JSON.stringify()实现
// JSON.stringify(obj) 把对象转换为JSON字符串 // JSON.parse(JSON.stringify(obj)) 把JSON字符串转换为对象 const obj = { uname:'pink', age:18, habby:['乒乓球','足球'], family:{ boby:'小pink' } } const o = JSON.parse(JSON.stringify(obj))
2.异常处理
2.1throw 抛异常
异常处理是指
预估
代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法运行
function fn(x,y){
if(!x || !y){
throw new Error('没有参数传递过来')
}
return x + y
}
fn()
注意: throw会中断程序
2.2try / catch 捕获异常
<p>123</p>
<script>
function fn(){
try{
// 可能发生错误的代码写到try中
const p = document.querSelector('.p') //p标签获取错误
p.style.color = 'red'
}catch(err){
// 拦截错误,提示浏览器提供的错误信息,不中断程序的运行
console.log(err.message) // 不会执行try中的内容,并且抛出错误信息
retuen // 中断程序
}finally{
// 不管程序是否正确,一定会去执行的代码
alert('弹出对话框')
}
console.log(11) // 11 会继续执行,不会中断程序
}
</script>
2.3debugger
const arr = [1,3,5]
const newArr = arr.map((item,index) =>{
debugger // 相当于给程序打断点
console.log(item)
console.log(index)
return item + 10
})
console.log(newArr) // [11,13,15]
3.处理this
3.1 this指向
-
开启严格模式
'use strict' // 严格模式下没有调用者this指向undefined function fn(){ console.log(this) // undefined } fn()
3.2 改变this
-
3.2.1 call(): 使用call()方法调用函数,同时指定被调用函数中this的值
语法: 注意:不需要指向时可以设为null
fun.call(thisArg,arg1,arg2,...) // thisArg: 在fun函数运行时指定的this的值 // arg1,arg2: 传递的其他参数 const obj = { age:18 } function fn(x,y){ console.log(this) // 指向obj console.log(x + y) //3 } fn.call(obj,1,2)
-
3.2.2 apply(): 使用call()方法调用函数,同时指定被调用函数中this的值
语法:
fun.apply(thisArg,[argsArray]) // thisArg: 在fun函数运行时指定的this的值 // argsArray: 传递的值,必须包含在数组里面 const obj = { age:18 } function fn(x,y){ console.log(this) // 指向obj console.log(x + y) //3 } fn.apply(obj,[1,2])
应用场景:求最大值
const arr = [100,44,77] const max = Math.max.apply(null,arr) const min = Math.min.apply(null,arr) console.log(max,min) // 100 44
-
3.2.3 bind(): bind()方法不会调用函数
语法:
fun.apply(thisArg,[argsArray]) // thisArg: 在fun函数运行时指定的this的值 // arg1,arg2: 传递的其他参数 const obj = { age:18 } function fn(x,y){ console.log(this) // 指向obj console.log(x + y) //3 } fn.bind(obj,[1,2])
注意:返回由指定的this值和初始化参数改造的
原函数拷贝(新函数)
4.性能优化
4.1 防抖(debounce)
单位时间内,频繁触发事件,只执行最后一次
-
利用防抖处理-鼠标滑过盒子显示文字(要求鼠标在盒子上移动,鼠标停止500ms后, 数字才加一)
const box = document.querySelector('.box') let i = 1 function mouseMove() { box.innerHTML = i++ } box.addEventListener('mousemove',mouseMove)
-
1.lodash提供的防抖
_.debounce(func,[wait=0],[options=]) // func:要防抖的函数 // wait:需要延迟的毫秒数 // func:选项对象 const box = document.querySelector('.box') let i = 1 function mouseMove() { box.innerHTML = i++ } box.addEventListener('mousemove',_.debounce(mouseMove,500))
-
2.手写一个防抖的函数
const box = document.querySelector('.box') let i = 1 function mouseMove() { box.innerHTML = i++ } function debounce(fn,t){ let timer return function(){ if(timer) clearTimeout(timer) timer = setTimeout(function(){ fn() },t) } } box.addEventListener('mousemove',debounce(mouseMove,500))
4.2 节流(throttle)
单位时间内,频繁触发事件,只执行一次
-
利用节流处理-鼠标滑过盒子显示文字(要求鼠标在盒子上移动,不管移动多少次,每隔500ms才加一)
const box = document.querySelector('.box') let i = 1 function mouseMove() { box.innerHTML = i++ // 如果存在开销较大操作,大数据处理,大量dom操作,可能会卡 } box.addEventListener('mousemove',mouseMove)
-
1.lodash提供的节流函数
_.throttle(func,[wait=0],[options=]) // func:要防抖的函数 // wait:需要延迟的毫秒数 // func:选项对象 const box = document.querySelector('.box') let i = 1 function mouseMove() { box.innerHTML = i++ } box.addEventListener('mousemove',_.throttle(mouseMove,500))
-
2.手写一个节流的函数
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))
4.3 事件
说明:video.currentTime: 可以获取当前视频播放的时间
-
1.ontimeupdate: 事件在视频/音频(audio/video)当前的播放位置发生改变时触发
-
2.onloadeddata: 事件在当前帧的数据加载完成且还没有足够的数据播放视频/音频(audio/video)的下一帧触发
const video = document.querySelector('video') video.ontimeupdate = _.throttle(()=>{ localStorage.setItem('currentTime', video.currentTime) },1000) video.onloadeddata = ()=>{ video.currentTime = localStorage.getItem('currentTime') || 0 }