4 ES6
约 2411 字大约 8 分钟
2025-06-15
局部作用域
使用作用域符{}包裹的区域就是一个局部作用域,不在作用域范围内无法使用作用域里面的变量(var声明的变量除外) let和const声明的变量会产生块作用域,var不会产生块作用域
在js中允许函数嵌套定义
全局作用域
<script>标签和js文件的【最外层】就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。 全局作用域中声明的变量,任何其它作用域都可以被访问
- 为window对象动态添加的属性默认也是全局的,不推荐!
- 函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
- 尽可能少的声明全局变量,防止全局变量被污染
垃圾回收机制
垃圾回收机制(Garbage Collection)简称GC JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收
内存的生命周期 JS环境中分配的内存,一般有如下生命周期:
- 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
- 内存使用:即读写内存,也就是使用变量、函数等
- 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
全局变量一般不会回收(关闭页面回收) 一般情况下局部变量的值,不用了,会被自动回收掉
内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏
堆栈空间分配区别:
- 栈(操作系统):由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面。
- 堆(操作系统):一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面。
垃圾回收算法
两种常见的浏览器垃圾回收算法:引用计数法和标记清除法
引用计数法
IE采用的引用计数算法,定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象
算法:
- 跟踪记录被引用的次数
- 如果被引用了一次,那么就记录次数1,多次引用会累加++
- 如果减少一个引用就减1
- 如果引用次数是0,则释放内存
但它却存在一个致命的问题:嵌套引用月(循环引用) 如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。
标记清除法
现代的浏览器已经不再使用引用计数算法了。 现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。
核心:
- 标记清除算法将“不再使用的对象”定义为“无法达到的对象”
- 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。
- 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
闭包
一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
闭包=内层函数+外层函数的变量
简单的示例
function outer(){
const a=1
function f() {
console.log(a)
}
f()
}
outer()
函数之间嵌套是不会产生闭包的 里层函数用到了外层函数的变量对外层函数产生了捆绑就是闭包
闭包的常见用法是让其他外部函数使用另一个外部函数的内部变量
function outer(){
let a=100
function fn() {
console.log(a)
}
return fn
}
const fun = outer()
fun()//相当于调用fn
作用
可以避免让一个变量被外部修改\
例如做一个计数的函数
let count =1
function fn(){
count++
console.log(函数被调用${count}次)
}
fn() //2
fn() //3
这样写count就是一个全局变量容易被修改
使用闭包
function fn(){
let count =1
function Fun(){
count++
console.log(函数被调用${count}次)
}
return fun
}
const result = fn()
result()//2
result() // 3
这样count就是一个函数内部的变量避免了被外部修改,同时在外部调用闭包时也会对count继续累加而不是只在当前代码块内被访问 由于result变量使用了闭包函数,因此这个函数以及它内部的数据都不会被回收
变量提升
js程序在执行之前会把所有var声明的变量提升到当前作用域的最前面.只提升声明,不提升赋值
所以var定义的变量可以放在使用之后
函数
函数提升
函数提升与变量提升比较类似,是指函数在声明之前即可被调用,
js执行前会把所有函数声明提升到当前作用域的最前面.只提升函数声明,不提升函数调用
调用函数
foo()
//声明函数
function foo(){
console.log()'声明之前即被调用..')
}
但函数表达式不存在提升的现象,即使是赋值给var变量
//不存在提升现象
bar()// 错误
var bar = function () {
console.log()'函数表达式不存在提升现象...")
}
函数参数
动态参数
arugments arguments动态参数只存在于函数里面,是一个伪数组
function getSum(){
console.log(arugments) //[1,2,3]
}
getSum(1,2,3)
只有使用function定义的函数才有arguments,箭头函数没有
剩余参数
...arg 动态形参
function getSum(...arg){
for(e : arg){
console.log(e)
}
}
getSum(1,2,3)
剩余参数是一个真数组 剩余参数必须声明在形参列表最后
展开运算符
展开运算符也是... 它用来展开一个数组
const arr = [1, 5, 3, 8, 2]
console.log(...arr) // 1 5 3 8 2
不会修改原数组
合并数组
const arr1=[1,2,3]
const arr2=[4,5,6]
const arr=[...arr1,...arr2]
箭头函数
引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁 在箭头函数中没有this指针
箭头函数中没有arguments,但是又剩余参数
箭头函数用来取代(简化)匿名函数
const fn=(x)=>{
sonsole.log(x)
}
fn(1)
只有一行代码的时候可以省略大括号
const fn = x => console.log(x)
如果只有的一行代码是要返回值的也可以省略返回值
const fn = x => x+1
只有一行且返回值被小括号和大括号括起来的时候可以之间返回字面量
const fn =(uname) =>({name: uname})
console.log(fn('刘德华')) //name: '刘德华'
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。 也就是箭头函数中的this是它父亲的this
解构赋值
数组的解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法
const [a,b,c] = [1,2,3]
交换元素值的用法:
consta=1
constb=2;
[b, a] = [a, b]
但以数组开头的写法时要注意,尽量在 前面加分号隔开,不然js在解析时会解析错误
const a=1
const b=2
[b, a].map()
//会解析为
//const b=2[b, a].map()
所以js在立即执行函数和数组结构的这两种情况下必须加分号
变量多,单元值少 如果赋值的变量超过了数组元素的个数,那么超过部分就是undefined
const[a,b,c,d]=[1,2,3]
d就是undefined
为了避免undefined传递可以给数组结构的变量设置默认值,这样赋不到值得变量也会有默认值
const[a=0,b=0,c=0,d=0]=[1,2,3]
变量少,单元值多
const[a,b]=[1,2,3]
a=1,b=2
也可以使用剩余参数解决单元值比变量少的情况
const [a,b,...c] = [1,2,3,4]
这样c就是一个数组[3,4]
多维数组解构
const[a,b,c] =[1,2, [3, 4]]
c=[3,4]
可以多维解构
const[a,b,[c,d]] =[1,2, [3, 4]]
对象解构
将对象中的变量赋值给外部变量
const {uname,age}={uname,'pink老师',age:1888}
在解构时变量名要与对象的属性名一致,且前面不能有和对象属性名同名的const常量
要只解构想要的数据只写对应的属性名即可
const msg={
"code":200,
"msg":"获取消息列表成功,
"data":[{...}.{...}]
}
const {data}=msg
function render({data}){}
render(msg)
对象解构的变量名改名
const {uname:username,age}={uname:'pink老师',age:18}
forEach
forEach()方法用于调用数组的每个元素,并将元素传递给回调函数 主要使用场景:遍历数组的每个元素
被遍历的数组.forEach(function(当前数组元素,当前元素索引号){
//函数体
})
forEach可以更改数组中的值
贡献者
版权所有
版权归属:PinkDopeyBug