前言
js的作用域作为三大入门大山之一,其中蕴藏知识网络交错复杂。因为知识点多,所以可以借助测试题了解自己不足,再对应学习。内容也会不断更新内容的。。
JS的作用域
什么是作用域
作用域代表在那个范围内可以被使用,就比如中国的作用域就是整个中国,在这个范围内的变量(身份证)都可以使用。
作用域属性分类
- 词法作用域(js的主要作用域类型):在js代码编译时就确定了作用域的范围。
var a = 2
function foo() {
console.log(a) // 2
}
function bar() {
var a = 3
foo()
}
bar()
foo函数定义时,就确定了函数的作用域在全局中,不会因为在bar函数中运行,就改变作用域。
- 动态作用域 (了解):在js运行阶段才确定作用域的范围。
var obj = {a: 1, b: 2}
with (obj) {
a = 'is_a',
b = 'is_b'
c = 'is_c'
}
console.log(obj, c) // {a: 'is_a', b: 'is_b'}, 'is_c'
var a = 0
function foo(str, b){
eval(str) //严格模式不会改变作用域
console.log(a, b)
}
foo('var a = 1', 2) // 1, 2
作用域范围分类
-
全局作用域
在全局定义的变量或者在函数内部未使用var定义的变量都会分配到全局作用域中,自动挂载到window 的属性上面,并且 全局作用域中的变量不会执行垃圾回收过程 (全局污染)
function zxxFn () {
zxxs = 2
}
var zxx = 1
console.log('zxx' in window) //true
console.log(window.zxx) // 1
zxxFn()
console.log(zxxs) // 2
全局变量都会赋值到 window 上面
全局变量会在浏览器关闭时销毁
一般采用老生代的垃圾处理机制(了解)
-
函数作用域(局部作用域)
函数作用域是函数定义函数时生成的一个领域,在函数作用域里面的变量无法被函数外部访问(闭包除外),并且因为函数会在函数运行完毕时销毁函数作用域,使用函数作用域里面的变量会在函数运行完毕销毁。(可以使用某些方法让函数作用域不立即销毁)
var a = 0, b = 0, c = 0, d = 0; function fun1(){ var a = 1, b = 1, c = 1; function fun2(){ var a = 2, b =2 function fun3(){ var a = 3 console.log(a, b, c, d) // 3 2 1 0 } fun3() } fun2() } fun1()
变量在函数内声明,变量属于函数作用域。
一般在函数运行完毕,函数作用域就会关闭
函数作用域可以镶套
一般采用新生代的垃圾处理机制,销毁迅速(了解)
-
块级作用域
块级作用域和函数作用域差不多,只是两个作用的范围不一样,块级作用域是对应代码块的(大括号之间的内容),本身JS是不存在块级作用域的,但是在ES6里面出现了let、const产生块级作用域。
{ var i = 10 let b = 3 } console.log(i) //10 console.log(b) //报错
作用域的特点
-
变量提升(预处理)
在编译阶段要确定作用域首先要做的就是找到所有变量的声明,并利用相关机制将它们关联起来 ,在构建作用域时,会首先将var,function声明的变量或函数,进行变量提升处理,就是说提前声明。
a() function a(){ console.log(b) //undefined var b=10 } var a = undefined
补充:
| 概念 | 定义 |
| --- | :--- |
| 声明 | var a; 代表声明,现在的a还是undefined,但是告诉了编辑器存在a这个变量 |
| 赋值 | a=10; 对已经存在的变量存取值 |
- 通过var定义的变量会提升,而let和const进行的声明不会提升
- 通过function关键字定义的函数会被提升,而函数表达式则不会提升。
- 变量提示只是提升声明位置,并不会提升赋值,函数会提升整个函数
- 函数的提升大于变量的提升
- 对于同一个变量的多次提升,以最后一次为准备
扩展:为什么var变量可以声明多次,而let const只能声明一次?
var i=10
var i=20 //不会报错
let i=10
let i=20 //会报错
因为var存在变量提示,就算多个变量声明,也是按照最后一个为准。