ES6新
ES6新
EchoES6 本质上是指ES6+ 本质上包含了Es6 及以上的一些版本
1. 作用域
概念:作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问。
作用域分为 局部作用域和全局作用域
局部作用域
1.函数作用域在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
函数内部声明的变量,在函数外部无法被访问
函数的参数也是函数内部的局部变量
不同函数内部声明的变量无法互相访问
函数执行完毕后,函数内部的变量实际被清空了
2. 块作用域:在 JavaScript 中使用 { } 包裹的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问
let 声明的变量会产生块作用域,var 不会产生块作用域
const 声明的常量也会产生块作用域
不同代码块之间的变量无法互相访问
推荐使用 let 或 const
全局作用域
script>标签 和 .js 文件 的【最外层】就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。全局作用域中声明的变量,任何其它作用域都可以被访问。
为 window 对象动态添加的属性默认也是全局的,不推荐!
函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
尽可能少的声明全局变量,防止全局变量被污染
1.1 作用域链(面试八股文)
作用域链本质上是底层的变量查找机制。
在函数被执行时,会优先查找当前函数作用域中查找变量。
如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域。 说白了就是一层一层的往上找。
嵌套关系的作用域串联起来形成了作用域链
相同作用域链中按着从小到大的规则查找变量
子作用域能够访问父作用域,父级作用域无法访问子级作用域
1.2 垃圾回收机制
JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。说白了就是先给变量啊,函数啊这种,分配内存空间,如果你这个代码或者函数一直不用的话,js就自动帮你把这个空间回收了。
内存的生命周期
内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
内存使用:即读写内存,也就是使用变量、函数等
内存回收:使用完毕,由垃圾回收自动回收不再使用的内存
注意:
全局变量一般不会回收(关闭页面回收);
一般情况下局部变量的值, 不用了, 会被自动回收掉
内存泄漏:不再用到的内存,没有及时释放,就叫做内存泄漏(不用, 但是又没有释放)
垃圾回收机制算法说明:
栈(操作系统): 由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面。
堆(操作系统): 一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面。
引用计数
IE采用的引用计数算法, 定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象
算法:
跟踪记录被引用的次数
如果被引用了一次,那么就记录次数1,多次引用会累加 ++
如果减少一个引用就减1 –
如果引用次数是0 ,则释放内存。
1
2let a={1}
let b=a上述的{1} 就是被引用了两次
1
2const 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
8function fn() {
let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
return '引用计数无法回收'
}
fn()你指向我 ,我指向你,都会计数,但是没什么用
标记清除法
浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。
算法:
标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的。
那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
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 | log(a) |
- 把所有用var 声明的变量提升到最前面,只提升声明,不提升赋值
1 | log(a) |
变量在未声明即被访问时会报语法错误
变量在var声明之前即被访问,变量的值为 undefined
let/const 声明的变量不存在变量提升
变量提升出现在相同作用域当中
实际开发中推荐先声明再访问变量
变量提升并不是一个好东西,很多时候程序写着写着就会出现一些bug,所以现在都是使用es6 的新语法,基本上不存在变量提升
1.5 函数提升
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。 就是把所有的函数声明都提升到最前面。
1 | function fun(){ //函数的声明会提升到最前面 |
函数提升能够使函数的声明调用更灵活
函数表达式不存在提升的现象
函数提升出现在相同作用域当中
1.6 函数的动态参数
不管用户传几个参数,都要把和求出来
arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参
1 | <script> |
1.7 函数的剩余参数
剩余参数允许我们将一个不定数量的参数表示为一个数组,在不确定具体要传几个参数的时候就使用剩余参数
… 是语法符号,置于最末函数形参之前,用于获取多余的实参
借助 … 获取的剩余实参,是个真数组
1 | function fun(a,b,...other){ |
开发中还是使用剩余参数
1.8 展开运算符
展开运算符(…),将一个数组进行展开,不会改变原数组。
1 | let arr=[1,2,3,4,5] |
展开运算符的使用 求数组中的最大值
1 | let arr=[1,2,3,4,5] |
2.箭头函数(非常重要)
目的:引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
使用场景:箭头函数更适用于那些本来需要匿名函数的地方,匿名函数可以使用箭头函数来替代
- 箭头函数的写法
1 | // 匿名函数 |
- 箭头函数的传参
1 | <script> |
- 箭头函数的简写形式: 只有一个参数的时候,() 也可以省略,没有参数的时候不能省略()
1 | const fun1=a=>{console.log(a) } |
- 省略return:如果函数体只有一行代码,可以写到一行上,并且无需写 return 直接返回值
1 | // 普通函数 |
- 直接返回一个对象
1 | // 箭头函数直接返回一个对象 |
2.1 箭头函数this指向问题
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
只有函数才有this !!!
- 普通函数的this
箭头函数的this:箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
!!!只有函数才有this !!!
- DOM 函数中不推荐使用this
在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window,因此
DOM事件回调函数为了简便,还是不太推荐使用箭头函数
3.解构赋值
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。说白了就是将数组中的元素赋值给变量
基本语法:
赋值运算符 = 左侧的 [] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
变量的顺序对应数组单元值的位置依次进行赋值操作
1 | const arr=[100,200,300] |
1 | const [a,b,c]=[1,2,3] //1给a 2 给b 3 给 c |
使用解构赋值 交换两个变量
1 | <script> |
立即执行函数后面必须加分号,数组为代码开头的时候必须加分号
数组解构赋值练习
3.1 数组解构
- 数组多,单元值少
1 | const [a,b,c,d]=[1,2,3] |
- 变量少,单元值多
1 | const [a,b]=[1,2,3,4] |
- 变量少,单元值多,剩余值的方式来传递
1 | const [a,b]=[1,2,3,4] |
防止undefined 情况,可以设置默认值
当有参数传递过来的时候就使用传递过来的参数,否则就使用默认的值。
1 | const [a=1,b=2]=[3] |
按需导入,忽略某些值
传入的时候都是以逗号为分界的
1 | const [a,b,,d]=[1,2,3,4] |
- 多维数组的解构
1 | const arr=[1,2,[1,2]] |
多维数组的解构
1 | const [a,b,c]=[1,2,[1,2]] |
3.2 对象解构(重要)
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法、
解构赋值的语法
赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
对象属性的值将被赋值给与属性名相同的变量
注意解构的变量名不要和外面的变量名冲突否则报错
对象中找不到与变量名一致的属性时变量值为 undefined
1 | <script> |
给新的变量名赋值
变量名不想使用被解构的对象的变量名,可以给变量名改名。
1 | <script> |
- 解构数组对象
1 | <script> |
- 多级对象解构(多练习)
1 | const obj={ |
1 | const obj=[{ |
练习
1 | <!DOCTYPE html> |
解析
1 | <!DOCTYPE html> |
3.3遍历数组方法 forEach (重点)
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数,适合遍历数组对象。
forEach 主要是遍历数组
参数当前数组元素是必须要写的, 索引号可选。
1 | const arr=[1,2,3,4] |