艾草:「我们今天来提升一下吧!」
「不是每天都在提升魔力总量了吗?」
艾草:「不一样唷,今天的提升更特别,我特别提倡身心灵的提升。」
(艾草一说完,我发现身体开始往上升了起来。)
「咦咦,啊啊,修但几勒,不是这种提升吧,我怕高~~~~~~呜啊啊啊啊」
艾草:「看样子这次的提升也会是你难忘的回忆呢!ಠﭛಠ」
is not defined
因为记忆体里没有变数 a
,自然会显示找不到变数 a
。
console.log(a); //Uncaught ReferenceError: a is not defined
undefined
a
变数已宣告但尚未赋值时,undefined
为预设代入的值,表示值还没被定义。
var a;
console.log(a);//undefined
理解了以上两者的差异後,来想想在变数宣告前先 console.log(a)
,会跑出哪个结果呢?
console.log(a);
var a = 10;
ReferenceError: a is not defined
undefined
如果依照程序码由上往下跑的特性,我们可能会猜测是答案 1,但正确解答为 2.undefined
!这其中的原因便是 Hoisting。
Hoisting 为 JavaScript 独有的现象,代表变数的宣告和函式的宣告会在创造阶段就被放入记忆体。
让我们复习一下执行环境吧!
JavaScript在运行前会先建立执行环境,第一个被建立的是全域执行环境 (Global Execution Context),而每个 function
也都会有自己独立的执行环境,会由下依序往上叠加。
那我们把环境建立好後,当然就是开始动工啦!
其实执行环境在创建後区分以下两阶段运行:
进入创造阶段时,JavaScript 会开始创造 Variable Object(VO),所谓的创建 VO 指的是会先搜寻此作用领域(scope) 内拥有的变数、函式,找到後会先将这些被宣告的变数、函式储存到记忆体,这就是提升的幕後黑手!
值得注意的点是需将变数分为宣告与赋值两阶段,仅会提升宣告阶段,不提升赋值阶段。
在此阶段会依造程序码一行一行去执行。
回到一开始的程序码,console.log(a)
显示 undefined
便是因为 a
在创造阶段 JavaScript 创造 VO 时,已经被存入记忆体空间了,而 a 的值尚未被赋予 10 这个值,是因为仅会提升宣告阶段,不提升赋值阶段。
小结:JavaScript 会在 EC 里创造阶段时先创造 VO ,这就是 Hoisting 的原因。
为何特别提到变数的宣告会在编译阶段就被放入记忆体呢?
console.log(a);
var a = 10;
我们可能会以为结果会是 10,但结果却是 undefined
。
那是因为在创造时会将已被宣告的变数先建立新的记忆体空间,但跑程序码的顺序还是由上往下。
可以理解为:
var varHoistingTest; //创造时提前建立 a 变数的记忆体空间
console.log(varHoistingTest);//undefined
a = 10;
既然是变数的宣告都会在创造阶段被放入记忆体那不论写 var
、let
、const
应该都是一样的吧?
console.log(varHoistingTest); //undefined
var varHoistingTest;
//let、const 的 Hosting 效果差不多,所以先用let来测试
console.log(letHoistingTest); //ReferenceError: letHoistingTest is not defined
let letHoistingTest;
咦!结果居然不一样难道是 let
跟 const
没有被提升吗?
NONONO,事情可没这麽简单!
var a = 10;
function letTest(){
console.log(a)
//ReferenceError: Cannot access 'a' before initialization at letTest
let a;
}
letTest()
如果今天 let
没有被提升的话,在查询时应该会果断回覆 10,但却显示了还没执行到 a
之前不能使用这个变数!
其实变数 var
、let
、const
都是有被提升的,差别在 var
预设会初始化一个值 undefined
,而 let
、const
并没有这个设定,且在初始化前它们不能够被使用,如果被使用会导致 Temporal Dead Zone(TDZ) 时间死区。
小结:只要是变数都有被提升,差别在 var
会自动初始化并赋予值 underfind
,但 let
、const
不会,在未对它们进行初始化前强行使用会导致时间死区TDZ。
这里复习一下定义函式的方式:
函式表达式 Function expression,通常会把它存成一个变数:
functionExpression(); //ReferenceError: functionExpression is not defined
var functionTest = function functionExpression(){
console.log("我没有被提升");
}
可以将程序码拆解成以下的范例,因为 VO 只提升宣告阶段,所以变数已存入记忆体空间,但变数 = function
这件事尚未被执行到,所以呼叫时会显示型态错误,并不是 function
。
var functionTest;
console.log(functionTest);//underfind
functionTest(); //TypeError: functionTest is not a function
functionTest = function functionExpression(){
console.log("我没有被提升");
}
函式陈述式 Function declaration,仅陈述状态,function
放在最前方的写法:
functionDeclaration();
function functionDeclaration() {
console.log('我被提升了');
}
在函式陈述式(函式宣告)时,我们即使提前呼叫程序码,依然能执行 function
内容,函式陈述式有 Hoisting 的效果。
argumentsTest(10); //20
function argumentsTest(number) {
return number + number;
}
带入在函式陈述式里的参数也会有提升的效果。
小结:函式陈述式及函式参数有提升的效果,但函式表达式没有。
现在我们已经知道变数、函式宣告都会有提升的效果了,那它们是谁优先呢?
变数 PK 函式
console.log(hoistingFirst);
var hoistingFirst = 123456789;
function hoistingFirst() {};
检查时会回传的是 function
而不是 undefined
,结果为 function
获胜!
var
宣告的变数,会先初始化被赋予undefined
let
、const
宣告的变数,提前取用会导致时间死区TDZhttps://blog.techbridge.cc/2018/11/10/javascript-hoisting/
https://medium.com/digital-dance/javascript执行环境-execution-context-简介-672185ed6bf4
https://blog.alexdevero.com/temporal-dead-zone-in-javascript/?ref=webdesignernews.com#declaration-and-initialization-differences-between-var-let-and-const
https://wcc723.github.io/javascript/2017/12/16/javascript-hoisting/
https://ithelp.ithome.com.tw/articles/10191549
<<: TailwindCSS 从零开始 - 价目表卡片实战 - 进阶卡片样式
【前言】 本系列为个人前端学习之路的学习笔记,在过往的学习过程中累积了很多笔记,如今想藉着IT邦帮忙...
前言 延续前篇Android Curv Gradient 曲线渐层 过了一个月...终於改好啦!!!...
前言 在React中通常我们并不会直接操作到DOM元素。 但有些情况反而需要操作DOM元素,来使使用...
「验收测试的目的是沟通、澄清及精确化。从专业开发人员的眼光来看,与业务方、测试方协同工作,确保大家...
今晚参与了"MakerPro社群媒体平台"举办的 在MCU 上全面建构AI能力 ...