1. 作用域
作用域的含义:规定变量能够被访问的'范围",离开这个“范围”变量就无法被访问。
1.1 局部作用域:
局部作用域分为函数作用域和块作用域。
- 函数作用域
在函数内部声明的变量,只能在函数内部使用。
2.块作用域
在JavaScript中使用{ }包裹起来的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问
注意:
1.let声明的变量会产生块作用域,var不会产生块作用域。
2.const声明的常量也会产生块作用域。
(es6之后)
1.2 全局作用域
注意:
1.为Windows对象动态添加的属性默认也是全局的(不推荐)
2.函数中未使用任何关键字声明的变量为全局变量(不推荐)
3.尽可能少的声明全局变量,避免全局变量被污染
1.3作用域链
本质;底层的变量查找机制
- 在函数被执行时,会优先查找当前函数作用域中的变量
- 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域。
注意:子作用域可以访问父作用域,而父作用域无法访问子作用域。
1.4 垃圾回收机制(GC)
概念:js中内存分配和回收都是自动完成的,内存不使用时会被垃圾回收器自动回收
生命周期:
- 内存分配: 当我们声明变量、函数、对象的时候,系统会自动为它们分配内存
- 内存使用: 即读写内存,也就是使用变量,函数等
- 内存回收: 使用完毕,由垃圾回收器自动回收不再使用的内存
说明:
- 全局变量一般不回收(关闭页面回收)
- 一般情况下局部变量的值,不用了,就会自动回收。
内存泄露:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏
堆栈空间分配区别
1:栈(操作系统):由操作系统自动分配释放函数的参数值,局部变量等,基本数据类型放在栈中
2: 堆(操作系统):一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放在堆里面
垃圾回收算法
1.引用计数法
概念:E采用的引用计数算法,定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象
算法:
1.跟踪记录被引用的次数
2.如果被引用了一次,那么就记录为1,多次引用就累加++
3.如果减少一次引用就减1 --
4;如果引用次数为0,则释放内存
存在致命问题:嵌套引用(循环引用)
如果两个对象相互引用,尽管他们已经不再使用,垃圾回收器不会进行回收,导致内存泄露
2.标记清除法
核心:
- 将“不再使用的对象”定义为“无法达到的对象”
- 就是从根部(在js中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的
- 那些无法从根部出发触及的对象被标记为不可再使用,售后进行回收
1.5 闭包
概念:一个函数对周围状态的引用捆绑在一起,内存函数中访问到其外层函数的作用域
即:闭包 = 内存函数 + 外层函数的变量
闭包的作用:封闭数据,提供操作,外部也可以访问函数内部的变量
闭包的应用:实现数据的私有
闭包的风险:内存泄漏
1.6 变量提升(其实是个缺陷:面试可能问)
变量提升是JavaScript中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在var声明变量)
执行过程:
- 把所有var声明的变量提升到 当前作用域的最前面
- 只提升声明, 不提升赋值
总结:
- 变量在未声明即被访问时会报语法错误
- 变量在声明之前即被访问,变量的值为
undefined let声明的变量不存在变量提升,推荐使用let- 变量提升出现在相同作用域当中
- 实际开发中推荐先声明再访问变量
2.函数进阶
2.1 函数提升
执行过程:
- 会把所有函数声明提升到当前作用域的最前面
- 只提升函数声明,不提升函数调用
总结:
- 函数提升能够使函数的声明调用更灵活
- 函数表达式不存在提升的现象
- 函数提升出现在相同作用域当中
2.2 函数参数
1. 动态参数
arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。
总结:
arguments是一个伪数组arguments的作用是动态获取函数的实参- 可以通过for循环依次得到传递过来的实参
2.剩余参数
剩余参数允许我们将一个不定的参数表示为一个数组
使用场景:用于获得多余的实参
总结;
...是语法符号,置于最末函数形参之前,用于获取多余的实参- 借助
...获取的剩余实参,是个真数组
(开发中多提倡使用剩余参数) - 展开运算符(与剩余参数相似)
展开运算符(...),将一个数组进行展开
说明: 不会修改原数组
典型运用场景:求数组的最大值(最小值)、合并数组等
展开运算符和剩余参数:
剩余参数:函数参数使用,得到真数组
展开运算符:数组中使用,数组展开
2.3 箭头函数(重要)
目的:引入箭头函数是更简洁的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
使用场景:箭头函数更适用于那些本来需要匿名函数的地方
箭头函数是一种声明函数的简洁语法,它与普通函数并无本质的区别,差异性更多体现在语法格式上。
基本语法:()=>{ }
<script>
// 以前this的指向: 谁调用的这个函数,this 就指向谁
// console.log(this) // window
// // 普通函数
// function fn() {
// console.log(this) // window
// }
// window.fn()
// // 对象方法里面的this
// const obj = {
// name: 'andy',
// sayHi: function () {
// console.log(this) // obj
// }
// }
// obj.sayHi()
// 2. 箭头函数的this 是上一层作用域的this 指向
// const fn = () => {
// console.log(this) // window
// }
// fn()
// 对象方法箭头函数 this
// const obj = {
// uname: 'pink老师',
// sayHi: () => {
// console.log(this) // this 指向谁? window
// }
// }
// obj.sayHi()
const obj = {
uname: 'pink老师',
sayHi: function () {
console.log(this) // obj
let i = 10
const count = () => {
console.log(this) // obj
}
count()
}
}
obj.sayHi()
</script>
总结:
- 箭头函数属于表达式函数,因此不存在函数提升
- 箭头函数只有一个参数时可以省略圆括号
() - 箭头函数函数体只有一行代码时可以省略花括号
{},并自动做为返回值被返回
注意:
箭头函数中没有 arguments,只能使用 ... 动态获取实参
箭头函数的this
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this
3. 解构赋值
含义:解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值,分为数组解构、对象解构两大类型。
3.1 数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法
基本语法:
- 赋值运算符 = 左侧的[ ]用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
- 变量的顺序对应数组单元值的位置依次进行赋值操作
典型应用:交换变量
如下代码所示:
<script>
// 普通的数组
let arr = [1, 2, 3]
// 批量声明变量 a b c
// 同时将数组单元值 1 2 3 依次赋值给变量 a b c
let [a, b, c] = arr
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3</script>
拓展:两种加分号的情况
- 立即执行函数
(functioon ( ) { } ) ( ); // 防止连在一起
(functioon ( ) { } ) ( ); - 使用数组解构
<script>
// 1. 立即执行函数要加
// (function () { })();
// (function () { })();
// 2. 使用数组的时候
// const arr = [1, 2, 3]
const str = 'pink';
[1, 2, 3].map(function (item) {
console.log(item)
})
let a = 1
let b = 2
;[b, a] = [a, b]
console.log(a, b)
</script>
总结:
- 赋值运算符
=左侧的[ ]用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量 - 变量的顺序对应数组单元值的位置依次进行赋值操作
- 变量的数量大于单元值数量时,多余的变量将被赋值为
undefined - 变量的数量小于单元值数量时,可以通过
...获取剩余单元值,但只能置于最末位 - 允许初始化变量的默认值,且只有单元值为
undefined时默认值才会生效
注:支持多维解构赋值,比较复杂后续有应用需求时再进一步分析
3.2 对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法,如下代码所示:
<script>
// 普通对象
const user = {
name: '小明',
age: 18
};
// 批量声明变量 name age
// 同时将数组单元值 小明 18 依次赋值给变量 name age
const {name, age} = user
console.log(name) // 小明
console.log(age) // 18</script>
总结:
- 赋值运算符
=左侧的{ }用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量 - 对象属性的值将被赋值给与属性名相同的变量
- 对象中找不到与变量名一致的属性时变量值为
undefined - 允许初始化变量的默认值,属性不存在或单元值为
undefined时默认值才会生效
注:支持多维解构赋值
// 解构的语法
const { uname, age } = {age: 18, uname: 'pink老师' }
// 等价于 const uname = obj.uname
// // 要求属性名和变量名必须一直才可以
console.log(uname)
console.log(age)
// 1. 对象解构的变量名 可以重新改名 旧变量名: 新变量名
const { uname: username, age } = { uname: 'pink老师', age: 18 }
console.log(username)
console.log(age)
forEach():主要是遍历数组,例如:
<script>
// forEach 就是遍历 加强版的for循环 适合于遍历数组对象
const arr = ['red', 'green', 'pink']
const result = arr.forEach(function (item, index) {
console.log(item) // 数组元素 red green pink
console.log(index) // 索引号
})
// console.log(result)
</script>
forEach遍历数组
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数
注意:
1.forEach 主要是遍历数组
2.参数当前数组元素是必须要写的, 索引号可选。
filter筛选数组
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
主要使用场景: 筛选数组符合条件的元素,并返回筛选之后元素的新数组