函数声明提升

来源:https://dancon.gitbooks.io/git-books/content/js/essay/function_hoisting.html 

在ES5 中存在这样的事实,变量声明提升,函数声明提升。 在抛出这篇文章要讨论的问题之前,我们先来解释下这两个概念。

变量声明提升: 通过var声明的变量在代码执行之前被js引擎提升到了当前作用域的顶部。

函数声明提升: 通过函数声明的方式(非函数表达式)声明的函数在代码执行之前被js引擎提升到了当前作用域的顶部,而且函数声明提升优先于变量声明提升。

示例代码

1. js中变量没有声明就直接使用,是会导致引用错误的,如下:

console.log(a);

运行结果:

Uncaught ReferenceError: a is not defined(…)

2. 变量声明提升

console.log(a);

var a = 1;

运行结果:

undefined

没有报错,而是输出了undefined, 说明变量声明var a被提升到了作用域的顶部,console.log进行RHS引用时找到了变量a, 所以没有报错。

3. 函数声明提升

a();

function a() {

console.log('This is a function body');

}

运行结果:

This is a function body

函数的调用发生在函数声明之前,但是依旧正常执行。其实真正的函数声明只包含如下部分:

function () {

console.log('This is a function body');

}

这部分执行的结果就是创建了一个函数对象,我们假设为funcObj。

a只是指向funcObj的指针,函数声明提升,提升的应该是创建funcObj的过程,也就是上述的代码块。

在一些引擎的实现中,a也会赋值给funcObj对象上的一个name属性。 比如 a.name 会输出 a

4. 函数声明提升优于变量声明提升

a();

var a;

function a() {

console.log(1);

}

a = function() {

console.log(2);

}

a();

运行结果:

1

2

上述代码会被js引擎解析为如下:

function a() {

console.log(1);

}

a();

a = function() {

console.log(2);

}

a();

var a ;属于重复声明,被忽略掉了。然而后续的函数声明还是会覆盖之前的函数声明。如下:

a();

var a;

function a() {

console.log(1);

}

a = function() {

console.log(2);

}

function a() {

console.log(3);

}

运行结果:

3

所以得出如下猜想:

函数声明提升其实做了两个提升动作:

函数名变量提升,类似变量声明function a();

函数定义提升{ /*function body*/ }.

也就是说通过函数声明的方式定义一个函数会有以下三个步骤(或者说两个也可以,第三个可以归并到第二步中)(YY而已,不要当真):

声明函数名变量a

创建函数对象funcObj

把函数名变量a指向函数对象funcObj

而一般的函数声明提升会把这三个步骤都提升到作用域的顶部。

一般的函数声明提升的结果如上所述,那不一般的函数声明提升又会是什么样的呢?

问题如下:在chrome 49, FF 46, IE 11中运行如下代码都得出类似结果

(function(){

console.log(a);

if(false){

console.log(a());

function a(){

console.log('true');

}

}

a();

})();

chrome运行结果:

undefined

Uncaught TypeError: a is not a function

奇怪吧?!函数提升发生在所有代码执行之前,所以,尽管a 的定义过程写在了 if 分支中,但是理论上或者说ES6之前, 它是不会影响函数声明提升的,而现在,在作用域顶部console.log(a)输出undefined, 而执行a()发生TypeError。 我们稍作如下修改:

(function(){

console.log(a);

if(true){

console.log(a());

function a(){

console.log('true');

}

}

a();

})();

chrome运行结果:

undefined

true

true

再做一次修改:

(function(){

'use strict';

if(true){

console.log(a());

function a(){

console.log('true');

}

}

console.log(a);

})();

chrome运行结果:

true

Uncaught ReferenceError: a is not defined

现代浏览器的JS引擎已经支持块作用域了, 只是在非严格模式下,只有函数名变量声明会提升到当前闭包的顶部,这也是不管if 分支是否成立,在它之前console.log(a)都会输出undefined的原因。 而函数定义提升只提升到了if 的块作用域内,这就是在if 为真时,在if块内且在函数声明之前console.log(a())会输出true的原因。 但是在严格模式下, 函数名变量的提升也只提升到了if 块的顶部。这就是在严格模式下,在if 块外部对a 进行RHS引用是报 TypeError 的原因。

+

注意

在ES5中,严格模式下,函数的声明只能在全局作用域或者函数内,其他块(if, for)都会报错。

// 如下代码ES5中都会报错

if(1){

function a() {……} // 报错,ES6中不报错,是因为ES6支持了块作用域,所以在块作用域中声明函数是合理的。

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • 1. 变量声明提升 1.1 变量定义 可以使用var定义变量,变量如果没有赋值,那变量的初始值为undefined...
    cbw100阅读 414评论 0 3
  • JS引擎的工作方式:先解析代码,获取所有变量的声明。 变量提升 等价于 所以 等加于: 函数声明提升 当函数通过函...
    Hunter_Gu阅读 240评论 0 0
  • 函数声明和函数表达式有什么区别 (*)解析器会率先读取函数声明,并使其在执行任何代码之前可以访问;函数表达式则必须...
    coolheadedY阅读 383评论 0 1
  • let 命令 块级作用域 const 命令 顶层对象的属性 global 对象 let 命令 基本用法 ES6 新...
    嘉奇呦_nice阅读 1,623评论 0 2
  • ①海尼曼41-50点读,牛津树18-22点读 ②手指谣5首 ③小熊宝宝5本,宝贝计划5本,婴儿游戏绘本3本 ④婴儿...
    韦钰阅读 207评论 0 0