前面的话

  一般认为,javascript代码在执行时是由上到下一行一行执行的。但实际上这并不完全正确,主要是因为声明提升的存在。本文是深入理解javascript作用域系列第三篇——声明提升(hoisting)

变量声明提升

a = 2 ;
var a;
console.log( a );

  直觉上,会认为是undefined,因为var a声明在a = 2;之后,可能变量被重新赋值了,因为会被赋予默认值undefined。但是,真正的输出结果是2

console.log( a ) ;
var a = 2 ;

  鉴于上面的特点,可能会认为这个代码片段也会同样输出2。但,真正的输出结果是undefined

  所有这些和观感相违背的原因是在于编译器的编译过程

  第一篇介绍过作用域的内部原理。引擎会在解释javascript代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来

  包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理

var a = 2 ;

  这个代码片段实际上包括两个操作:var a 和 a = 2

  第一个定义声明是在编译阶段由编译器进行的。第二个赋值操作会被留在原地等待引擎在执行阶段执行

//对变量a的声明提升到最上面后,再执行代码时,控制台输出2
var a;
a = 2 ;
console.log(a);

  声明从它们在代码中出现的位置被“移动”到了最上面,这个过程就叫作提升(hoisting)

  [注意]每个作用域都会进行提升操作

console.log(a);
var a = 0;
function fn(){
console.log(b);
var b = 1;
function test(){
console.log(c);
var c = 2;
}
test();
}
fn();
//变量声明提升后,变成下面这样
var a ;
console.log(a);
a = 0;
function fn(){
var b;
console.log(b);
b = 1;
function test(){
var c ;
console.log(c);
c = 2;
}
test();
}
fn();

函数声明提升

  声明包括两种:变量声明和函数声明。不仅变量声明可以提升,函数声明也有提升操作

foo();
function foo(){
console.log(1);//1
}

  上面这个代码片段之所以能够在控制台输出1,就是因为foo()函数声明进行了提升,如下所示:

function foo(){
console.log(1);
}
foo();

  函数声明会提升,但函数表达式却不会提升

foo();
var foo = function(){
console.log(1);//TypeError: foo is not a function
}

  上面这段程序中的变量标识符foo被提升并分配给全局作用域,因此foo()不会导致ReferenceError。但是foo此时并没有赋值,foo()由于对undefined值进行函数调用而导致非法操作,因此会抛出TypeError异常

//变量提升后,代码如下所示:
var foo;
foo();
foo = function(){
console.log(1);
}

  即使是具名的函数表达式也无法被提升

foo();//TypeError: foo is not a function
var foo = function bar(){
console.log(1);
};
//声明提升后,代码变为:
var foo;
foo();//TypeError: foo is not a function
foo = function bar(){
console.log(1);
};

  [注意]函数表达式的名称只能在函数体内部使用,而不能在函数体外部使用

var bar;
var foo = function bar(){
console.log(1);
};
bar();//TypeError: bar is not a function

函数覆盖

  函数声明和变量声明都会被提升。但是,函数声明会覆盖变量声明

var a;
function a(){}
console.log(a);//'function a(){}'

  但是,如果变量存在赋值操作,则最终的值为变量的值

var a=1;
function a(){}
console.log(a);//1
var a;
function a(){};
console.log(a);//'function a(){}'
a = 1;
console.log(a);//1

  [注意]变量的重复声明是无用的,但函数的重复声明会覆盖前面的声明(无论是变量还是函数声明)

  【1】变量的重复声明无用

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

  【2】由于函数声明提升优先于变量声明提升,所以变量的声明无作用

var a;
function a(){
console.log(1);
}
a();//1

  【3】后面的函数声明会覆盖前面的函数声明

a();//2
function a(){
console.log(1);
}
function a(){
console.log(2);
}

  所以,应该避免在同一作用域中重复声明

