JS作用域
作用域分类
静态作用域
如果函数中的变量,没有在该函数中定义,就去定义该函数的地方查找
静态作用域,也叫词法作用域,代码写完后,变量的作用域就已确定不变
动态作用域
如果函数中的变量们没有在该函数中定义,就去调用该函数的地方查找
动态作用域,代码写完后,变量的作用域还无法确定,它和调用它所在的函数有关
javascript使用静态作用域
函数作用域
- 变量声明
使用var声明的变量在声明它的函数体以及该函数体中的嵌套函数体内都是可以访问的。
函数内所有使用var声明的变量在函数体内可见。这意味着变量在声明之前就已经可用,这个特性就叫声明提前,即函数中的所有变量及函数声明都会提升至函数体的顶部。
- 函数声明
函数声明可以提前,但函数表达式是不会提前的。函数表达式相当于一个普通的变量,只是它的值是一个函数。
变量声明和函数声明的提升优先级
函数声明提前高于变量声明提前。
块级作用域
是指变量在指定块的作用域外部无法被访问,它位于一对花括号中。语法和var语句一样,只是它使用let或者const来声明。
禁止重复声明 如果变量在语句块中已经有定义,则无法再用let或者const进行重复声明,会报语法错误。
const声明
使用const声明的原始类型,是常量,之后不能修改,不然会报类型错误。使用const声明的对象类型变量,变量本身无法赋值为其他类型,但它的属性可以修改。const x = 1 // const x = {}
x = 2 // TypeError
const x = { }
x.age = 18 // 属性可以修改
全局块级绑定
使用var声明的变量会成为全局对象的属性,意味着可能会无意覆盖已存在的全局属性。
而用let和const声明的全局变量,不会添加到全局对象上。
最佳实践
- 优先使用const
- 如果之后会改变,则使用let
- 避免使用var
作用域链
在JS的最顶层代码中,作用域链由一个全局对象组成,这个全局对象可以通过this访问到。
在没有被嵌套的作用域中,作用域上有两个对象,第一个是包含局部变量的对象,第二个是全局对象。前者无法被JS代码访问,是不可见的内部实现。
作用域链延长
有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象在代码执行完后被移除。有两种情况:
- try-catch语句中的catch块
with语句
- catch语句会创建一个新的变量对象,它的值是被抛出的错误对象。在代码进入catch语句时,这个错误对象会被添加到作用域的顶端,当执行完catch语句时,该对象不再可用。
let e = 5
try {e = y
} catch (e) {
console.dir(e)
// message: "y is not defined"
// stack: "ReferenceError: y is not defined"
}
- with可将某个对象添加到作用域链的顶端,然后执行with语句块中的代码,执行完后会把作用域链恢复到原来的状态。
function f(foo, x, y) {
with (foo) {
console.log(x, y) // 1, 3
}
console.log(x, y) // 2, 3
}
f({ x: 1}, 2, 3)
还没有评论,来说两句吧...