作用域
约 1681 字大约 6 分钟
2021-03-07
let、const、和 var 的区别?
- let 和 const 定义的变量不会出现变量提升,但是var定义的变量会提升
- let 和 const 是JS中的块级作用域
- let 和 const 不允许重复声明,重复声明会抛错
- let 和 const 定义的变量在定义语句之前,如果使用会抛出错误,而var不会。
- const 声明一个只读的常量。一旦声明,常量的值就不能改变(如果声明是一个对象,那么不能改变的是对象的引用地址,里边的值是可以改变的)
什么是变量提升?什么是暂时性死区?
变量提升(预解释)
变量提升也叫预解释。在当前作用域下,JS代码执行之前,浏览器首先会默认的把所有带var和function的进行提前声明或者定义,不管条件是否成立。
- 声明:var num;就是告诉浏览器在当前的作用域下有一个num的变量了,这时候属于声明了但未定义,所以num的值为undefined
定义:num = 12;给变量num进行赋值,num的值由undefined变为了12 - 对于带var和function关键字的在预解释时操作不一样,var在预解释时只是提前声明;而function在预解释时提前声明+定义都完成了(匿名函数不进行预解释)
- 预解释只发生在当前作用域下(开始的时候只对window下的进行预解释,当函数执行时才会对函数中的进行预解释)
暂时性死区
在代码块内,使用let/const命令声明变量之前,该变量都是不可用的(会抛出错误)。这在语法上,称为“暂时性死区”。暂时性死区也意味着typeof不再是一个百分百的操作。
typeof a; //暂时性死区,抛错 Cannot access 'c' before initialization
let a;typeof a; // 没有任何操作,直接typeof则是undefined,不会报错暂时性死区的本质是,只要一进入当前作用域,所有使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
如果正确判断this?ES6箭头函数的this是什么?
测试题目:
var number = 5;
var obj = {
number: 3,
fn1: (function () {
var number;
this.number *= 2;
number = number * 2;
number = 3;
return function () {
var num = this.number;
this.number *= 2;
console.log(num);
number *= 3;
console.log(number);
}
})()
}
var fn1 = obj.fn1;
fn1.call(null);
obj.fn1();
console.log(window.number);具体分析解析步骤如图: 
词法作用域和this的区别
- 词法作用域是由你在写代码时将变量和块级作用域写在哪里来决定的
- this是在调用的时候被绑定的,this指向什么,完全取决于函数的调用位置
谈谈你对JS执行上下文栈和作用域链的理解
执行上下文就是当前JavaScript代码被解析和执行时所在环境,JS执行上下文栈可以认为是一个存储函数调用的栈结构,遵循“先进后出”的原则。
- JavaScript执行在单线程上,所有的代码都是排队执行
- 一开始浏览器执行全局的代码时,首先创建全局的执行上下文,压入执行栈的顶部。
- 每当进入一个函数的执行就会创建函数的执行上下文,并且把它压入执行栈的顶部。当前函数执行完成后,当前函数的执行上下文出栈,并等待垃圾回收
- 浏览器的JS执行引擎总是访问栈顶的执行上下文
- 全局上下文只有唯一的一个,它在浏览器关闭的时候出栈
作用域链:无论是LHS还是RHS查询,都会在当前的作用域开始查找,如果没有找到,就会向上级作用域继续查找目标标识符,每次上升一个作用域,一直到全局作用域为止。
作用域
执行上下文汇总还包含作用域链。
作用域其实可以理解为该上下文中声明的 变量和声明的作用范围,可分为块级作用域和函数作用域
特性:
- 声明提前:一个声明在函数体内都是可见的,函数优先于变量。
- 非匿名自执行函数,函数变量为只读状态,无法修改。
(function foo() {
foo = 10; // 由于foo在函数中只为可读,因此赋值无效
console.log(foo); // ƒ foo() { foo = 10 ; console.log(foo) }
})();
// 对比
(function fo() {
foo = 10;
console.log(foo); //10
})();执行上下文(EC)
执行上下文可以理解为一个简单的对象
包含三个部分:
- 变量对象(VO)
- 作用域链(词法作用域)
- this指向
类型:
- 全局执行上下文
- 函数执行上下文
- eval执行上下文
代码执行过程:
- 创建全局上下文(global EC)
- 全局执行上下文(caller)逐行自上而下执行。遇到函数时,函数执行上下文(callee)被push到执行栈顶层。
- 函数执行上下文被激活,成为active EC,开始执行函数中的代码,caller被挂起。
- 函数执行完后,callee被pop移出执行栈,等待垃圾回收。并将控制权交还给全局上下文(caller),继续执行。
- 全局上下文继续执行,等到浏览器关闭的时候出栈。
堆和栈的区别?
空间分配
- 堆(操作系统): 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表;
- 栈(操作系统): 由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;
缓存方式
- 堆是存放在二级缓存中的,生命周期由虚拟机的垃圾回收算法决定(这里,并不是成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来的低一些;
- 栈使用的是一级缓存,它们通常都是被调用时处于缓存空间中,调用完毕立即释放;
结构
- 堆(数据结构):堆可以被看成是一棵树,如: 堆排序;
- 栈(数据结构):是一种先进后出的数据结构;