JS Scope / Hoisting
笔记性质
建议搭配服用 : JS 执行环境
菜鸟初学,为了避免误人子弟,请大神留步勘误 QQ
範例
先来个範例,猜猜结果为何?
var foo = 1; function bar() { console.log('bar First',foo) if (!foo) { var foo = 10; } console.log('bar Last',foo); } bar();
bar First: undefinedbar Last: 10
题目解析
执行 bar() 建立执行环境创建阶段: 将 var foo 提升至最上,但不会给予值 (此时为undefined)执行阶段: log('first', foo) 得到 undefinedfoo 为 undefined (falsy) !fasle得到 true因此可执行 if 内容 给予 foo 值log('last',foo) 得到10不熟falsy可见此 var foo = 1; function bar() { var foo; // 这里改了 console.log('bar First',foo) // undefined if (!foo) { foo = 10; // 这里改了 } console.log('bar Last',foo); // 10 } bar();
Scope
ES5 为 函数级作用域(function-level scope)
ES5 (var) Scope 切割单位为 Function
因此 ES5 需要用 IIFE 来产生区域的 scope
ES6 以 {} 做划分,使用 let / const 即可 (Blocak-level scope)
Scope Chain 範围链
找寻变量的过程,由内往外找,直到全域环境。
注意 若在 Function 内 没有用 var 重新宣告,会产生全域变数
,进而变成改写全域变数中的 myVar
Outer Environment 外部环境
当该执行环境找不到该变量时,会往外部环境做参照
要如何区分 Outer Environment (外部环境) : 看程式码的相对位置
var myVar = 'Global'function a() { var myVar = 'Local' function b() { // b 的外层为 a() console.log(myVar) // Local } return b}// c 的外层为 Globalfunction c() { console.log(myVar) // Global}a()()c()
小测试
var x = 1 if (true) { var x = 2 let y = 2 console.log(x) // ? a console.log(y) // ? b } console.log(x) // ? c console.log(y) // ? d
Ans
a : 2 / b : 2 / c : 2 / d : y is not defined
ES5 var Scope 切割单位为 Function
因此 if else 中的 var x = 2 等同于 重新宣告了 x
Hoisting 观念
使用 var 时,不论是否被执行,变数宣告皆会被提升至环境的最上方执行环境的建立阶段,就已经将变数宣告加入该环境当中了,
即 执行环境的建立阶段,已进行Hoisting了
函式的 Hoisting
函式 宣告式 会整块程式码被 Hoisting,因此可于宣告前使用函式 表达式 则只有变数宣告的部分被 Hoisting,因此于宣告前呼叫,会回传 undefined,执行到该行时,才会进行给予值
foo() // foo is not a functionbaz() // baz is not a functionbar() // 可用,函式宣告式,整块被提升function bar() = {}var foo = function () {}; // 匿名函式表达式 (只有foo被提升)var baz = function spam() { // 命名函式表达式 (只有baz被提升) // spam 变数名称 只可以用于此区块 console.log(spam) // 指向此 Function}; // Scope Chain 只可往外找,无法往内找spam(); // ReferenceError "spam is not defined"// IIFE 同 命名函式表达式 概念;(function bzz() { var inSide = 123 console.log(bzz) // 指向此 Function}())// IIFE 为 Function , Scope Chain 只可往外找,无法往内找console.log(inSide) // inSide is not definedconsole.log(bzz) // bzz is not defined
练习题
function b() { console.log(myVar) // a var myVar console.log(myVar) // b}function a() { var myVar = 2 b() console.log(myVar) // c function d() { console.log(myVar) // d } d()}var myVar = 1console.log(myVar) // ea()
Ans
a : undefined / b : undefined / c : 2 / d : 2 / e : 1
a 因为 myVar 会在 b 中 hoisting 因此不会取到外部变数,
而是取到 var myVar; 的 undefined 值
总结 重要重点
ES5 Scope 切割单位为 Function
ES6 let const 为 Block Scope 因此只需要以 {} 划分
使用 var 时,不论是否被执行,变数宣告皆会被提升至环境的最上方
Hoisting 于 建立 执行环境 的 建立阶段 执行 (複习 JS 执行环境)
只会 Hoisting 宣告的部分,赋値仍要在执行阶段执行。
函式宣告式 : 会 Hoisting 整块程式码,因此可于宣告前使用
函式表达式 : 只会提升宣告的部分,赋値仍要在执行阶段执行。
参考资料
andyyou
DavidShariff
PJChENder
ben cherry