ES6新

ES6 本质上是指ES6+ 本质上包含了Es6 及以上的一些版本

1. 作用域

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

作用域分为 局部作用域和全局作用域

  • 局部作用域

    1.函数作用域在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。

    局部作用域

    1. 函数内部声明的变量,在函数外部无法被访问

    2. 函数的参数也是函数内部的局部变量

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

    4. 函数执行完毕后,函数内部的变量实际被清空了

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

    块作用域

    1. let 声明的变量会产生块作用域,var 不会产生块作用域

    2. const 声明的常量也会产生块作用域

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

    4. 推荐使用 let 或 const

  • 全局作用域

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

    全局作用域

    1. 为 window 对象动态添加的属性默认也是全局的,不推荐!

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

    3. 尽可能少的声明全局变量,防止全局变量被污染

1.1 作用域链(面试八股文)

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

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

如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域。 说白了就是一层一层的往上找。

作用域链

  1. 嵌套关系的作用域串联起来形成了作用域链

  2. 相同作用域链中按着从小到大的规则查找变量

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

1.2 垃圾回收机制

JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。说白了就是先给变量啊,函数啊这种,分配内存空间,如果你这个代码或者函数一直不用的话,js就自动帮你把这个空间回收了。

内存的生命周期

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

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

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

注意:

全局变量一般不会回收(关闭页面回收);

一般情况下局部变量的值, 不用了, 会被自动回收

内存泄漏:不再用到的内存,没有及时释放,就叫做内存泄漏(不用, 但是又没有释放)


垃圾回收机制算法说明:

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

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

  • 引用计数

    IE采用的引用计数算法, 定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象

    算法:

    1. 跟踪记录被引用的次数

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

    3. 如果减少一个引用就减1 –

    4. 如果引用次数是0 ,则释放内存。

    1
    2
    let a={1}
    let b=a

    上述的{1} 就是被引用了两次

    1
    2
    const arr = [1, 2, 3, 4] //[1,2,3,4] 目前被 arr 引用
    arr = null //arr 的栈中直接存的是 null 不指向 [1,2,3,4] ,所以[1,2,3,4]就不被引用了 次数就是0

    它却存在一个致命的问题:嵌套引用(循环引用)

    如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。

    1
    2
    3
    4
    5
    6
    7
    8
    function fn() {
    let o1 = {}
    let o2 = {}
    o1.a = o2
    o2.a = o1
    return '引用计数无法回收'
    }
    fn()

    你指向我 ,我指向你,都会计数,但是没什么用

  • 标记清除法

    浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。

    算法:

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

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

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

1.3 闭包

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

简单理解:闭包 = 内层函数 + 外层函数的变量 外层函数包裹着一个内层的函数,内层的函数还在使用外层函数的变量

