[Day7] 提升

何谓提升(Hoisting)?

提升(Hoisting) 其实主要是为了厘清 JavaScript 的运作所提出的观念,在 ECMAScript 并未提出这个专有名词,而所谓的提升,就是先将函式变数宣告先提到程序码最上面的动作,再接着执行程序码

执行环境的阶段

JavaScript 会在执行环境中运行程序码,而在建立一个执行环境时可以分为两个阶段:

  • 创造阶段
    • 先将所有函式载入,且已可以运行
    • 再取出程序码中的变数,并在记忆体上预留变数空间,此时不会赋予变数值,系统会先给予一个初始值 undefined
  • 执行阶段
    • 依序执行程序码,此时可赋予变数值

在创造环境阶段优先把函式与变数的记忆体空间准备好,就叫做 提升 Hositing

变数的提升

同上面所述,在创造阶段时,会将程序码中所有变数先提到最前面,并在记忆体中预留空间给变数後,系统会先给予一个 undefined 的初始值,等到了执行阶段,会依序执行剩下的程序码,这时候才会赋予变数值,范例如下:

范例1:

var nickName = 'Carol';
console.log(nickName); // Carol

运行过程:

// 1. 创造阶段

// 先宣告变数 nickName,在记忆体中预留空间,预设值为 undefined
var nickName;

// 2. 执行阶段

// 赋予变数 nickName 值
nickName = 'Carol';

// 显示变数 nickName 值
console.log(nickName); // Carol

范例2:

  • 第一个 console.log(nickName);
    在创造阶段中,记忆体已预留空间给 变数 nickName,但还未赋予值,系统先给予一个预设值 undefined,所以显示 undefined

  • 第二个 console.log(nickName);
    到了执行阶段,程序码会依序执行,所以到了第二个 console.log,已赋予 变数 nickName 一个值,显示 Carol

console.log(nickName); // undefined
var nickName = 'Carol';
console.log(nickName); // Carol

运行过程:

// 1. 创造阶段

// 先宣告变数 nickName,在记忆体中预留空间,预设值为 undefined
var nickName;

// 2. 执行阶段

// 显示变数 nickName 值
console.log(nickName); // undefined

// 赋予变数 nickName 值
nickName = 'Carol';

// 显示变数 nickName 值
console.log(nickName); // 'Carol'

范例3:

变数 nickName 没被建立或是宣告,所以系统找不到此变数,故显示错误讯息 Uncaught ReferenceError: nickName is not defined

console.log(nickName);
// Uncaught ReferenceError: nickName is not defined

undefinedis not defined 差异可在 [Day6] 'undefined' vs 'not defined' 查看更多内容

函式的提升

关於函式的提升,首先我们要了解函式陈述式与函式表达式

函式陈述式

函式陈述式又称为具名函式,他会直接给函式一个名子,并可直接利用此名子呼叫函式,而在创造阶段时,函式陈述式优先,他会在记忆体中将函式陈述式的所有内容做保留

范例1:

function fn() { 
 // ...
}

fn();
运作过程:
// 1.创造阶段
function fn() { 
 // ...
}

// 2.执行阶段
fn();

范例2:

fn();
var nickName = 'Carol';
function fn() {
    console.log('我的昵称叫 Carol');
}
var nickName = 'Mary';
fn();
运作过程:
// 1.创造阶段
function fn() {
    console.log('我的昵称叫 Carol');
}

// 2.执行阶段
fn();

函式表达式

函式表达式可以将匿名函式或具名函式赋予至一个变数上。而函式表达式并不会像函式陈述式一样,在创造阶段就已经被记忆体保留空间,只会在创造阶段时将此变数在记忆体中保留空间,直到执行阶段才会将函式表达式赋予给此变数,所以若要利用函式表达式,就必须等函式已经赋予到变数上,才能运行此函式,因此只有函式陈述式会提升,而函式表达式则不会

若未定义函式名称则为匿名函式,反之则为具名函式

范例1:

var fn = function () { 
 // ...
}

fn();
运作过程:
// 1.创造阶段
var fn;

// 2.执行阶段
fn = function () { 
 // ...
}

fn();

范例2:

console.log(fn); // undefined

var fn = function () {
    console.log('我的昵称叫 Carol');
}

console.log(fn); // ƒ () { console.log('我的昵称叫 Carol'); }

