文章目录
JS进阶
一、作用域(scope)
- 局部作用域
- 函数作用域 :在函数内部声明的变量只能刚在函数内部被访问,外部无法直接访问
- 块作用域 在{ }包裹的代码称为代码块,代码块内部声明的变量外部有可能无法被访问
- 全局作用域
script标签 或者 .js文件里面的,都是全局声明的
- 作用域链
本质是一个底层的变量查找机制。
在函数被执行时,会优先查找当前函数作用域中查找变量,如果当前作用域查找不到,会逐级查找父级作用域直到全局作用域
像上面所说的逐级查找,就形成了一个作用域链
二、垃圾回收机制
-
内存的生命周期
- 内存分配 :声明变量,函数,对象的时候,系统会为他们自动分配内存
- 内存使用:即读写内存,也就是使用函数,变量等等
- 内存回收 :使用完毕,由垃圾回收器自动回收不再使用的内存
全局变量一般不会回收,除非关闭页面,一般情况局部变量的值,不用了,会被自动回收掉,
内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏
-
垃圾回收机制–算法说明
堆栈空间分配区别:
- 栈:由操作系统自动分配释放函数的参数值,局部变量等,基本数据类型放到栈里面
- 堆:一般有程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到栈里面
回收算法:
-
引用计数法
- 跟踪记录被引用的次数
- 如果引用了一次就累加++
- 如果引用次数为0,则释放内存
有个致命的问题就是:嵌套引用(循环引用),就是2个对象相互引用,就会导致内存泄漏
-
标记清除法
- 标记清除法将 不再使用的对象,定义为 无法达到的对象
- 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是可以从根部到达的对象,都是还需要使用的
- 无法从根部出发触及到的对象被标记为不再使用,就回收
三、闭包
简单理解:闭包 = 内层函数 + 外层函数的变量
// 外层函数 outer
function outer(){
const a = 1 // 外层函数的变量
//内层函数 f
function f(){
console.log(a)
}
f()
}
outer()
const fun = outer()
fun()// 外层函数使用内部函数的变量
闭包会造成内存泄漏
四、函数进阶
-
函数提升
函数提升和变量提升比较类似,指的是函数在声明之前即可被调用
<script> fn() function fn(){ console.log('函数提升') } //但是下面的函数表达式就不可以先调用再声明了 fun() var fan = function () { console.log('函数表达式') } // 函数表达式 必须先声明和赋值,后调用 否则会报错 </script>
-
函数参数
-
动态参数
例如求和函数,不管用户输入多少个实参,都需要把和求出来
arguments:
function getSum(){ // arguments 动态参数,只存在函数里面 是个伪数组 let sum = 0 for(let i = 0; i <arguments.length;i++){ sum+= arguments[i] } return sum } getSum(2,3,4)
-
剩余参数
function getSum(...arr){ // 是个真数组 let sum = 0 for(let i = 0; i <arr.length;i++){ sum+= arr[i] } return sum } getSum(2,3,4)
开发中推荐使用剩余参数 ‘…’
-
↓
五、箭头函数
箭头函数
const fn = function (){
console.log(123)
}
// 箭头函数
const fn = () =>{
console.log(321)
}
fn()
//如果只有一个形参,可以省略括号
const fn = x =>{
console.log(x)
}
fn(1)
// 只有一行代码的时候,可以省略大括号
const fn = x => console.log(x)
fn(1)
// 箭头函数可以直接返回一个对象
const fn = (uname) => ({uname:uname})
fn('ljh')
- 箭头函数属于表达式函数,因此不存在函数提升
- 箭头函数只有一个参数时可以省略圆括号()
- 箭头函数函数体只有一行代码时可以省略花括号{},并且自动作为返回值被返回
- 加括号的函数体返回的是对象字面量表达式
- 普通函数有arguments动态参数,箭头函数没有动态参数,但是有剩余参数 …arr
- 箭头函数的 this
- 箭头函数本身不会有this,它会从自己的作用域链的上一层沿用this
const obj = {
name:'ljh',
talk: () =>{
console.log(this) // 指的是window
}
}
const obj = {
name:'ljh',
talk: function () {
console.log(this) // 指的是obj
const fn = () =>{
console.log(this) //指的是obj
}
fn()
}
}
// 总结一下,普通函数(function)的this,指的是谁调用指向谁,箭头函数没有this,会从自己的作用域链的上一层沿用this,也就是说,上述代码中,第二个obj里面有一个普通函数,它的this就是obj,所以箭头函数向上一层沿用的就是普通函数的this,也就是obj
六、解构赋值
-
数组解构
const arr = [100,123,321] // 解构赋值 const [a,b,c] = arr console.log(a) // 100
-
对象解构
const obj = { name:'ljh', age:24 } // 解构 const {name,age} = obj // 有一个要求,声明的变量名字,必须和对象中属性名相同 console.log(name)// ljh const {name:username,age} = obj // 这样就可以进行修改名字 console.log(username) //ljh
七、深入对象
-
利用字面量
const o = { ...... }
-
利用 new Object
const obj = new Object() obj.name = 'ljh' // 给对象里面添加 // 第二种 const obj = new Object({name:'ljh'})
-
利用构造函数
八、构造函数
是一种特殊的函数,主要用来初始化对象
//创建一个构造函数
function Pig(uname,age,sex){
this.uname = uname
this.age = age
this.sex = sex
}
new Pig('佩奇',6,'女')
九、内置构造函数
Object、Array、String、Number
-
获得所有属性名 Object.keys(obj)
-
获得所有属性值 Object.values(obj)
-
拷贝对象 Object.assign(newObj,obj)
-
数组方法reduce
返回累计处理的结果,经常用于求和
arr.reduce((上一次值,当前值) =>{},初始值)
九、面向对象
原型对象 prototype
- 构造函数通过原型分配的函数是所有对象所共享的
- JS,规定每一个构造函数都有一个prototype属性,指向另一个对象,所以我们也称为原型对象
- 这个对象可以挂在函数,对象实例化不会多次创建原型上的函数,节约内存
- 我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例都可以共享这些方法
- 构造函数和原型对象中的this都指向实例化的对象
function Star(uname,age){
this.uname = uname
this.age = age
//this.sing = function(){
// console.log('唱歌')
//}
}
const ldh = new Star('刘德华',55)
// 把sing方法 挂在到原型对象上 ↓↓↓
Star.prototype.sing = function(){
console.log('唱歌')
}
constructor(构造函数)
function Star(uname){
this.uname = uname
}
Star.prototype = {
// 手动利用constructor 指向Star 构造函数
constructor:Star,
sing:function(){console.log('唱歌')},
dance:function(){console.log('跳舞')}
}
对象原型
对象都会有一个属性’ _ _ proto _ _’
总结
-
prototype是什么?哪里来的
原型(原型对象)
构造函数都自动有原型
-
constructor属性在哪里?作用干啥的
prototyoe原型和对象原型 _ _proto _ _ 里面都有
都指向创建实例对象/原型的构造函数
-
_ _proto _ _属性在哪里?指向谁?
在实例对象里面
指向原型prototype
原型继承
继承的时候放在原型上prototype
const Person = {
eays:2,
head:1
}
function Woman(){
}
// Woman 通过原型来继承Person
Woman.prototype = Person
// 智回原来的构造函数
Woman.prototype.construcor = Woman
// 给女人添加 一个生孩子的方法
Woman.prototype.baby = function (){
console.log('宝贝')
}
//const Person = {
// eays:2,
//head:1
//}
function Person (){
eays:2,
head:1
}
function Woman(){
}
function Man(){
}
// Woman 通过原型来继承Person
Woman.prototype = new Person()
// 智回原来的构造函数
Woman.prototype.construcor = Woman
// 给女人添加 一个生孩子的方法
Woman.prototype.baby = function (){
console.log('宝贝')
// 这里就会有一个问题,导致第二个构造函数也会出现女人生孩子的方法,所有在上面再声明一个构造函数 Person
}
原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构称为原型链
十、深浅拷贝
- 浅拷贝
拷贝的是地址
常用方法:
- 拷贝对象 Object.assgin() 或者 展开运算符 {…obj}拷贝对象
- 拷贝数组:Array.prototype.concat()
- 深拷贝
首先浅拷贝和深拷贝只针对引用类型
深拷贝:拷贝的是对象,不是地址
常见方法:
- 通过递归实现深拷贝
- lodash 、cloneDeep
- 通过JSON.stringify()实现
十一、异常处理
throw:new Error(‘没有参数传递过来’) 会中断
try/catch 拦截错误,不会中断
try{
...
}catch(err){
...
}
//不管你成功与否都会finally
finally{
...
}
debugger:断点
十二、处理this
call()
-
语法 : fun.call(this.Arg,arg1,arg2,…)
this.Arg :在fun函数运行的时候指定的this值
arg1、arg2 :传递的其他参数
const obj = {
name:"ljh"
}
function fn(x,y){
console.log(this) // 指向window
console.log(x + y)
}
fn.call(obj,1,2)
apply()
-
语法 : fun.apply(thisArg,[argsArray])
this.Arg :在fun函数运行的时候指定的this值
argsArray: 传递的值,必须包含在数组里面
const obj = { name:"ljh" } function fn(x,y){ console.log(this) console.log(x + y) } fn.apply(obj,[1,2])
bind()
-
语法 : fun.bind(this.Arg,arg1,arg2,…)
不会调用函数
this.Arg :在fun函数运行的时候指定的this值
arg1、arg2 :传递的其他参数
const obj = { name:"ljh" } function fn(x,y){ console.log(this) // 指向window console.log(x + y) } const fun = fn.bind(obj,1,2) fun()
十三、防抖(debounce)
单位时间内,频繁触发,只执行最后一次
实现方式:
-
ladash提供的防抖来处理
-
手写一个防抖函数
核心思路:
防抖的核心就是用定时器,setTimeout来实现
- 声明一个定时器变量
- 当事件触发的时候先判断是否有定时器了,如果有就先清楚以前的定时器
- 如果没有就开启定时器,记得存到变量里面
- 在定时器中调用要执行的函数
function debounce(fn,t){
let timer
return function(){
if(timer) clearTimeout(timer)
timer = serTimeout(function(){
fn()
},t)
}
}
box.addEvenListener('mousemove',debounce(mouseMove,500))
十四、节流-throttle
在单位时间内,频繁触发,只执行一次
实现方式:
- ladash提供的节流函数来处理
- 手写一个防抖函数
function throttle(fn,t){
let timer = null
return function(){
if(!timer)
{
timer = serTimeout(function(){
fn()
timer = null
},t)
}
}
}
box.addEvenListener('mousemove',throttle(mouseMove,500))
十五、防抖和节流总结
性能优化 | 说明 | 使用场景 |
---|---|---|
防抖 | 单位时间内,频繁触发事件,只执行最后一次 | 搜索框输入,手机号,邮箱验证输入检测 |
节流 | 单位时间内,频繁触发事件,只执行一次 | 高频事件:鼠标移动、页面尺寸缩放resize、滚动条滚动scroll |