JS高级,温习记录,持续更新

JS进阶

一、作用域(scope)
  • 局部作用域
  1. 函数作用域 :在函数内部声明的变量只能刚在函数内部被访问,外部无法直接访问
  2. 块作用域 在{ }包裹的代码称为代码块,代码块内部声明的变量外部有可能无法被访问
  • 全局作用域

script标签 或者 .js文件里面的,都是全局声明的

  • 作用域链

本质是一个底层的变量查找机制

在函数被执行时,会优先查找当前函数作用域中查找变量,如果当前作用域查找不到,会逐级查找父级作用域直到全局作用域

像上面所说的逐级查找,就形成了一个作用域链

二、垃圾回收机制
  • 内存的生命周期

    1. 内存分配 :声明变量,函数,对象的时候,系统会为他们自动分配内存
    2. 内存使用:即读写内存,也就是使用函数,变量等等
    3. 内存回收 :使用完毕,由垃圾回收器自动回收不再使用的内存

    全局变量一般不会回收,除非关闭页面,一般情况局部变量的值,不用了,会被自动回收掉,

    内存泄漏:程序中分配的内存由于某种原因程序未释放无法释放叫做内存泄漏

  • 垃圾回收机制–算法说明

堆栈空间分配区别:

  1. 栈:由操作系统自动分配释放函数的参数值,局部变量等,基本数据类型放到栈里面
  2. 堆:一般有程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到栈里面

回收算法

  1. 引用计数法

    1. 跟踪记录被引用的次数
    2. 如果引用了一次就累加++
    3. 如果引用次数为0,则释放内存

    有个致命的问题就是:嵌套引用(循环引用),就是2个对象相互引用,就会导致内存泄漏

  2. 标记清除法

    1. 标记清除法将 不再使用的对象,定义为 无法达到的对象
    2. 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是可以从根部到达的对象,都是还需要使用
    3. 无法从根部出发触及到的对象被标记为不再使用,就回收
三、闭包

简单理解:闭包 = 内层函数 + 外层函数的变量

// 外层函数 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>
    
  • 函数参数

    1. 动态参数

      例如求和函数,不管用户输入多少个实参,都需要把和求出来

      arguments:

       function getSum(){
       	// arguments 动态参数,只存在函数里面  是个伪数组
           let sum = 0
           for(let i = 0; i <arguments.length;i++){
               sum+= arguments[i]
           }
           return sum
       }
      getSum(2,3,4)
      
    2. 剩余参数

       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')
  1. 箭头函数属于表达式函数,因此不存在函数提升
  2. 箭头函数只有一个参数时可以省略圆括号()
  3. 箭头函数函数体只有一行代码时可以省略花括号{},并且自动作为返回值被返回
  4. 加括号的函数体返回的是对象字面量表达式
  • 普通函数有arguments动态参数,箭头函数没有动态参数,但是有剩余参数 …arr
  • 箭头函数的 this
    1. 箭头函数本身不会有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
    
七、深入对象
  1. 利用字面量

    const o = {
    	......
    }
    
  2. 利用 new Object

    const obj = new Object()
    obj.name = 'ljh' // 给对象里面添加
    // 第二种
    const obj = new Object({name:'ljh'})
    
  3. 利用构造函数

八、构造函数

是一种特殊的函数,主要用来初始化对象

//创建一个构造函数 
function Pig(uname,age,sex){
    this.uname = uname
    this.age = age
    this.sex = sex
}
new Pig('佩奇'6'女')
九、内置构造函数

Object、Array、String、Number

  1. 获得所有属性名 Object.keys(obj)

  2. 获得所有属性值 Object.values(obj)

  3. 拷贝对象 Object.assign(newObj,obj)

  4. 数组方法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 _ _’

总结

  1. prototype是什么?哪里来的

    原型(原型对象)

    构造函数都自动有原型

  2. constructor属性在哪里?作用干啥的

    prototyoe原型和对象原型 _ _proto _ _ 里面都有

    都指向创建实例对象/原型的构造函数

  3. _ _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
}
原型链

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

十、深浅拷贝
  • 浅拷贝

拷贝的是地址

常用方法:

  1. 拷贝对象 Object.assgin() 或者 展开运算符 {…obj}拷贝对象
  2. 拷贝数组:Array.prototype.concat()
  • 深拷贝

首先浅拷贝和深拷贝只针对引用类型

深拷贝:拷贝的是对象,不是地址

常见方法:

  1. 通过递归实现深拷贝
  2. lodash 、cloneDeep
  3. 通过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)

单位时间内,频繁触发,只执行最后一次

实现方式:

  1. ladash提供的防抖来处理

  2. 手写一个防抖函数

    核心思路

    防抖的核心就是用定时器,setTimeout来实现

    1. 声明一个定时器变量
    2. 当事件触发的时候先判断是否有定时器了,如果有就先清楚以前的定时器
    3. 如果没有就开启定时器,记得存到变量里面
    4. 定时器中调用要执行的函数
function debounce(fn,t){
    let timer
    return function(){
        if(timer) clearTimeout(timer)
       	timer = serTimeout(function(){
            fn()
        },t)
    }
}
box.addEvenListener('mousemove',debounce(mouseMove,500))
十四、节流-throttle

在单位时间内,频繁触发,只执行一次

实现方式:

  1. ladash提供的节流函数来处理
  2. 手写一个防抖函数
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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0.2℃-冰激凌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值