前端学习笔记 | JS进阶

一、作用域

1、局部作用域

(1)函数作用域

(2)块作用域

let和const会产生块作用域  ,而var不会产生块作用域

2、全局作用域

script标签和js文件的【最外层】变量

3、作用域链

本质:底层的变量查找机制

4、JS垃圾回收机制(GC)

(1)内存的生命周期

  • 内存分配
  • 内存使用
  • 内存回收

(2)算法说明

  • 引用计数法:嵌套循环容易使内存泄漏
  • 标记清除法:从全局出发,找不到就回收

5、闭包

闭包=内存函数+外层函数的变量

作用:外部访问局部变量、局部函数

function outer(){
    let i=1
    return function(){
      console.log(i)
    }
}
const fun = outer()
fun()

应用:实现数据的私有,外部无法直接修改变量

注:容易引起内存泄漏

6、变量提升

 JS的缺陷(使用var的时候):代码执行前会将所有的var声明的变量提升到当前作用域的最前面,只提升变量声明,不提升变量声明——不建议

二、函数进阶

1、函数提升

2、函数参数

(1)动态参数(不推荐)

数组arguments:不管拿多少变量,都能获取到。

方法:不用形参,函数内部直接使用arguments

缺点:伪数组

(2)剩余参数(推荐)

arr获取的是多余的实参,是真数组,可以使用数组方法

function getSum(a,b,...arr)

...arr是展开数组

3、箭头参数

(1)特点

替代匿名函数

属于表达式函数,不存在函数提升

(2)基本语法

//基本写法
const fn = () =>{ }

//只有一个形参的时候可以省略小括号
const fn= x => { }

//只有一个形参的时候可以省略大括号
const fn= x => console.log(x)

//只有一行代码可以省略return
const fn= x => return x+x
const fn= x => x+x

//箭头函数可以直接返回一个对象
const fn= (unname) => ({uname:uname})

(3)箭头函数参数

普通函数有arguments,箭头函数没有

(4)箭头函数this

this指的是对象

箭头函数的this指向上一层作用域的this

三、解构赋值

1、数组解构

将数组单元值快速批量赋值给变量的简洁语法

const arr = [100,60,80]
const [max,min,avg ] =arr
等同于
//const max=arr[0]
//const min=arr[1]
//const avg=arr[2]

典型应用:交换两个变量的值

let a=1
let b=2
;[b,a]=[a,b] //这里必须加分号

补充:必须加;的情况

  • 立即执行函数
(function(){})();
  • 数组的时候:数组开头的
const str='pink';
[1,2,3].map(function(item)){}

2、对象解构

将对象属性和方法快速批量赋值给变量的简洁语法

(1)基本

const[{uname,age}]=obj

(2)多级对象

复合对象需要在前面加一个名字

const pig={
    name:'佩奇'
    family:{
      mother:'猪妈妈'
      father:'猪爸爸'
      sister:'乔治'
    }
    age:6
}

const [{name,family:{mother,father,sister}}]=pig

 四、数组forEach遍历和filter筛选

1、forEach语法

(1)语法

const arr=['red','green','blue']
arr.forEach(function(item,index){
    console.log(item) 
    console.log(index)
})

(2)作用

主要遍历数组——加强版for循环

和map(返回数组)的区别,不返回值

2、filter方法

(1)语法

const arr=[10,20,30]
arr.filter(funtion(item,index){
    return item>=20
})

(2)特点

用于筛选数组

函数返回生成一个新数组

 五、深入对象

1、创建对象三种方式

(1)通过字面量

const o={
    name:'佩奇'
}

(2)利用new Object创建对象

const o=new Object({name:'佩奇'})
console.log(o)

(3)利用构造函数创建对象

2、构造函数

(1)是什么

特殊函数,用于初始化对象

(2)语法

function pig(name){
    this.name=name
}
const peppa=new Pig('佩奇') //实例化

3、实例成员&静态成员

(1)实例成员

构造函数创建的对象叫实例对象,实例对象的属性和方法为实例成员

(2)静态成员

