通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
//1、全局变量
let num = 10
console.log(num) //10
function fn() {
console.log(num)
}
fn()
//2、局部变量
function fun() {
let num2 = 'linsir'
}
console.log(num2) //undefined
全局作用域:
<script> 标签 和.s 文件 的[最外层] 就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。全局作用域中声明的变量,任何其它作用域都可以被访问。
注意:
1为 window 对象动态添加的属性默认也是全局的,不推荐!
2.函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
3尽可能少的声明全局变量,防止全局变量被污染
局部作用域:
局部作用域分为函数作用域和块作用域。
1、函数作用域:
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
1.函数内部声明的变量,在函数外部无法被访问
2函数的参数也是函数内部的局部变量
3.不同函数内部声明的变量无法互相访问
4.函数执行完毕后,函数内部的变量实际被清空了
2、 块作用域:
在JavaScript 中使用什包裹的代码称为代码块,代码块内部声明的变量外部将[有可能]无法被访问。
1.let声明的变量会产生块作用域,var 不会产生块作用域
console.log(t) // 报错
2const声明的常量也会产生块作用域
3不同代码块之间的变量无法互相访问
4.推荐使用let或const
for(let i=1;i<=6;i++){
console.log(i) //正常运行
}
// 超出了 i 的作用域
console.log(i) //undefined
变量有一个坑,特殊情况:
如果函数内部,变量没有声明,直接赋值,也当全局变量看,但是强烈不推荐。但是有一种情况,函数内部的形参可以看做是局部变量。
let num = 20
function fn() {
num = 10 //全局变量来看,强烈不允许
}
fn()
console.log(num) //10
function fun(x, y) {
//形参可看作是函数的局部变量
console.log(x) // 1
}
fun(1, 2)
console.log(x) // false undefined
作用域链
作用域链本质上是底层的变量查找机制。
在函数被执行时,会优先查找当前函数作用域中查找变量
如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
总结:
1.嵌套关系的作用域串联起来形成了作用域链
2.相同作用域链中按着从小到大的规则查找变量
3子作用域能够访问父作用域,父级作用域无法访问子级作用域
变量的访问原则:
1)只要是代码,就至少有一个作用域。
2)写在函数内部的局部作用域
3)如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
访问原则:在能够访问到的情况下 先局部,局部没有在找全局
let num = 10
function fn() {
let num = 20
function fun() {
let num = 30
console.log(num) //30
}
fun()
}
fn()
let num = 10
function fn() {
let num = 20
function fun() {
// let num = 30
console.log(num) //20
}
fun()
}
fn()
垃圾回收机制
内存的生命周期:
JS环境中分配的内存,一般有如下生命周期:
1.内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
2.内存使用:即读写内存,也就是使用变量、函数等
3.内存回收: 使用完毕,由垃圾回收器自动回收不再使用的内存
说明:
全局变量一般不会回收(关闭页面回收)一般情况下局部变量的值,不用了,会被自动回收掉
内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏
拓展-JS垃圾回收机制-算法说明:
两种常见的浏览器垃圾回收算法:引用计数法和标记清除法。现代的浏览器已经不再使用引用计数算法了。现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的
核心:
1.标记清除算法将“不再使用的对象”定义为“无法达到的对象”
2.就是从根部(在S中就是全局对象)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的。3.那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收
函数案例:
需求:用户输入秒数 可以自动转换为时分秒
let second = +prompt('请输入秒数:')
// 封装函数
function getTime(t) {
//申明变量使成为局部变量
let h = parseInt(t / 60 / 60 % 24)
let m = parseInt(t / 60 % 60)
let s = parseInt(t % 60)
h = h < 10 ? '0' + h : h
m = m < 10 ? '0' + m : m
s = s < 10 ? '0' + s : s
return `转换完毕之后是${h}小时${m}分钟${s}秒`
}
let str = getTime(second)
document.write(str)