JS—作用域、解构、箭头函数

Js进阶之作用域、解构、箭头函数

作用域

局部作用域

函数作用域

在函数内部声明的变量只能在函数内部访问,外部无法直接访问。

块作用域

用{}包裹的代码称为代码块,代码块内部声明的变量在外部 有可能 无法被访问。

注意:

  1. let声明的变量会产生块作用域,var不会产生块作用域
  2. const声明的常量也会产生块作用域。
  3. 不同代码块之间的变量无法互相访问。
  4. 推荐使用let或者const。

全局作用域

script标签 和.js文件的最外层就是全局作用域,在此声明的变量在函数内部也可以被访问。

全局作用域中声明的变量,任何其他作用域都可以访问。

注意:

  1. 为window对象动态添加的属性默认也是全局的。
  2. 函数中未使用任何关键字声明的变量为全局变量。
  3. 尽可能少声明全局变量,防止变量污染。

作用域链

作用域链本质上是底层的变量查找机制

  • 在函数被执行时,会优先查找当前函数作用域中查找变量。
  • 如果当前作用域差找不到则会依次逐级查找父级作用域直至全局作用域。(有点像冒泡(?))

即:

  1. 嵌套关系的作用域串联起来形成了作用域链。
  2. 相同作用域链中按着从小到大的规则查找变量。
  3. 子作用域能够访问父作用域,父级作用域无法访问子级作用域。

内存的生命周期

  1. 内存分配:声明变量、函数、对象时,系统自动分配内存。
  2. 内存使用:即读写内存,简而言之就是使用变量、函数。
  3. 内存回收:使用完毕,垃圾回收器自动回收不再使用的内存。