构造函数的属性和方法被称为静态成员

  • 静态成员只能构造函数来访问
  • 静态方法中的this指向构造函数

4、内置构造函数

(1)Object 

  • Object.keys(o) 
  • Object.values(o) 
  • Object.assign(新对象,原对象) 
const o={name:'佩奇',age:6}
Object.keys(o) //返回数组['uname','age']
Object.values(o) //返回数组['佩奇','6']

(2)Array

  • forEach
  • filter
  • map
  • join

  • reduce 返回累计处理结果,经常用于求和
arr.reduce(function(上一次值,当前值){},初始值)

如果需要累加的是对象数组,初始值必须写0

  • find

找出符合的对象

const arr=[
{name:'小米',price:1999},
{name:'华为',price:3999}
]

const mi=arr.find(function(item)){
    return item.name==='华为'
}

//返回{name:'小米',price:1999}
  • every

  • some
  • from

把伪数组转换成真数组,可使用pop、push等

  • ...

(3)String

  • length
  • split('分隔符')  分割字符串
  • substring(需要截取的第一个字符的索引[,结束的索引号])  窃取字符串
  • startwith(检测字符串[,检测位置索引号]) 从索引值搜索是否以目标字符串开头
  • endwith(检测字符串[,检测位置索引号])  从索引值搜索是否以目标字符串结束
  • includes(搜索的字符串[,检测位置索引号])  判断是否包含  
  • ... 

(4)Number

  • toFixed(保留位数) 让数字指定保留的位数,默认整数

 六、深入面向对象

1、编程思想

  • 面向过程:面向函数
  • 面向对象
    • 封装性
    • 继承性
    • 多态性

2、构造函数

优点好用,缺点浪费内存——e.g一个对象相同方法但是不等

this指向实例化对象

3、原型对象prototype

能够利用原型对象实现方法共享——原型对象的函数和构造函数都指向实例化对象(this)

公共属性写在构造函数里

公共方法写在原型对象里

function Star(uname,age){
    this.uname=uname
    this.age=age
}
Star.prototype.sing=function(){}

4、 constructor属性

指向原型对象的构造函数

prototype和__proto__均有

5、对象原型_proto_

对象都有一个属性_proto_指向构造函数的prototype原型对象

  • JS非标准属性
  • [[prototype]]和__proto__意义相同
function Star(){}
const ldh =new Star()
console.log(ldh.__proto__ === Star.prototype) //true

6、原型继承

const person={
    eyes:2,
    head:1
}
function Woman(){
}

Woman.prototype=person
Woman.prototype.constructor=Woman

缺点:继承同一个person,新加方法会有问题

=>用构造函数的写法

7、原型链

基于原型对象的继承使得不同构造函数的原型对象关联在一起

什么是原型链?——一种查找规则:为对象成员查找机制提供一个方向

8、instanceof 

作用:判断对象属不属于原型链上

const ldh=new person()
console.log(ldh instanceof person) //true
console.log(ldh instanceof Object) //true

七、 模态框封装(案例)

1、modalBox 模态框属性

2、(打开模态框)设置open挂载点,页面显示模态框是在open里面

3、(关闭模态框)在open中添加关闭的绑定事件close

注:BUG-会打开多个——先判断,有就移除,没有就添加

box&&box.remove() //逻辑中断,有box则后面不执行,无则执行box.remove()

 八、深浅拷贝

1、深浅拷贝(只针对引用类型)

(1)浅拷贝——简单数据类型拷贝值,引用数据类型拷贝地址

  • 拷贝对象:Object.assign(新对象,旧对象) | 展开对象{...obj}
    • 缺点:复杂对象拷贝会出现问题
  • 拷贝数组 :Array.prototype.concat() | 展开数组[...arr]

(2)深拷贝——拷贝的是对象不是地址

  • 方法1:通过递归(=自己调自己)实现深拷贝