深入理解javascript作用域系列第三篇的更多相关文章

  1. 深入理解javascript作用域系列第三篇——声明提升(hoisting)

    × 目录 [1]变量 [2]函数 [3]优先 前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javasc ...

  2. 深入理解javascript函数系列第三篇——属性和方法

    × 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...

  3. 深入理解javascript函数系列第三篇

    前面的话 函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本文是深入理解javascript函数 ...

  4. 深入理解javascript作用域系列第四篇——块作用域

    × 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...

  5. 深入理解javascript作用域系列第四篇

    前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀.简洁的 ...

  6. 深入理解javascript作用域系列第五篇——一张图理解执行环境和作用域

    × 目录 [1]图示 [2]概念 [3]说明[4]总结 前面的话 对于执行环境(execution context)和作用域(scope)并不容易区分,甚至很多人认为它们就是一回事,只是高程和犀牛书关 ...

  7. 深入理解javascript作用域系列第五篇

    前面的话 对于执行环境(execution context)和作用域(scope)并不容易区分,甚至很多人认为它们就是一回事,只是高程和犀牛书关于作用域的两种不同翻译而已.但实际上,它们并不相同,却相 ...

  8. 深入理解javascript对象系列第三篇——神秘的属性描述符

    × 目录 [1]类型 [2]方法 [3]详述[4]状态 前面的话 对于操作系统中的文件,我们可以驾轻就熟将其设置为只读.隐藏.系统文件或普通文件.于对象来说,属性描述符提供类似的功能,用来描述对象的值 ...

  9. 深入理解javascript作用域系列第一篇——内部原理

    × 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域 ...

随机推荐

  1. 使用JavaScript实现使用鼠标画线的效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. c++ poco 使用mysql中文乱码问题

    poco 是c++ 一个比较好的库,现在正在学习使用它,碰到一些问题记录在此. poco版本:poco-1.46-all ,带有数据库的支持模块 操作系统:ubuntu 1.使用poco的MySQL模 ...

  3. 微信小程序开发(三)项目目录及文件结构

    第二章我们已经创建了一个Hello WXapplet示例小程序.我们从文件目录结构来了解Hello WXapplet项目的构成. 目录结构显示,在小程序项目的根目录下面包含3个app开头的文件(app ...

  4. 数组A - 财务管理

    Larry graduated this year and finally has a job. He's making a lot of money, but somehow never seems ...

  5. 吐泡泡(2018年全国多校算法寒假训练营练习比赛(第二场)+栈模拟)+Plug-in(codeforces81A+栈模拟)

    吐泡泡题目链接:https://www.nowcoder.com/acm/contest/74/A 题目: 思路: 这种题目当初卡了我很久,今天早训时遇到一个一样得题,一眼就想到用栈模拟,就又回来把这 ...

  6. PHP对象5: define / const /static

    define定义全局常量: define('PATH', '/data/home/www'); const也是定义常量, 一般用于类中, 饰成员属性,不可以修饰方法,如下: class Test{ c ...

  7. kippo蜜罐搭建

    kippo蜜罐搭建 总结一下kippo蜜罐搭建的方法,centos系统.kippo-master.zip的安装包 (gcc,python-devel,pip,twisted==13.10,pycryp ...

  8. offset宏的讲解【转】

    转自:http://blog.csdn.net/tigerjibo/article/details/8299584 1.offset宏讲解 #define offsetof(TYPE, MEMBER) ...

  9. static作用(修饰函数、局部变量、全局变量)转自http://www.cnblogs.com/stoneJin/archive/2011/09/21/2183313.html

    static作用(修饰函数.局部变量.全局变量) 在C语言中,static的字面意思很容易把我们导入歧途,其实它的作用有三条. (1)先来介绍它的第一条也是最重要的一条:隐藏. 当我们同时编译多个文件 ...

  10. Machine Learning系列--L0、L1、L2范数

    今天我们聊聊机器学习中出现的非常频繁的问题:过拟合与规则化.我们先简单的来理解下常用的L0.L1.L2和核范数规则化.最后聊下规则化项参数的选择问题.这里因为篇幅比较庞大,为了不吓到大家,我将这个五个 ...