作用域&作用域链
作用域定义
-
MDN上说,作用域就是当前值和表达式在其中 “可见” 或可被访问到的上下文。
-
w3school上说,作用域指的是您有权访问的变量集合。
两个权威网站上对作用域的解释虽然不一样,但表达的意思差不多。
作用域分类
变量有三种作用域类型:全局作用域、局部作用域、es6块级作用域。(在 JavaScript 中,对象和函数也是变量。)
-
在函数外面定义的变量、非“严格模式”下未定义直接赋值的变量、window对象的属性都具有全局作用域;
-
局部作用域又称函数作用域,即函数内部定义的变量只拥有函数内部的局部作用域,函数参数也是函数内的局部变量,拥有局部作用域。
-
块级作用域是指在非函数的{}里用let定义的变量只拥有{}内部的块级作用域。
不同作用域变量的有效期
JavaScript 变量的有效期始于其被创建时。
局部变量会在函数完成时被删除。
全局变量会在您关闭页面是被删除。
作用域链
作用域链会把作用域连接起来,作用域链是对作用域中的变量进行访问,是访问的规则。如果在函数A里声明了一个函数B,那么在B创建的时候就会引用A的作用域,这样就组成了所谓的函数作用域链。当在自身作用域内找不到该变量的时候,会沿着作用域链逐步向上查找,若在全局作用域内部仍找不到该变量,则会抛出异常。
如何运用作用域链的知识进行性能优化?
作用域链越深,读写速度就会越慢,查找全局变量更慢,所以在编写代码的时候应:
尽量少用全局变量,应尽可能使用局部变量。
如果一个跨作用域的变量被引用了一次以上,应该把它先存储为局部变量再使用。
上下文(执行上下文)
当执行 JS 代码时,会产生三种执行上下文
- 全局执行上下文
- 函数执行上下文
- eval 执行上下文
每个执行上下文中都有三个重要的属性
- 变量对象(VO),包含变量、函数声明和函数的形参
- 作用域链(JS 采用词法作用域,也就是说变量的作用域是在定义时就决定了)
- this
变量提升
JS 变量的三阶段「创建create、初始化initialize 和赋值assign」
- 创建:为变量a开辟内存空间
- 初始化:a=undefined
- 赋值:a=1
简单来说,变量提升是一种把创建/初始化/赋值中的某一阶段的变量移动到作用域最前面的机制。(js中对象和函数也是变量)
举例来说,
var a = 1;
console.log(a);// 1
console.log(b);// undefined
var b=2;
复制代码
当a、b被声明时,同时被提升到了作用域的顶端,所以相当于:
var a=undefined;
var b=undefind;
a = 1;
console.log(a);
console.log(b);
b=1;
复制代码
因此,打印出的是undefined。
当
my();// 1
function my(){
var b = 1;
console.log(b);
}
复制代码
此处会打印出1是因为:在js中,当函数被声明时他就被提到了作用域的顶端,而且是整个函数都被提升了,因此,my()函数执行无误。
let、const、var、function分别提升了哪一阶段的变量?
let 的「创建」过程被提升了,但是初始化没有提升。(很多人把这种情况叫做let的暂存性死区)
const 的「创建」过程被提升了,但是初始化没有提升。
var 的「创建」和「初始化」都被提升了。
function 的「创建」「初始化」和「赋值」都被提升了。