作用域和作用域链

247 阅读5分钟

作用域是什么?

作用域就是变量与函数的可访问范围,它控制着变量和函数的可见性和生命周期。需要注意的是作用域在代码定义确定的而不是在调用的时候确定的。

全局作用域: 最外层函数和最外层函数外面定义的变量拥有全局作用域 所有未定义直接赋值的变量自动声明为全局作用域所有 window 对象的属性拥有全局作用域全局作用域有很大的弊端,过多的全局作用域变量会污染全局命名空 间,容易引起命名冲突。

函数作用域: 函数作用域声明在函数内部的变零,一般只有固定的代码片段可以访 问到 作用域是分层的,内层作用域可以访问外层作用域,反之不行。

块级作用域:使用 ES6 中新增的 let 和 const 指令可以声明块级作用域,块级作用 域可以在函数中创建也可以在一个代码块中的创建(由{ }包裹的代 码片段。

作用域链?

在当前作用域中查找所需变量,但是该作用域没有这个变量,那这个 变量就是自由变量。如果在自己作用域找不到该变量就去父级作用域 查找,依次向上级作用域查找,直到访问到 window 对象就被终止, 这一层层的关系就是作用域链。

作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,可以访问到外层环境的变量和函数。

作用域链的本质上是一个指向变量对象的指针列表。变量对象是一个 包含了执行环境中所有变量和函数的对象。作用域链的前端始终都是

当前执行上下文的变量对象。全局执行上下文的变量对象(也就是全 局对象)始终是作用域链的最后一个对象。

当查找一个变量时,如果当前执行环境中没有找到,可以沿着作用域 链向后查找。

JS是如何支持块级作用域?

在函数内部通过 var 声明的变量,在编译阶段全都被存放到变量环境里面了。通过 let 或者const声明的变量,在编译阶段会被存放到词法环境(Lexical Environment)中。在词法环境内部,维护了一个小型栈结构,栈底是函数最外层的变量,进入一个作用域块后,就会把该作用域块内部的变量压到栈顶;当作用域执行完成之后,该作用域的信息就会从栈顶弹出。

什么是词法作用域?

词法作用域是一种静态作用域,通过它就能够预测代码在执行过程中如何查找标识符。

var、const、let的区别是什么?

1.块级作用域: 块作用域由 { }包括,let和const具有块级作用域,var不存在块级作用域。块级作用域解决了ES5中的两个问题:内层变量可能覆盖外层变量,用来计数的循环变量泄露为全局变量。

2.变量提升: var存在变量提升,let和const不存在变量提升,即在变量只能在声明之后使用,否在会报错。

3.给全局添加属性: 浏览器的全局对象是window,Node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。

4.重复声明: var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const和let不允许重复声明变量。

5.暂时性死区: 在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区。

6.初始值设置: 在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值。

7.指针指向: let和const都是ES6新增的用于创建变量的语法。 let创建的变量是可以更改指针指向(可以重新赋值)。但const声明的变量是不允许改变指针的指向。

变量提升?

变量提升是指在JS代码执行过程中JS引擎会把变量、函数声明的部分提升到了代码开头位置的行为,变量被提升之后会给变量设置默认值undefined。

好处:提高性能,因为在代码执行会进行一次代码检查和预编译(只进行一次),这样就不需要在函数变量每次执行的时 候都再进行一次代码检查和重新解析。容错了提升。

坏处:内层变量可能覆盖外层变量,用来计数的循环变量泄露为全局变量导致本应该销毁的变量没有被销毁。

从目前ES6引入块级作用域和let、const变量声明关键字来看显然变量提升的弊大于利。

const对象的属性可以修改吗?

const保证的并不是变量的值不能改动,而是变量指向的那个内存地址不能改动。对于基本类型的数据(数值、字符串、布尔值),其值就保存在变量指向的那个内存地址,因此等同于常量。但对于引用类型的数据(主要是对象和数组)来说,变量指向数据的内存地址,保存的只是一个指针,const只能保证这个指针是固定不变的,至于它指向的数据结构是不是可变的,就完全不能控制了。