闭包

  • 闭包的作用: 外部也能访问函数内部的变量

    闭包

    外部能够访问内部的变量

    闭包

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <script>
    function other(){
    let i=0
    return function(){
    console.log(i);
    }
    }
    other()()// other() 是调用other函数 结果是返回一个函数 然后后面的() 是调用被返回的这个函数
    </script>

    使用闭包统计函数被使用的次数

    1
    2
    3
    4
    5
    6
    7
    8
    //不使用闭包
    let i=0
    function other(){
    i++
    return function(){
    console.log(i);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <script>
    function other(){
    let i=0
    return function(){
    i++
    console.log(i);
    }
    }
    </script>

1.4变量提升

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

1
2
log(a)
var a=10
  1. 把所有用var 声明的变量提升到最前面,只提升声明,不提升赋值
1
2
3
4
5
6
7
8
log(a)

var a=10
----------------------------------

var a //变量提升
log(a)
a=10
  1. 变量在未声明即被访问时会报语法错误

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

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

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

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

变量提升并不是一个好东西,很多时候程序写着写着就会出现一些bug,所以现在都是使用es6 的新语法,基本上不存在变量提升

1.5 函数提升

函数提升与变量提升比较类似,是指函数在声明之前即可被调用。 就是把所有的函数声明都提升到最前面。

函数提升

1
2
3
4
5
6
7
8
9
10
11
12
13
function fun(){  //函数的声明会提升到最前面
log("111")
}

fun()// 函数的调用

//--------------------------------------

fun2() //函数的声明会提升到最前面 所以这样写是没有问题的

function fun2(){
log("bbb")
}
  1. 函数提升能够使函数的声明调用更灵活

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

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

1.6 函数的动态参数

不管用户传几个参数,都要把和求出来

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
function fun(){

// arguments 是动态参数,只存在于函数里面
// arguments 是个伪数组,里面放的是参数
console.log(arguments);

let sum=0
for(let i=0;i<arguments.length;i++){
sum+=arguments[i]
}
}

fun(1,2,3)
</script>

动态参数

1.7 函数的剩余参数

剩余参数允许我们将一个不定数量的参数表示为一个数组,在不确定具体要传几个参数的时候就使用剩余参数

  1. … 是语法符号,置于最末函数形参之前,用于获取多余的实参

  2. 借助 … 获取的剩余实参,是个真数组

剩余参数

1
2
3
4
5
6
function fun(a,b,...other){

console.log(other);

}
fun(1,2,3,4,5) //把1 给了a, 2给了b 剩余的都给了other

开发中还是使用剩余参数

1.8 展开运算符

展开运算符(…),将一个数组进行展开,不会改变原数组。

1
2
3
let arr=[1,2,3,4,5]

log(...arr)//展开运算符,能将数组展开

展开运算符的使用 求数组中的最大值

1
2
3
let arr=[1,2,3,4,5]

log(Math.Max(...arr))//展开运算符,能将数组展开

2.箭头函数(非常重要)

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

使用场景:箭头函数更适用于那些本来需要匿名函数的地方,匿名函数可以使用箭头函数来替代

  • 箭头函数的写法
1
2
3
4
// 匿名函数
const fun=function(){}
// 使用箭头函数来替代匿名函数
const fun1=()=>{}
  • 箭头函数的传参
1
2
3
4
5
6
7
8
9
<script>
// 匿名函数
const fun=function(){}
// 使用箭头函数来替代匿名函数
const fun1=(a)=>{
console.log(a);
}
fun1(1)
</script>
  • 箭头函数的简写形式: 只有一个参数的时候,() 也可以省略,没有参数的时候不能省略()
1
const fun1=a=>{console.log(a) }
  • 省略return:如果函数体只有一行代码,可以写到一行上,并且无需写 return 直接返回值
1
2
3
4
5
6
7
8
9
  // 普通函数
const fun=function(a,b){
return a+b
}

fun(1,2)

// 箭头函数
const fun =(a,b)=>a+b
  • 直接返回一个对象
1
2
3
4
5
6
7
8
9
//  箭头函数直接返回一个对象
const fun =(name)=>{
return {name:name}
}

// 简写形式
const fun=name=>({name:name})// 对象必须用()包裹起来,不然他不是一个整体

fun('张三')

2.1 箭头函数this指向问题

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

只有函数才有this !!!

  • 普通函数的this

箭头函数this

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

    !!!只有函数才有this !!!

箭头函数this

箭头函数

  • DOM 函数中不推荐使用this

在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window,因此

DOM事件回调函数为了简便,还是不太推荐使用箭头函数

箭头函数this

3.解构赋值

数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。说白了就是将数组中的元素赋值给变量

解构赋值

基本语法:

  1. 赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量

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

1
2
3
4
5
6
7
8
const arr=[100,200,300]

// const min=arr[0]
// const avg=arr[1]
// const max=arr[2]
//数组解构 赋值
const [min,avg,max]=arr
console.log(fun('张三'));
1
const [a,b,c]=[1,2,3] //1给a  2 给b  3 给 c

使用解构赋值 交换两个变量

1
2
3
4
5
6
7
8
9
<script>

let a=100
let b=200;// 这里必须加分号,不加分号就相当于 let b=200和 [a,b]=[b,a]连在一起

[a,b]=[b,a]

console.log(a,b);
</script>

立即执行函数后面必须加分号,数组为代码开头的时候必须加分号

数组解构赋值练习

数组解构

3.1 数组解构

  • 数组多,单元值少
1
2
3
const [a,b,c,d]=[1,2,3]
//a 1 b 2 c 3
//d 是undefined
  • 变量少,单元值多
1
2
const [a,b]=[1,2,3,4]
//a 1 b 2
  • 变量少,单元值多,剩余值的方式来传递
1
2
3
4
5
const [a,b]=[1,2,3,4]
//a 1 b 2 这种时候可以使用 剩余参数

const [a,b,...c]=[1,2,3,4]
// a 1 b 2 c:[3,4]
  • 防止undefined 情况,可以设置默认值

    当有参数传递过来的时候就使用传递过来的参数,否则就使用默认的值。

1
2
3
const [a=1,b=2]=[3]

//a 3 b2
  • 按需导入,忽略某些值

    传入的时候都是以逗号为分界的

1
2
3
4
const [a,b,,d]=[1,2,3,4]
//a 1
//b 2
//d 4
  • 多维数组的解构
1
2
3
4
const arr=[1,2,[1,2]]
//arr[1] 1
//arr[2] 2
//arr[3] [1,2]

多维数组的解构

1
2
3
4
5
6
7
8
9
10
const [a,b,c]=[1,2,[1,2]]
//a 1
//b 2
//c [1,2]

const [a,b,[c,d]]=[1,2,[1,2]]
//a 1
//b 2
//c 1
//d 2

3.2 对象解构(重要)

对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法、

  • 解构赋值的语法

    1. 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量

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

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

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

对象解构

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
const obj={
name:'佩奇',
age:19
}

//把对象的值赋值给变量
// let name=obj.name
// let age=obj.age

//使用解构赋值语法
const {name,age}=obj
</script>
  • 给新的变量名赋值

    变量名不想使用被解构的对象的变量名,可以给变量名改名。

1
2
3
4
5
6
7
8
9
<script>
const obj={
name:'佩奇',
age:19
}

//解构名重命名 旧名:新名
const {name:myName,age:Myage}=obj
</script>
  • 解构数组对象
1
2
3
4
5
6
7
8
9
10
11
<script>

const obj=[{
name:'佩奇',
age:19
}]

// 解构数组对象
const [{name,age}]=obj
console.log(name,age)
</script>

练习


  • 多级对象解构(多练习)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const obj={
name:'佩奇',
age:19,

family:{
mom:'菲菲',
fa:'ggboy',
br:'乔治'
}
}

// 多级对象解构 前面需要加上名称

const {name,age, family:{mom,fa,br}}=obj

console.log(name,age,mom,fa,br);
1
2
3
4
5
6
7
8
9
10
11
12
13
const obj=[{
name:'佩奇',
family:{
mom:'菲菲',
fa:'ggboy',
br:'乔治'
},
age:19
}]

const [{name, family:{mom,fa,br},age}]=obj

console.log(name,age,mom,fa,br);

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<script>
// 1. 这是后台传递过来的数据
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 方面后面使用渲染页面



// 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数

function render(这里怎么写) {
// 我们只要 data 数据
// 内部处理

}
render(msg)

// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
function render(这里怎么写) {
// 要求将 获取过来的 data数据 更名为 myData
// 内部处理

}
render(msg)

</script>
</body>

</html>

解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<script>
// 1. 这是后台传递过来的数据
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



// 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数

// 以往的写法--------------------------------

function render (data) {

console.log(data);
// 我们只要 data 数据
// 内部处理

}
render(data)



// ------------------------------------------

// 对象解构写法 稍微麻烦
function render (msg) {

const { data } = msg

console.log(data);
// 我们只要 data 数据
// 内部处理

}
render(msg)

// 最简单写法
function render ({data}) {
console.log(data);
}
render(msg)

// ----------------------------------------

// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
function render({data:myData}) {

console.log(myData);
// 要求将 获取过来的 data数据 更名为 myData
// 内部处理

}
render(msg)

</script>
</body>

</html>

3.3遍历数组方法 forEach (重点)

forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数,适合遍历数组对象。

forEach

  1. forEach 主要是遍历数组

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

1
2
3
4
5
const arr=[1,2,3,4]

arr.forEach(function(item,index){{
console.log(item,index);
}})