//递归函数
function getTime(){
    document.querySelector('div').innerHTML= new Date().toLocalString()
    setTimeout(getTime,1000)
}
getTime()
//深拷贝的方法——做到新对象不会影像旧对象
//1、运用函数递归的方法
//2、如果是普通的,则直接赋值;遇到数组,需要再次调用递归函数。遇到对象,同样再次调用递归函数。
//3、先Array后对象

function deepCopy(newObj,oldObj){
    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]
        }
    }
}

  • 方法2:利用lodash
//1、引用lodash.min.js
<script src="./lodash.min.js"></script>
//2、用_cloneDeep(拷贝对象)方法进行深拷贝
<script>
    const obj={
        uname:'pink',
        age:18,
        hobby:['乒乓球','足球'],
        family:{
            baby:'小pink'    
        }
}
    const o =_.cloneDeep(obj)
</script>
  • 方法3:利用JSON
//利用JSON.stringify()把JSON转换为字符串
//JSON.parse可以将字符串转换为对象


//因为通过对象转换成字符串再转为对象,和原来的对象已经不一样了,修改新对象不会被影响
const o=JSON.parse(JSON.stringify(obj))

九、 异常处理

1、throw 抛异常

//会自动抛出异常并中断程序
//经常配合Error使用

function counter(x,y){
    if(!x || !y){
        throw new Error("没有参数传递过来")
    }
    return x+y
}
counter()

2、try/catch/finally 捕获异常

//可能发生错误的代码,要写到try
//拦截错误,提示浏览器提供的错误信息,但不中断程序的执行

function fn(){
    try{
    const p=document.querSelector('.p')
    p.style.color='red'
    }
    catch(err){
    console.log(err.message)
    throw new Error("你看看,选择器错误了吧")
    }
    finally
    {
    //finally 不管你程序对不对,一定会执行的代码
    alert("执行")
    }
}

3、debugger

打断点——可以直接跳转到改行代码调试

十、处理this

1、this指向

(1)普通函数的指向:谁调用指向谁

//1、指向window
(1)
console.log(this) //指向window
(2)
function fn (){
    console.log(this)
}  //指向window
(3)
setInterval(function(){
    console.log(this)
},1000) //指向window
(4)
<button>点击</button>
document.querySelector('button').addEventLisener('click',function(){
 console.log(this)
}) //指向window

//2、指向对象
const obj={
    sayHI:function(){
    console.log(this)
}
}
obj.sayHI() //指向obj

(2)箭头函数的指向

与普通函数不同,箭头函数this不受调用方式的影响

箭头函数不存在this,沿用的是上一级的

  • 不适用:构造函数,原型函数,dom事件函数等等
//箭头函数中断this为函数声明函数的this一致
const user={
    name:'小明'
    walk:()=>{
        console.log(this)
    }
}
user.walk() //指向window

!!!注:原型对象最好不 要用箭头函数,否则this不指向实例对象

2、改变this

(1)call() - 了解

调用函数,同事只当被调用函数的this的值

fn.call(thisArg,arg1,arg2,...)

const obj={
    uname:'pink'
}
function fn (x,y){
    console.log(this)//指向window
    console.log(x+y)
}  

fn.call() //单纯调用fn,指向window
fn.call(obj,1,2) //调用fn,改变this,指向obj

(2)apply() -理解

fn.call(thisArg,[argsArray]) 第二个参数必须是数组

const obj={
    uname:'pink'
}
function fn (x,y){
    console.log(this)//指向window
    console.log(x+y)
}  
fn.call(obj,[1,2])//指向obj,打印3

使用场景:求数组的最大值

1、用数组遍历

2、扩展运算符

Math.max(...[arr])

3、用apply方法

Math.max/min.apply(Math/null,数组名)

//传统写法
const max = Math.max(1,2,3) 
console.log(max)

//数组求最大值
const arr=[1,2,3]
const max = Math.max.apply(Math,arr)
const min = Math.min.apply(null,arr)

(3)bind()

call()、apply() 会调用函数,但bind()不会调用函数

会改变函数内部this指向

返回值是一个函数,这个函数的this是更改的obj

const obj={
    age:18    
}

function fn(){
    console.log(this)
}

