变量
- 在js中有三种变量的定义方法,分别为var、let、const,其中let、const是es6中的语法
- var 非块级作用域、存在变量提升,又分为全局变量、局部变量
if( true ) {
var g = 10
}
console.log(g) // 输出10
// 等价于
var g
if (true) {
g = 10
}
console.log(g)
var a = 3 // 全局变量
function test () {
var a = 3 // 局部变量
b = 5 // 全局变量
}
- let变量
- 是块级变量,在块级内部有效
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
// ReferenceError: i is not defined
for (var i = 0; i < 10; i++) {
// ...
}
console.log(i);
// 10
- 不存在变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
- 暂时性死区
- 只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
- 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
// 示例1
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
// 示例2
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
- 不予许重复声明
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
function func(arg) {
let arg; // 报错
}
function func(arg) {
{
let arg; // 不报错
}
}
- const 只读的常量。一旦声明,常量的值就不能改变,基本类型和引用类型是有区别的,对于基本类型的值定义好了,更改值会报错,对于引用类型的变量,引用地址发生变更会报错
const PI = 3.1415926
PI = 3 // TypeError: Assignment to constant variable.
const arr = [1, 2, 3]
arr[0] = 1
arr = [1, 3, 5] // 报错
数据类型
- 基本数据类型:Boolean、Null(typeof Null 'object')、Undefined、Number[范围在-(2^63-1)到2^63-1),在实际应用中后台返回的id超过最大值,以整数的形式返回就会以科学计数法表示]、String、Symbol(es6 语法新定义)
- 引用类型:Object、Array,栈内存存取引用地址、对内存储数据。
执行上下文
-
执行上下文是评估和执行Javascript代码的环境的抽象概念。每当在JavaScript中运行任何代码时,它都在执行上下文中运行。
-
每个执行上下文都提供此关键字(this),该关键字(this)指向正在执行的当前代码所属的对象。
-
执行上下文分类
-
- 全局执行上下文 - 这是默认或基本执行上下文。不在任何函数内的代码位于全局执行上下文中。它执行两件事:它创建一个全局对象,它是一个窗口对象(在浏览器的情况下)并将其值设置为等于全局对象。程序中只能有一个全局执行上下文。
-
- function执行上下文 - 每次调用函数时,都会为该函数创建一个全新的执行上下文。每个函数都有自己的执行上下文,但是在调用或调用函数时会创建它。可以有任意数量的函数执行上下文。每当创建一个新的执行上下文时,它都会按照定义的顺序执行一系列步骤,我将在本文后面讨论。
-
- Eval函数执行上下文 - 在eval函数内执行的代码也有自己的执行上下文,但由于JavaScript开发人员通常不使用eval,所以我不在这里讨论它。
-
示例说明
var a = 10,
b = 20;
funntion fn (x) {
var a = 100,
c = 300;
function bar (x) {
var a = 1000,
d = 4000;
}
bar(100)
bar(200)
}
fn(10)

创建阶段
- this值确定(也称this值绑定)
- LexicalEnvironment 创建
- VariableEnvironment 创建
- 执行上下文如下所示
ExecutionContext = {
ThisBinding = <this value>,
LexicalEnvironment = { ... },
VariableEnvironment = { ... },
}
- 解析创建阶段的三个步骤
- this 绑定:在全局执行上下文中,this的值指的是全局对象。(在浏览器中,这指的是Window对象)。在函数执行上下文中,this的值取决于函数的调用方式。如果它由对象引用调用,则将其值设置为该对象,否则将其值设置为全局对象或未定义(在严格模式下)。
let person = {
name: 'peter',
birthYear: 1994,
calcAge: function() {
console.log(2018 - this.birthYear);
}
}
person.calcAge();
// 'this' refers to 'person', because 'calcAge' was called with //'person' object reference
let calculateAge = person.calcAge;
calculateAge();
// 'this' refers to the global window object, because no object reference was given
- Lexical Environment:词汇环境是一种包含标识符变量映射的结构。有两个组成部分:(1)环境记录和(2)对外部环境的引用。(此处标识符是指变量/函数的名称,变量是对实际对象[包括函数类型对象]或原始值的引用)。
-
- 环境记录:存储变量和函数声明的实际位置
- 1. Declarative environment record:函数和参数。功能环境包含声明性环境记录
- 2. Object environment record:环境记录用于定义在全局执行上下文中出现的变量和函数的关联。全局环境包含对象环境记录
-
- 外部环境的引用:对外部环境的引用意味着它可以访问其外部词汇环境
- 抽象的Lexical Environment如下
GlobalExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
}
outer: <null>
}
}
FunctionExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
}
outer: <Global or outer function environment reference>
}
}
- Variable Environment: 变量环境也是一个词汇环境,因此它具有上面定义的词法环境的所有属性。LexicalEnvironment组件和VariableEnvironment组件之间的一个区别是前者用于存储函数声明和变量(let和const)绑定,而后者用于仅存储变量(var)绑定。
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
GlobalExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
c: undefined,
}
outer: <null>
}
}
// 函数multiply调用时才会创建函数执行上下文
FunctionExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
g: undefined
},
outer: <GlobalLexicalEnvironment>
}
}
- 你可能已经注意到let、const申明的变量在创建阶段未初始化,而var声明的变量设置为undefined。这就是为什么你可以在声明变量之前访问var定义的变量(虽然是未定义的)但在声明变量之前访问let和const变量时会得到引用错误的原因,也是作用域提升的根本原因
Execution Phase(执行阶段)
- 在此阶段,完成对所有这些变量的分配,最后执行代码。
执行栈
-
执行堆栈,在其他编程语言中也称为“调用堆栈”,是具有LIFO(后进先出)结构的堆栈,用于存储在代码执行期间创建的所有执行上下文。
-
当JavaScript引擎首次遇到