注意:

  1. 全局变量一般不会回收(关闭页面后回收
  2. 一般局部变量的值会被自动回收

内存泄漏

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

JS垃圾回收机制

  1. 引用计数法:定义“内存不再使用”,即看一个对象是否有指向它的引用,没有就回收。 【已经很少使用】

    在这里插入图片描述

    例子:

    // 声明了一个对象pa,地址存在栈中,内容存在堆中
    let pa = {
        uname: 'jack',
        age: 18
    }
    // 声明一个对象pb,将pa地址赋给pb
    let pb = pa
    // 此时,内容的引用次数为2
    // 改变pa地址,使其不再指向内容
    pa = 0
    // 此时,内容的引用次数为1(pb)
    // 再改变pb地址
    pb = null
    // 最后,内容的引用次数为0,被回收
    

    但是,如果存在嵌套引用,两个对象相互引用,尽管它们已不再使用,垃圾回收器不会回收,导致内存泄漏。这是因为它们的引用次数永远不会是0。

      function fn(){
        let a={}
        let b ={}
        a.p = b
        b.p = a
        return
        // return完后,a,b局部变量本应该销毁,但是a.p引用了b,b.p引用了a,二者的引用次数恒为1,造成了内存泄漏
      }
      fn()
    
  2. 标记清除法 【现代浏览器使用的改进算法的基础】

    1. 将“不再使用的对象”定义为“无法到达的对象”。
    2. 根部出发定时扫描内存中的对象。凡是能从根部到达的对象,都还是要使用的。
    3. 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。

    在这里插入图片描述

    总的来说就是,从根部定期扫描,扫不到的就回收。

闭包

定义:一个函数对周围状态的引用捆绑在一起,内层函数访问到其外层函数的作用域。

简单理解:闭包 = 内层函数 + 外层函数的变量 (内层函数用到了外层函数的变量(?))

可以用来让外部访问函数内部的变量

// 闭包简单写法
function out(){
    let a = 10
    function fn(){
        console.log(a)
    }
    fn()
}
out()
// 闭包常见写法
function pp(){
    let a = 20
    function fn(){
        console.log(a)
    }
    return fn	// 闭包可能引起内存泄漏,a没有被回收,一直能被查找到
}
const fu = pp()
fu() // 输出10
// 外面使用了10这个值

应用:实现数据的私有。

变量提升

允许在变量声明之前被访问(仅存在于var声明变量)。

  1. 把所有var声明的变量提升到当前作用域最前面。
  2. 只提升声明,不提升赋值。

在这里插入图片描述

【此处输出的是 undefined, 说明只提升声明不提升赋值的特性】

函数进阶

函数提升

  1. 把所有函数声明提升到当前作用域的最前面。
  2. 只提升函数声明,不提升函数调用。
fn()
function fn(){
    console.log(1)
}

【特殊】函数表达式必须先声明和赋值,再调用

函数参数

  1. 动态参数

    arguments 是函数内置的伪数组变量,它包含了调用函数时传入的所有实参。

    function getSum(){
        let sum=0
        for(let i=0;i<arguments.length;i++)
            sum+=arguments[i]
        console.log(sum)
    }
    getSum(1,2)
    getSum(1,2,3,4)
    
  2. 剩余参数

    ...变量名允许将不定量的参数表示为一个数组。【获取多余的参数】

    function getSum(...arr){
        console.log(arr)
    }
    getSum(1,2)
    

二者区别:

在这里插入图片描述

  1. 剩余参数是将没有形参对应的那些实参传入…中。
  2. 剩余参数是真数组,动态参数是伪数组。

展开运算符

...可以把数组展开。

const arr = [1,2,3]
console.log(...arr) // 输出1,2,3

其典型运算场景:求数组最值,合并数组等。

// 求数组最值
const arr = [1,2,3]
console.log(Math.max(...arr))
console.log(Math.min(...arr))
// 合并数组
const arr2 = [4,5,6]
const arr3 = [...arr,...arr2]
console.log(...arr3) // 1,2,3,4,5,6

箭头函数

目的:更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁。

使用场景:更适用于本来需要匿名函数的地方

基本语法
const fn = (x) => {
    console.log(x)
} 
// x是参数,只有一个形参的时候,可以省略小括号
// 只有一行代码,省略大括号,并且无需写return,直接返回值
const fn2 = x => console.log(x)
const fn3 = x => x+x
fn(1) //1
fn2(2) //2
console.log(fn3(2)) //4

// 箭头函数可以直接返回一个对象
const fn4 = (uname) => ({ name:uname })
// 因为{}会分不清是函数体还是要返回对象,所以用()包起来
console.log(fn4('刘德华'))
箭头函数参数

箭头函数没有arguments动态参数,但是有剩余参数...args

在这里插入图片描述

箭头函数this

箭头函数不会创建自己的this,它只会从自己的作用域链上一层沿用this。

在这里插入图片描述

在使用DOM事件回调函数,不推荐用箭头函数,this的值不指向该事件函数本身。

解构赋值

数组解构

将数组对的单元值快速批量赋值给一系列变量的简洁语法。

基本语法
  1. 赋值运算符 = 左侧的[]用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量。
  2. 变量的顺序对应数组单元的位置依次进行赋值操作。
const arr = [1,2,3]
const [a,b,c] = arr 	// a = 1 , b = 2, c = 3

注意:

  1. 单元值少,变量多的情况下:依次赋值后,多余的变量为undefined。

    //防止undefined传递
    const [a=0,b=0] = [1]
    
  2. 单元值多,变量少的情况下:利用剩余参数,但剩余参数只能放在最末位。

    const [a,b,...c] = [1,2,3,4]
    // a = 1, b = 2, c = [3,4]
    
  3. 按需导入赋值

    const [a,,b,c] = [1,2,3,4]
    // a = 1, b = 3, c = 4
    
  4. 支持多维数组的解构

    const [a,b,c] = [1,2,[3,4]]
    // a=1, b=2, c=[3,4]
    
典型应用

变量交换

let a = 1
let b = 2;	// 一定要加分号
[b,a] = [a,b] // a、b交换了值

JS前面一定要加分号的情况:

  1. 立即执行函数
  2. 直接使用数组的时候([1,2,3]、[a,b]……)

对象解构

基本语法
  1. 赋值运算符 = 左侧的{}用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量。
  2. 对象属性的值将被赋值给与属性名相同的变量。
  3. 注意解构的变量名不要和外面的变量名冲突。
  4. 对象中找不到与变量名一致的属性时变量值为undefined
const obj = {
    uname:'小明',
    age:20
}
const {uname,age} = obj
// 等价于 uname = obj.uname; age = obj.age;

注意:

【对象属性名更改】

const uname = 'jack'
const {uname:name,age} = {uname:'jack',age:19}
// 这个uname就被改名为name了,不会报错
// 用的时候就是用name了

【解构数组对象】

const obj = [
    {
        uname:'小明'age:18
    }
]
const [{uname,age}] = obj

【多级对象解构】

const pig = {
    name:'佩奇',
    family:{
        mother:'猪妈妈',
        father:'猪爸爸',
        sister:'乔治'
    },
    age:6
}
// 语法
const {name,family:{mother,father,sister}} = pig
console.log(name)	// 佩奇
console.log(mother)		// 猪妈妈
console.log(father)		// 猪爸爸
console.log(sister)		// 乔治

数组遍历forEach方法

要遍历的数组.forEach(function(当前数组元素,当前元素下标){
    //函数体
})

与map不同的是,forEach不返回数组,它的返回值是undefined。

const arr = ['red','green','blue']
const result = arr.forEach(function(item,index){
    console.log(item) // 数组元素
    console.log(index) //索引号
})
console.log(result) // undefined

注意:index可以省略,但是item不可以省略。

forEach方法适合用于数组对象。

数组筛选filter方法

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素

  1. 筛选数组符合条件的元素,并返回筛选之后元素的新数组
  2. currentValue必须写,index可不写
  3. 返回的是新数组,不影响原数组

语法:

被遍历的数组.filter(function(currentValue,index){
    return 筛选条件
})

例子:

// 筛选数组中大于30的元素
const score = [10,50,3,40,33]
const  re = score.filter(function(item){
	return item>30
})
// re [50,40,33]

做相关案例时遇到的一些点

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值