JavaScript 执行
一段 JavaScript 经过编译会生成两部分:执行上下文,可执行代码。
在执行上下文中存在一个变量环境对象(Viriable Environment),保存了变量提升的内容。
简单的变量环境对象
VariableEnvironment:
myname -> undefined,
showName ->function : {console.log(myname)
JavaScript 代码先编译在执行
showName()
console.log(myname)
var myname = 'hello'
function showName() {
console.log('函数showName被执行');
}
- JS 引擎在环境对象中创建一个名为myname属性,并用
undefined
对其初始; - JS 引擎发现同 function 定义的函数,把函数定义存储在堆(HEAP)中,并在环境对象中创建一个 showName 的属性,然后将地址指向堆中的环境位置;
- JS 引擎会把声明外的代码编译为字节码
调用栈 call statck
调用栈是用来管理函数调用关系的一种数据结构
把这种用来管理执行上下文的栈成为执行上下文栈,又称调用栈
console.track()
查看调用栈
let 和 const
作用域 scope
作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。通俗地理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。
- 全局作用域中的对象在代码中的任何地方都能访问,其生命周期伴随着页面的生命周期
- 函数作用域就是在函数内部定义的变量或者函数,并且定义的变量或者函数只能在函数内部被访问。函数执行结束之后,函数内部定义的变量会被销毁
代码块内部定义的变量外部是访问不到的,代码块中的代码执行完成,代码块中定义的变量就会销毁。
S6 之前是不支持块级作用域的,因为当初设计这门语言的时候,并没有想到 JavaScript 会火起来,所以只是按照最简单的方式来设计。没有了块级作用域,再把作用域内部的变量统一提升无疑是最快速、最简单的设计,不过这也直接导致了函数中的变量无论是在哪里声明的,在编译阶段都会被提取到执行上下文的变量环境中,所以这些变量在整个函数体内部的任何地方都是能被访问的,这也就是 JavaScript 中的变量提升。
ES6 是如何做到既要支持变量提升的特性,又要支持块级作用域的呢?”
- 第一步是编译并创建执行上下文,下面是我画出来的执行上下文示意图,你可以参考下:
- 函数内部通过 var 声明的变量,在编译阶段全都被存放到变量环境里面了。
- 通过 let 声明的变量,在编译阶段会被存放到词法环境(Lexical Environment)中
- 在函数的作用域内部,通过 let 声明的变量并没有被存放到词法环境中。
作用域链
每个执行上下文的变量环境中,都包含一个外部引用,
用来指向外部的执行上下文,我们把这个外部引用成为outer.
词法作用域
词法作用域是指作用域是有代码中函数声明的
法作用域是代码阶段就决定好的,和函数是怎么调用的没有关系
词法作用域
词法作用域是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态作用域。词法作用域是代码阶段就确定好的,和函数执行无关。
闭包
function foo() {
var myName = "li"
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("liyunfei")
bar.getName()
console.log(bar.getName())
当执行到foo函数时的调用栈情况
foo函数执行上下文 =》变量环境, 词法环境
全局执行上下文 =》变量环境, 词法环境
闭包是怎么回收的
this
执行上下文:
变量环境、词法环境、outer、this;