- 变量提升
- 调用栈
- 块级作用域
- 作用域链和闭包
闭包 => 作用域链(词法作用域) => 调用栈(栈溢出) => 上下文(全局,函数)(变量环境,词法环境,可执行代码,外部引用outer)=> 块级作用域(词法环境)
背景:
代码块1:
// 调用栈顺序
var a = 2
function add(b,c){
return b+c
}
function addAll(b,c){
var d = 10
result = add(b,c)
return a+result+d
}
addAll(3,6)
代码块2:
// 变量提升
var myname = "极客时间"
function showName(){
console.log(myname); // undefined
if(0){
var myname = "极客邦"
}
console.log(myname); // undefined
}
showName()
代码块3:
// 变量提升,变量i没有销毁
function foo(){
for (var i = 0; i < 7; i++) {
}
console.log(i);
}
foo()
代码块4:
// ES6 是如何做到既要支持变量提升的特性,又要支持块级作用域的呢?
function foo(){
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a)
console.log(b)
}
console.log(b)
console.log(c)
console.log(d)
}
foo()
代码块5:
// 作用域链:(词法作用域链)
function bar() {
console.log(myName) // 极客时间
}
function foo() {
var myName = "极客邦"
bar()
}
var myName = "极客时间"
foo()
代码块6:
// 其实在每个执行上下文的变量环境中,都包含了一个外部引用,
// 用来指向外部的执行上下文,我们把这个外部引用称为 outer
function bar() {
var myName = "极客世界"
let test1 = 100
if (1) {
let myName = "Chrome浏览器"
console.log(test) // 1
}
}
function foo() {
var myName = "极客邦"
let test = 2
{
let test = 3
bar()
}
}
var myName = "极客时间"
let myAge = 10
let test = 1
foo()
代码块7:
function foo() {
var myName = "极客时间"
let test1 = 1
const test2 = 2
var innerBar = {
getName:function(){
console.log(test1)
return myName
},
setName:function(newName){
myName = newName
}
}
return innerBar
}
var bar = foo()
bar.setName("极客邦")
bar.getName()
console.log(bar.getName())
WeChat06c38c6f237ee19bee668ed1febe23eb.png
变量提升 :
变量定义由 变量声明 和 变量赋值组成
如:var myname = '极客时间'
相当于:
var myname // 声明部分
myname = '极客时间' // 赋值部分
- 变量声明:
var myname // 声明部分
- 函数声明:
function foo(){
console.log('foo')
}
-
所谓的变量提升,是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值,这个默认值就是我们熟悉的 undefined。
-
从概念的字面意义上来看,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,正如我们所模拟的那样。但,并不准确。实际上变量和函数声明在代码里的位置是不会改变的,而且是在编译阶段被 JavaScript 引擎放入内存中。
JavaScript 代码的执行流程:
一段 JavaScript 代码在执行之前需要被 JavaScript 引擎编译,编译完成之后,才会进入执行阶段。大致流程你可以参考下图:
JavaScript 代码的执行流程
-
编译阶段
一段代码,经过编译后,会生成两部分内容:执行上下文(Execution context)和 可执行代码。 - 执行阶段
JavaScript 引擎开始执行“可执行代码”,按照顺序一行一行地执行。
调用栈 :
JavaScript 引擎是利用栈的这种结构来管理执行上下文的。在执行上下文创建好后,JavaScript 引擎会将执行上下文压入栈中,通常把这种用来管理执行上下文的栈称为执行上下文栈,又称调用栈。
image.png块级作用域 :
作用域 是指在程序中定义变量的区域,该位置决定了变量的生命周期。通俗地理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。
在 ES6 之前,ES 的作用域只有两种:全局作用域和函数作用域。
变量提升所带来的问题:
1、变量容易在不被察觉的情况下被覆盖掉;
2、本应销毁的变量没有被销毁;
image.png
其实,在词法环境内部,维护了一个小型栈结构,栈底是函数最外层的变量,进入一个作用域块后,就会把该作用域块内部的变量压到栈顶;当作用域执行完成之后,该作用域的信息就会从栈顶弹出,这就是词法环境的结构。
作用域链和闭包:
作用域链:
词法作用域就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能够预测代码在执行过程中如何查找标识符。
闭包:
在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。比如外部函数是 foo,那么这些变量的集合就称为 foo 函数的闭包。
代码块7
网友评论