JS 执行环境
笔记性质
菜鸟初学,为了避免误人子弟,请大神留步勘误 QQ
建议搭配服用: JS Scope / Hoisting
执行环境的基础
以 Function 做为划分不同执行环境Function 于呼叫后,会先建立执行环境,才执行内部程式码只要有 call function 就会建立执行环境,包括递迴、whileLoop只有一个 Global Context (全域环境)执行环境的种类
全域 Global : 程式开始执行的预设环境函式 : 开始跑函式内部程式码的环境Eval把一串字串当作指令执行,会先将字串解析成JS,因此效能不佳 (ESLint 不建议使用)
执行环境 堆叠
Browser JS 为 单执行绪因此每次只能执行一件事,当一个事件在执行时,其他会被丢到执行伫列中。Event Loop 概念 (待整理....)
环境执行流程
Call Stack 特性为 后进先出会先建立 Global Context (全域执行环境) 于 Call Stack 当中执行 a()建立 a() 的执行环境,并放入 Call Stack当中等 a() 的内容执行完毕后,会从 CallStack 回收掉执行 Global Context 的 console.log()此段不懂可以去此看精美的图
function a() { var c = 1}a()console.log('123')
执行环境 内容
概念上我们可以把一个 执行环境 想像成一个物件内包含:
Scope Chain [注1]负责记录每个环境之间切换的关联
记录包含 自己的 VO + 所有上层执行环境的 VO 的集合。VariableObject (VO)
负责记录执行环境中定义的变数、函式、参数,
于建立阶段初始化,执行阶段给与値this
executionContextObject = { scopeChain: {}, variableObject: {}, this: {} }
建立执行环境的背后
建立 Global 全域环境执行 Function 前,建立执行该Function的环境 分为两阶段
建立阶段
建立阶段,初始化这个环境
除 arguments 外都只是先定义变数 & 函式指标,并没有赋值
进行 Hoisting
初始化 scope chain
判断决定 this 的值 (因此 this 在函式呼叫时才被决定)
建 variableObject
建 arguments object 并给予值
仅参数的值 会在建立阶段就给与检查传入参数,初始化参数的名称,值以及建立参考将 function 的宣告 加入 variableObject
函式宣告给予指标将 变数 的宣告 加入 variableObject
变数 / 函式宣告 初始化值为 undefined (没有赋值)执行阶段
给与値、设定 Function 的参考、逐行解译执行程式码
function foo(i) { var a = 'hello'; // 建立阶段 函式表达 不给与 值/指标 var b = function B() { }; // 建立阶段 函式宣告 给与指标 function c() { } } foo(22); // 建立阶段 / 执行阶段 fooExecutionContext = { scopeChain: { ... }, variableObject: { // arguments object // 0: 表第一个传入的参数 1: 表2...... //! 参数的值 会在建立阶段就给与。 arguments: { 0: 22, length: 1 }, i: 22, a: undefined, // 未给与値 b: undefined, // 函式运算 不给与値 c: pointer to function c(), // 函式宣告 给与指标 // 执行阶段才给与値 进行覆写建立阶段建立的变数 / 其他不变 a: 'hello', b: pointer to function B() }, this: { ... } }
建立阶段 Function 的不同行为
定义函式又分为三种
透过 new Function 关键字建立函式 (ESLint不建议,不讨论)a 函式运算式 Function Expression函式表达式,需要等待至执行阶段才会给与值。
var a = function () {}
b 函式宣告式 Function Declaration函式宣告会于建立阶段,赋予指标指向该 Function
function b() { }
实际範例
;(function () { // 建立阶段 a 为 函式运算式 未给值 undefined // 建立阶段 b 为 函式宣告 给予指标 pointer to function b() console.log(typeof a) // undefined console.log(typeof b) // function // 执行阶段 给与值 pointer to function a() var a = function () {} console.log(typeof a) // function // 执行阶段 给与值 进行覆写 var b = 123 console.log(typeof b) // number function b() { } // 执行阶段 给值 a : pointer to function a() // 执行阶段 给与值 b : 123 }())
建立阶段 函数宣告 & 变数宣告 的优先权
建立阶段: Hoisting 函数宣告 优先于 变数宣告 // Hoisting 函数宣告 优先于 变数宣告 (不论 Code相对位置) function a() {} var a // 并没有重新给值,因此 执行阶段此行不会执行 console.log(typeof a) // function // 于执行阶段给予值 var b = 1 function b() {} console.log(typeof b) // number
练习题
请解释
Why 可在宣告前存取 foo [Hoisting]Foo 被宣告 2 次,为什么 foo 是 function 而不是 undefined 或 string ?Why bar is undefined ? (function () { console.log("foo: " + typeof foo) var foo = 'hey' console.log("foo: " + typeof foo) function foo() { return 'hello' } console.log("bar: " + typeof bar) var bar = function() { return 'world' } console.log("bar: " + typeof bar) }())
Ans: 1.function / 2.string / 3.undefined / 4.function// 建立阶段: 1.函式宣告 给座标、3.函式运算 不给值// 执行阶段: 2. var foo = 'hey' foo 给值为 string// 执行阶段: 4. var bar = func~ bar 给值为 function
Why 可在宣告前存取 foo
在建立阶段我们就已经将变数建立了 [Hoisting]
Foo 被宣告 2 次,为什么 foo 是 function 而不是 undefined 或 string ?
第一个 log()
函式宣告 给与 指标 // foo: pointer to function foo()执行阶段 var foo = 'hey'
进行覆写建立阶段的变数,因此 foo: 'Hey'
第二个 log()
因为执行阶段会给于值,因此 foo 被改写成 str 'hey'
建立阶段还未给与値,因此为 undefined
执行阶段 才会给与値
C() 执行结果为何? Why?
var c = 1 function c() { console.log(typeof c) } console.log(c) c()
Ans: c is not a function console.log(c) // 1 // 建立阶段 Hoisting c = function pointer // 执行阶段给予值 先读 c = 1 后执行 c() 此时 c 给值为 1 // 因此 c is not a function
参考资料
andyyou
DavidShariff
PJChENder