老己的JavaScript 高级笔记1

21 阅读5分钟

JavaScript 高级 第一天

一、作用域

作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问

1.局部作用域

局部作用域分为函数作用域和块作用域

函数作用域

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

function getSum(){
      const num=10
    }
    console.log(num) //无法访问
  • 函数内部声明的变量,外部无法被访问

  • 函数参数是函数内部的局部变量

  • 不同函数内部声明变量无法互相访问

  • 函数执行完毕,变量实际上立刻被清空


块作用域

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

for(let i=0;i<=6;i++){
      console.log(t) //可被访问
    }
    console.log(t) //不可访问
  • 推荐使用了let或const,二者声明的变量会产生块作用域,var不会产生块作用域

  • 不同代码块之间的变量无法相互访问

2.全局作用域

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

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

const num=10
    function fn(){
      console.log(num) //可访问
    }
    console.log(num) //可访问
  • 尽可能少的声明全局变量,防止全局变量被污染

  • 为window对象动态添加属性默认为全局,但不推荐

  • 函数中未使用任何关键字声明的变量为全局变量,但不推荐

3.作用域链

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

在函数被执行时,会优先查找当前函数作用域中查找变量

如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域

子作用域能够访问父作用域,父级作用域无法访问子级作用域

4.JS垃圾回收机制

垃圾回收机制(Garbage Collection),简称 GC

S中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收

不再用到的内存,没有及时释放,就叫做内存泄漏


内存的生命周期:

  • 内存分配

当我们声明变量、函数、对象的时候,系统会自动为他们分配内存

  • 内存使用

即读写内存,也就是使用变量、函数等

  • 内存回收

使用完毕,由垃圾回收自动回收不再使用的内存

全局变量一般不会回收,会在关闭页面回收;而局部变量一般无需用到,会被自动回收掉


拓展

栈(操作系统): 由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面

堆(操作系统): 一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面

浏览器垃圾回收算法: 引用计数法标记清除法

引用计数法——基本不再使用

一个对象是否有指向它的引用,没有引用了就回收对象

即:

  • 跟踪记录被引用的次数

  • 如果被引用了一次,那么就记录次数1,多次引用会累加 ++

  • 如果减少一个引用就减1

  • 如果引用次数是0 ,则释放内存

eg:

const arr=[1,2,3,4] //引用为1
arr=null //引用为0,回收
let person={
    age: 18,
    name:'佩奇'
}
let p=person //p引用到对象,引用为2
person=1 //引用为1
p=null //引用为0,回收

但是当两个对象相互引用,哪怕二者不再使用,但是垃圾回收器不会进行回收,会导致内存泄露

eg:

function fn(){
    let o1={}
    let o2={}
    o1.a=o2
    o2.a=o1
    return '引用计数无法回收'
}
fn()

标记清除法

标记清除算法将“不再使用的对象”定义为“无法达到的对象”

从根部(在JS中就是全局对象)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的

那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收

只需要知道是从根部来扫描标记即可,除去全局对象,基本都可回收

5.闭包

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

闭包作用:封闭数据,提供操作,外部也可以访问函数内部的变量

基本格式:

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

闭包应用:实现数据的私有

eg:

// let count=1 
    function fn() {
      let count = 1
      function fun() {
        count++
        console.log(`函数被调用${count}次`)
      }
      return fun
    }
    const result = fn()
    result()

count为全局变量容易被修改

此时,实现数据私有,无法直接修改count

6.变量提升

变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)

eg:

console.log(str+'world')
var str='hello'
  • 变量在未声明即被访问时会报语法错误

  • 变量在var声明之前即被访问,变量的值为undefined

  • let/const声明的变量不存在变量提升

  • 变量提升出现在相同作用域当中

  • 实际开发中推荐先声明再访问变量

二、函数进阶

1.函数提升

函数在声明之前即可被调用

eg:

foo()
function foo(){
    console.log('声明之前即被调用')
}
  • 函数提升能够使函数的声明调用更灵活

  • 函数表达式不存在提升的现象

  • 函数提升出现在相同作用域当中

2.函数参数

动态参数

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

eg:

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, 3)
  • arguments 是一个伪数组,只存在于函数中

  • arguments 的作用是动态获取函数的实参

  • 通过for循环依次得到传递过来的实参

剩余参数——提倡使用

剩余参数将不定数量的参数转为数组进行传输

eg:

function getSum(...other) {
      console.log(other)
      //other 得到[1,2,3]
    }
    getSum(1, 2, 3)
  • ... 是语法符号,置于最末函数形参之前,用于获取多余的实参

  • 借助 ... 获取的剩余实参,是个真数组/argument是伪数组


补充

展开运算符

展开运算符(…),将一个数组进行展开

eg:

const arr=[1,5,3,8,2]
console.log(...arr) //1 5 3 8 2
  • 不会修改原数组

  • 运用场景: 求数组最大值(最小值)、合并数组等

eg:

最大值、最小值

const arr=[1,5,3,8,2]
console.log(Math.max(...arr))//8
console.log(Math.min(...arr))//1

合并数组

const arr1=[1,2,3]
const arr2=[4,5,6]
const arr3=[...arr1,...arr2]
console.log(arr3)//[1,2,3,4,5,6]

剩余参数:函数参数使用,得到真数组/ 展开运算符:数组中使用,数组展开

3.箭头函数

写法:

语法1:基本写法

const fn=()=>{
    console.log('箭头函数')
}
fn()

语法2:只有一个参数可以省略小括号

const fn=x=>{
    return x+x
}
console.log(fn(1))

语法3:如果函数体只有一行代码,可以写到一行上,并且无需写 return 直接返回值

const fn=(x,y)=>x+y
console.log(fn(1,2))

语法4:加括号的函数体返回对象字面量表达式

const fn1=uname=>({uname:uname})
console.log(fn1('pink'))//{uname:'pink'}

参数

箭头函数有剩余参数 ..args

eg:

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

this

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

也就是箭头函数的this是上一个this指向的

eg:

const user = {
      name: '小明',
      sleep: function () {
        console.log(this)// 指向user
        const fn = () => {
          console.log(this) //指向user,与上面的this一致
        }
        fn()
      }
    }
    user.sleep()

事件回调函数使用箭头函数时,this 为全局的 window,因此 DOM事件回调函数为了简便,还是不太推荐使用箭头函数

三、解构赋值

1.数组解构

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

eg:

const arr=[1,2,3]
const [a,b,c]=arr
console.log(a)//1
console.log(b)//2
console.log(c)//3
  • 赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量

  • 变量的顺序对应数组单元值的位置依次进行赋值操作


交互两个变量

let a=1
let b=3;//需要加分号
[b,a]=[a,b]

注意分号!:

(function t() {})();
//or
;(function t(){})()

案例

独立完成数组解构赋值

需求①: 有个数组: const pc = ['海尔', '联想', '小米', '方正'] 解构为变量: hr lx mi fz

需求②:请将最大值和最小值函数返回值解构 max 和min 两个变量

const pc = ['海尔', '联想', '小米', '方正']
    const [hr, lx, mi, fz] = pc
//
function getValue() {
    return [100, 60]
}
const [max, min] = getValue()

变量多、单元值少的情况:

eg:

const [a,b,c,d]=['小米','苹果','华为']
console.log(a) //小米
console.log(b) //苹果
console.log(c) //华为
console.log(d) //undefined

变量的数量大于单元值数量时,多余的变量将被赋值为 undefined


变量少、单元值多的情况:

如例子所示

eg:

const [a,b,c,d]=['小米','苹果','华为','格力']
console.log(a) //小米
console.log(b) //苹果
console.log(c) //华为

利用剩余参数解决变量少 单元值多的情况:

eg:

const [a,b,...tel]=['小米','苹果','华为','格力']
console.log(a) //小米
console.log(b) //苹果
console.log(tel) //['华为','格力']

剩余参数返回的是一个数组


防止有undefined传递单元值的情况,可以设置默认值:

eg:

const [a='手机',b='华为']=['小米']
console.log(a) //小米
console.log(b) //华为

允许初始化变量的默认值,且只有单元值为 undefined 时默认值才会生效


按需导入,忽略某些返回值:

eg:

const [a,,c,d]=['小米','苹果','华为','格力']
console.log(a) //小米
console.log(c) //华为
console.log(d) //格力

支持多维数组的结构:

eg:

const [a,b]=['苹果',['小米','华为']]
console.log(a) //苹果
console.log(b) //['小米','华为']
const [a,[b,c]]=['苹果',['小米','华为']]
console.log(a) //苹果
console.log(b) //小米
console.log(c) //华为

2.对象解构

解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值

(1)基本语法

eg:

const user = {
      name: '小明',
      age: 18
    }
    const { name, age } = user
    console.log(name)
    console.log(age)
  • 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量

  • 对象属性的值将被赋值给与属性名相同的变量

  • 注意解构的变量名不要和外面的变量名冲突否则报错

  • 对象中找不到与变量名一致的属性时变量值为 undefined


(2)给新的变量名赋值

可以直接从一个对象中提取变量并同时修改新的变量名

eg:

const user = {
      name: '小明',
      age: 18
    }
    const { name:uname, age } = user
    console.log(uname)
    console.log(age)

(3)数组对象解构

eg:

const pig = [
      {
        name: '佩奇',
        age: 6
      }
    ]
    const [{ name, age }] = pig
    console.log(name, age)

案例

需求①: 有个对象: const pig = { name: '佩奇',age: 6 }结构为变量: 完成对象解构,并以此打印出值

需求②:请将pig对象中的name,通过对象解构的形式改为 uname,并打印输出

需求③:请将 数组对象, 完成 商品名和价格的解构

const pig = {
      name: '佩奇',
      age: 6
    }
    const { name, age } = pig
    console.log(name)
    console.log(age)
 //
 const pig = {
      name: '佩奇',
      age: 6
    }
 const {name:uname,age}=pig
    console.log(uname)
    console.log(age)
//
const goods=[{
      goodsName:'小米',
      price:1999
    }]
    const [{goodsName,price}]=goods

(4)多级对象解构

对象里面包含对象

eg:

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)
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)

函数传入可自动在内部处理解构

eg:

const msg = {
      "code": 200,
      "msg": "获取新闻列表成功",
      "data": [
        {
          "id": 1,
          "title": "5G商用自己,三大运用商收入下降",
          "count": 58
        },
        {
          "id": 2,
          "title": "国际媒体头条速览",
          "count": 56
        },
        {
          "id": 3,
          "title": "乌克兰和俄罗斯持续冲突",
          "count": 1669
        },
      ]
    }
function render({data}){
    //内部处理 let {data}=msg
    console.log(data)
}

案例

完成需求

const msg = {
      "code": 200,
      "msg": "获取新闻列表成功",
      "data": [
        {
          "id": 1,
          "title": "5G商用自己,三大运用商收入下降",
          "count": 58
        },
        {
          "id": 2,
          "title": "国际媒体头条速览",
          "count": 56
        },
        {
          "id": 3,
          "title": "乌克兰和俄罗斯持续冲突",
          "count": 1669
        },

      ]
    }

    // 需求1: 请将以上msg对象  采用对象解构的方式 只选出  data 方面后面使用渲染页面
    const {data}=msg
    console.log(data)
    // 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数
    function render({data}){
      console.log(data)
    }
    render(msg)
    // 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
    function render2({data:myData}){
      console.log(myData)
    }
    render(msg)

补充

遍历数组forEach方法(重点)

forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数

遍历数组的每个元素

语法:

被遍历的数组.forEach(function(当前数组元素,当前元素索引号)){
    //函数体
})

eg:

const arr=['pink','red','green']
    arr.forEach(function(item,index){
      console.log(`当前数组元素是:${item}`) //依次打印数组每个元素
      console.log(`当前数组元素的索引是:${index}`) //依次打印数组每个元素的索引
    })
  • forEach 主要是遍历数组

  • 参数当前数组元素是必须要写的, 索引号可选。

案例

渲染商品列表

核心思路:有多少条数据,就渲染多少模块,然后 生成对应的 html结构标签, 赋值给 list标签即可

①:利用forEach 遍历数据里面的 数据

②:拿到数据,利用字符串拼接生成结构添加到页面中

③:注意:传递参数的时候,可以使用对象解构

let str = ''
    const list = document.querySelector('.list')
    goodsList.forEach(function (item) {
      const { picture, price, name } = item
      str += `<div class="item">
      <img src="${picture}" alt="">
      <p class="name">${name}</p>
      <p class="price">${price}</p>
    </div>`
    })
    list.innerHTML = str

筛选数组filter方法(重点)

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

筛选数组符合条件的元素,并返回筛选之后元素的新数组

语法:

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

eg:

const score = [10, 50, 3, 40, 33]
    const re = score.filter(function (item) {
      return item > 50
    })
    console.log(re) //[50,40,33]
  • 返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组

  • 因为返回新数组,所以不会影响原数组

  • currentValue 必须写, index 可选