作用域
- 作用域规定了变量能够访问的“范围”,离开了这个范围变量就不能被访问
局部作用域
- 函数作用域:
- 函数内部声明的变量只能在函数内部被访问,外部无法直接访问
- 函数的参数也是函数内部的局部变量
- 不同函数内部声明的变量无法相互访问
- 函数执行完毕后,函数内部的变量实际被清空了
- 块作用域:在JavaScript中使用{}包裹的代码称为代码块,代码块内部声明的变量外部将有可能无法被访问
- let声明的变量会产生块作用域,var不会产生块作用域
- const声明的常量也会产生块作用域
- 不同代码块之间的变量无法相互访问
全局作用域
- script标签和.js文件的最外层就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问
- 全局作用域中声明的变量,任何其他作用域都可以被访问
作用域链
作用域链本质上是底层的变量查找机制
- 函数执行时,会优先在当前函数作用域中查找变量
- 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
- 子作用域能够访问父作用域,父作用域无法访问子作用域
垃圾回收机制
- JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收
- 内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫内存泄漏
JS垃圾回收机制-算法说明
- 堆栈空间分配区别:
- 栈(操作系统):由操作系统自动分配释放函数的参数值,局部变量等,基本数据类型放在栈中
- 堆(操作系统):一般由程序员释放,否则由垃圾回收机制回收。复杂数据类型放到堆里面
- 两种常见的垃圾回收算法:引用计数法和标记清除法
- 引用计数法已经不常用
- 标记清除法:
- 标记清除法的核心是:无法到达的对象
- 从根部(JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的
- 那些无法从根部触发触及到的对象被标记为不再使用,稍后进行回收
闭包
- 概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
- 简单理解:闭包 = 内层函数 + 外层函数的变量
- 作用:封闭数据,提供操作,外部也可以访问函数内部变
- 应用:实现数据的私有,例如我们要做个统计函数调用次数,函数调用一次,就++;
- 风险:可能导致内存泄漏
- 例题:力扣2666
- 闭包理解:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)
也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域
一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但创建时所在词法环境依然存在,以达到延长变量的生命周期的目的
变量提升
- 变量提升是JavaScript中比较奇怪的现象,它允许在声明变量之前被访问(仅存在于var声明变量);
- 变量提升的具体步骤:
- 先将所有var声明的变量提到当前作用域的最前面
- 只提升变量声明,不提升变量赋值
函数进阶
函数提升
- 概念:函数提升于变量提升类似,可以在函数声明前调用,因为函数提升将函数声明放到当前作用域的最前面,不提升函数调用
- 函数表达式不存在提升现象
函数参数
- 动态参数
- arguments是函数内部内置的伪数组变量,它包含了调用参数是传入的所有实参
- 剩余参数
- 剩余参数可以允许我们将一个不定数量的参数作为一个数组
- 两者的区别:
- …是语法符号,置于最末函数形参之前,用于获取多余的实参
- 借助…获取的剩余实参,是一个真数组,动态参数获取的数组是伪数组
展开运算符(…)
- 展开运算符可以展开数组,对象等,并且不会修改原数组
- 典型的运用场景:最大(小)值,合并数组
- 展开运算符和剩余参数的区别(…)
- 剩余参数:函数形参内使用,等到真数组
- 展开运算符:数组中使用,数组展开
函数箭头
- 目的:引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
- 使用场景:箭头函数适合更适用于那些本来需要匿名函数的地方
// 只有一个形参的时候可以省略小括号,
const fn = (x) =>{
console.log(x);
}
fn(100);
//只有一行代码的时候可以省略花括号{}
const fn = x =>{
console.log(x);
}
fn(1);
// 只有一行时可以省略return
const fn = x => x+x;
console.log(fn(1));
// 箭头函数可以直接返回一个对象,需要小括号包大括号
const fn = (uname) =>({uanme:uname});
cosole.log(fn("刘德华"));
箭头函数参数
- 普通函数有argument动态参数
- 箭头函数没有argument,但有剩余参数 …arr
箭头函数this
- this的粗略规则:this指向函数的调用者
- 箭头函数不会创建自己的this,他只会从自己的作用域链的上一层沿用this
解构赋值
- 数组解构是将数组的单元值(数组元素)快速批量赋值给一系列变量的简洁语法
- 基本语法
const arr = [100,60,80];
[max,min.avg]=arr;
// 典型运用场景---交换两个变量
let a = 1;
let b = 2;
[a,b] = [b,a];
- 可以利用剩余参数解决变量少,单元值多的问题
- 防止undefined传递
- 支持多为数组解构
- 对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
- 基本语法:
(1) 赋值运算符 = 左侧的{}用于批量声明变量,右侧对象的属性值将被赋给左侧的变量
(2) 对象属性的值将被赋值给与属性名相同的变量
(3) 注意解构的变量名不要和外面的变量名冲突,否则报错
(4) 对象中找不到与变量名一致的属性时变量值为undefined - 支持多级对象解构
- forEach 方法(重点)
- forEach方法用于调用数组的每个元素,并将元素传递给回调函数
- 主要使用场景:遍历数组中的每个元素
- 语法:被遍历数组.forEach(function (当前数组元素,当前元素索引号){
// 函数体
}) - 注意:forEach主要是遍历数组,参数里当前数组元素是必须要写的,索引号可选,无返回值,适合于遍历数组对象
案例拓展
- filter方法
- filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
- 主要使用场景:筛选数组中符合条件的元素,并返回筛选元素的新数组
- 注意:返回值是一个新数组,不会影响原数组,元素必须写,索引号可选