JS进阶 1——作用域、解构、箭头函数
1.作用域
-
局部作用域分为:函数作用域和块作用域
- 函数作用域:在函数内部声明的变量只能在函数内部被访问
- 块作用域:被{ }包裹的代码成为代码块,代码块内部声明的变量外部将有可能无法访问
- var声明的不会产生块作用域
-
全局作用域:< script >标签和.js文件的最外层就是全局作用域,任何作用域都可以访问
-
作用域链:本质是底层变量查找机制,先查找当前函数作用域,若找不到则逐级查找父级作用域
-
垃圾回收机制(GC):内存的生命周期
- 内存分配:当声明变量、函数、对象的时候,系统自动为他们分配内存
- 内存使用:读写内存,使用变量。函数等
- 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
- 全局变量一般不会回收
- 局部变量的值,不用了会被自动回收
- 内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放就叫做内存泄漏
- 堆栈空间分配区别:栈存放基本数据类型,由操作系统自动分配释放函数的参数值、变量等;堆存放复杂数据类型,一般由程序员手动释放,或由垃圾回收机制回收。
- 垃圾回收算法:引用计数法(会发生内存泄漏)和标记清除法
-
闭包:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其他外层函数的作用域,即闭包=内层函数+外层函数的变量
//简单写法:内部函数访问外部函数的变量function outer() {let a = 10function fn() {console.log(a)}fn()}outer()//常见形式:外部可以访问函数内部的变量function outer() {let a = 10function fn() {console.log(a)}return fn}const fun = outer()fun() //调用函数
-
闭包应用:实现数据私有,且外部可以访问函数内部的变量,会有内存泄露的风险
//统计函数调用次数 调用依次 i++function count() {let i = 0return function () {i++console.log(i)}}const fn = count()
- 变量提升
- 在代码执行之前,会将var声明的变量提升到当前作用域的最前面,只提升声明不提升赋值
2.函数进阶
-
函数提升:将函数声明提升到当前作用域的最前面,函数在声明之前被调用
-
函数参数
- 动态参数:arguments是函数内部布置的伪数组变量,它包含了调用函数时传入的所有实参
cript>function getSum() {console.log(arguments);let sum = 0for (let i = 0; i < arguments.length; i++) {sum += arguments[i]}console.log(sum)}getSum(2, 3)
- 剩余参数:将“…参数名”当作形参写入函数中,是真数组变量
- 与arguments的区别:可以控制实参输入的个数
function getSum(a,b,...arr) { //至少有两个参数console.log(arr) //使用的时候不需要...}getSum(2, 3)getSum(1, 2, 3)
-
箭头函数(重点)
- 箭头函数比函数表达式更简洁,更适用于本来需要匿名函数的地方
- 基本语法
//箭头函数 const fn1 = (x) => {console.log(x)}fn(1)//2.只有一个形参时,小括号可以省略const fn2 = x => {console.log(x)}fn(1)//3.只有一行代码时,大括号可以省略const fn3 = x => console.log(x)fn(1)//4.只有一行代码,可以省略return const fn4 = x => x + xfn(2)//5.可以直接返回一个对象const fn5 = (uname) => ({ uname: uname })fn('小小怪')
-
箭头函数参数
- 普通函数有arguments动态参数
- 箭头函数没有,但有剩余参数
const getSum = (...arr) => {let sum = 0for (let i = 0; i < arr.length; i++) {sum += arr[i]}return sum}const result = getSum(2, 3)console.log(result)
-
箭头函数的this:箭头函数没有自己的this,指向上一层作用域的this指向
- 事件回调函数不推荐使用this
//箭头函数的this是上一层作用域的this指向const fn = () => {console.log(this) //window}fn() //对象方法箭头函数:上一层this为obj的this,而obj的this指向windowconst obj = {uname: '小小怪',sayHi: () => {console.log(this) //window}}obj.sayHi()
3.解构赋值
-
数组解构:将数组的单元值快速批量赋值给一系列变量的简洁语法
const arr = [100, 80, 60]const [max, avg, min] = arr //将100赋值给max,80赋值给avg,60赋值给minconsole.log(max) //100
- 交换变量eg:
//交换变量let a = 1let b = 2;[b, a] = [a, b] //必须加分号 数组开头会以为和前一行一起的console.log(a, b)// 必须加分号的两种情况// 1.立即执行函数// (function(){ })()// (function(){ })()//2.使用数组const ar = [1, 2, 3]const str = 'pink'; //必须加[1, 2, 3].map(function (item) {console.log(item);})
-
对象解构:将对象的属性和方法快速批量赋值给一系列变量的简洁语法
- 对象属性的值将被赋值给与属性名相同的变量
- 注意解构的变量名尽量不要与外面的变量名冲突否则报错
- 对象中找不到与变量名一致的属性名时变量值为undefined
const { uname, age } = {uname: '小小怪',age: 18}console.log(uname)console.log(age)
- 更改解构变量名,处理变量名冲突问题
//2.解构变量名改名 由于解构变量名与外部一个变量名相同 旧变量名:新变量名const { uname: username, age: PeopleAge } = {uname: '小小怪',age: 18}
- 多级对象解构
const pig = {name: '佩奇',family: {mother: '猪妈妈',father: '猪爸爸',sister: '乔治'},age: 6}const { name, family: { mother, father, sister }, age } = pigconsole.log(name, mother, father, sister, age)
4.forEach()方法
-
用于调用数组的每个元素,并将元素传递给回调函数
-
语法:
被遍历的数组.forEach(function () { })
const arr = ['red', 'green', 'pink']//不返回值,只遍历arr.forEach(function (item, index) {console.log(`当前元素:${item}`)console.log(`当前元素的索引:${item}`)})
5.filter方法
-
创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
-
使用场景:筛选数组中符合条件的元素,并返回筛选的新数组
-
语法
const str = [10, 20, 30]const newArr = str.filter((item, index) => {return item >= 20 //赶回[20,30]})console.log(newArr)