fn(); // 我的昵称叫 Carol
运作过程:
// 1.创造阶段

var fn;

// 2.执行阶段

console.log(fn); // undefined

fn = function () {
    console.log('我的昵称叫 Carol');
}

console.log(fn); // ƒ () { console.log('我的昵称叫 Carol'); }

fn(); // 我的昵称叫 Carol

范例3:

因匿名函式还未赋予到变数上,所以还不能运行此函式,故会报错

fn();
var fn = function () {
    console.log('我的昵称叫 Carol');
}
// Uncaught TypeErroe: fn2 is not a function

运作过程:
// 1.创造阶段
var fn;

// 2.执行阶段

fn();
// Uncaught TypeErroe: fn2 is not a function
// 报错,所以下面的程序码不会继续执行

// fn2 = function () {
//     console.log('我的昵称叫 Carol');
// }

混和例子练习

范例1:

fn(); // 第 1 名

function fn() {
    console.log('第 1 名');
}

fn(); // 第 1 名

var fn = function () {
    console.log('第 2 名');
}

fn(); // 第 2 名

运作过程:

// 1.创造阶段
function fn() {
    console.log('第 1 名');
}

var fn;

// 2.执行阶段
fn(); // 第 1 名

fn(); // 第 1 名

fn = function () {
    console.log('第 2 名');
}

fn(); // 第 2 名

范例2:

fn(); // 第 1 名

var fn = function () {
    console.log('第 2 名');
}

fn(); // 第 2 名

function fn() {
    console.log('第 1 名');
}

fn(); // 第 2 名

运作过程:

// 1.创造阶段
function fn() {
    console.log('第 1 名');
}

var fn;

// 2.执行阶段
fn(); // 第 1 名

fn = function () {
    console.log('第 2 名');
}

fn(); // 第 2 名

fn(); // 第 2 名

范例3:

後面函式覆盖掉前面

fn(); // 第 2 名

function fn() {
    console.log('第 1 名');
}

fn(); // 第 2 名

function fn() {
    console.log('第 2 名');
}

fn(); // 第 2 名

运作过程:

// 1.创造阶段
function fn() {
    console.log('第 1 名');
}
function fn() {
    console.log('第 2 名');
}

// 2.执行阶段
fn(); // 第 2 名
fn(); // 第 2 名
fn(); // 第 2 名

范例4:

fn(); // undefined

function fn() {
    console.log(nickName);
}

var nickName = 'Carol';

fn(); // Carol

运作过程:

// 1.创造阶段
function fn() {
    console.log(nickName);
}

var nickName;

// 2.执行阶段
fn(); // undefined

nickName = 'Carol';

fn(); // Carol

范例5:

fn();
function fn() {
    if (rank) {
        rank = 2;
    }
}
console.log(rank); // undefined
var rank = 1;
console.log(rank); // 1
fn();
console.log(rank); // 2
// 1.创造阶段
function fn() {
    if (rank) {
        rank = 2;
    }
}

var rank;

// 2.执行阶段
fn();
console.log(rank); // undefined
rank = 1;
console.log(rank); // 1
fn();
console.log(rank); // 2

额外备注:

变数在宣告时,有一特性,如该变数已存在,则不会修正他 (只能用 var,因 letconst 不能重复宣告)

var a = 1;
var a;
console.log(a); // 1

参考文献

六角学院 - JavaScript 核心篇

MDN - 提升(Hoisting)


<<:  【第7天】训练模型-前置作业

>>:  Day21 密室逃脱之藏宝图

Day29-JDK可视化监控工具:visualVM(五)

前言 延续着上篇的介绍,这篇要来介绍visualVM的Sampler页签 Sampler 这边我延续...

[Day3] Jetpack Compose: 为什麽这个EditText不会动?

今天跑去面试新工作和准备下一阶段面试,十一点才想到要写,所以就意思意思一下XD,之後会回来补齐的Q...

.obj 之绘制 & Skybox

大家好,我是西瓜,你现在看到的是 2021 iThome 铁人赛『如何在网页中绘制 3D 场景?从 ...

javascript函式教学1

1.函式基础 我们经常使用alert();这可以叫出一个警告视窗,alert就是一个系统预先写好的程...

失控玩家 又名 脱稿玩家 free guy

失控玩家在线看 烂番茄指数 81%,观众指数 95%,《脱稿玩家》叫好又叫座成为 2021 今年好莱...