const fun=fn.bind(obj)
fun() //打印obj对象

应用:有一个按钮,点击立马禁用,2s后开启

<button>按钮</button>
const btn=document.querySelector('.button')
btn.addEventListener('click',function(){
    this.disabled=true
    
    window.setTimeout(function(){
       // this.disabled=false //无法打开按钮
       //btn.disabled=false //可以打开按钮
    
    },2000)

    //或者
    window.setTimeout(function(){
       // this.disabled=false //无法打开按钮
       //btn.disabled=false //可以打开按钮
    
    }.bind(btn),2000)

     //或者
    window.setTimeout(function(){
       // this.disabled=false //无法打开按钮
       //btn.disabled=false //可以打开按钮
    
    }.bind(this),2000) //上一层的this指向btn

      //或者
    window.setTimeout(=>{
       this.disabled=false //可以打开按钮,箭头函数没有this,沿用上一层的this
    },2000) //上一层的this指向btn

})

(4)改变this的三种方法及区别(面试常考)

 十一、性能优化

1、防抖 debounce

(1)特点

  • 单位时间内,频繁触发事件,只执行最后一次
  • 王者回城,只要被打断,就需要重新来

(2)语法:_.debounce(fun,时间) 

(3)使用场景

  • 搜索框搜索输入,只需要用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测

(4)案例

要求:鼠标在盒子上移动,里面的数字就会变化+1

const box=document.querySelector('.box')
let i=1
function mousemove(){
    box.innerHTML=i++
}
box.addEventListener('mousemove',mousemove) //数字变化很快

(改造:不是移动1像素就+1)
方法1、lodash提供的防抖函数 _.debounce(func,[wait=0],[option=])

<script src="./lodash.min.js"></script>
const box=document.querySelector('.box')
let i=1
function mousemove(){
    box.innerHTML=i++
}

box.addEventListener('mousemove',_.debounce(mousemove,500))

方法2、手写一个防抖函数来处理(面试常考)

//核心:利用setTimeout进行实现
//1、声明定时器变量
//2、每次鼠标移动(事件触发)就判断是否有定时器,如果有就先清除定时器
//3、没有就开启定时器,存入到定时器变量里面
//4、定时器里面写函数调用

const box=document.querySelector('.box')
    let i=1

function mousemove(){
    box.innerHTML=i++
}

function(fn,t){
    let timer
    //这里用return是因为要反复调用该函数 
    return function(){
    if(timer) clearTimeout(timer)
    timer=setTimeout(function{
        fn()
        },t)
    }
}

box.addEventListener('mousemove',_.debounce(mousemove,500))

2、节流

(1)特点

  • 单位时间内,频繁触发事件,只执行一次
  • 王者荣耀技能冷却,期间无法继续释放技能

(2)语法:_.throttle(fun,时间)

(3)使用场景

高频事件:鼠标移动mousemove、页面尺寸缩放resize、滚动条滚动scroll等等

(4)案例

要求:鼠标再盒子上移动,不管移动多少次,每隔500ms才+1

方法1:使用lodash库实现节流,500ms之后采取+1

<script src="./lodash.min.js"></script>
const box=document.querySelector('.box')
let i=1
function mousemove(){
    box.innerHTML=i++
}

box.addEventListener('mousemove',_.throttle(mousemove,500))

方法2:手写节流函数

//核心:利用setTimeout进行实现
//1、声明定时器变量
//2、每次鼠标移动(事件触发)就判断是否有定时器,如果有就先清除定时器
//3、没有就开启定时器,存入到定时器变量里面
//4、定时器里面写函数调用

const box=document.querySelector('.box')
    let i=1

function mousemove(){
    box.innerHTML=i++
}

function(fn,t){
    let timer
    //这里用return是因为要反复调用该函数 
    return function(){
    if(!timer) {
        timer = setTimeout(function(){
        fn()
        timer=null //在setTimeout函数中不能用clearTimeout(timer),因为定时器在运行
        },t)
    }
}

box.addEventListener('mousemove',_.debounce(mousemove,500))

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值