之前的面试中遇到过一道面试题

var a =10;
(function(){
console.log(a);
var a =20;}
)()
  • 短短5行代码log的结果是什么?
  • 如果把var a = 20;console.log(a)语句顺序对调呢?

这道题目的答案是undefined。不是10。

关键在于javascript的变量声明有一个hoisting机制,变量声明永远都会被提升至作用域的最顶端(注意测试还只是声明,还没有赋值)。其实上面的语句相当于:

var a =10;
(function(){
var a;//在这里对变量hoisting,先声明
console.log(a);
a =20;//再赋值
})()

再精简一点:

bla =2
var bla; // 这是分割线,上下代码的效果其实是一样的 var bla;
bla =2;

也就是先使用,再声明(注意是声明,还没有赋值),这样一来,声明和赋值就被分开来了。所以最佳实践都推荐最好在函数的顶端把需要使用的变量首先声明一遍。

同理,我们可以理解下面的代码也是会报错的

f()//明显这里有错,因为f还没有被赋一个函数
var f =function(){
console.log("Hello");
}

但有一个问题,如果将上例f的函数声明修改一下,还会报错吗

f()//可以运行吗?
function f(){
console.log("Hello");
}

这里我其实想强调的是两种函数声明的var f = function () {}function f() {}差别。

事实上,javascript中所有的函数声明(function declarations)和变量声明(variable declarations)都会被提升(hoisted)至它们所在作用域的最顶端。需要注意的是函数声明只有一种,也就是function f() {}的形式。而var f = function () {}是什么?你可以理解为它是将一个匿名函数(当然也可以取函数名,下面会解释)赋值给了一个变量。

就哪上面两个例子来说,同样是想实现先使用再定义的效果。只有第二种有用,虽然函数f在使用之后才定义,但是在javascript解释器中,它仍然是先于执行语句被定义的。

而第一个例子,执行的效果是这样的

var f;
f()//没有定义任何函数,当然无法执行
f =function(){
console.log("Hello");
}

这么看来,虽然javascript是允许先执行再声明,但切勿这么做,请遵循先声明再使用的好习惯。

再看看另一种情况,如果我把之前的函数定义

var f =function(){};
  1. 给右侧的匿名函数增加函数名
  2. 以右侧函数名来执行函数
  3. 能成功吗?
var f =function ab(){};
ab();

答案是否定的,因为上面的代码对f函数的定义是以命名函数表达式(Named Function Expressions),而并非真正的函数声明,注意该函数名只在该函数的作用域内有用。下面这段代码充分说明了它的意义:

var f =function foo(){
return typeof foo;
}; typeof foo;// "undefined"
f();// "function"

那么如此声明还有什么意义呢?好吧,就我目前找到的资料而言,这样做的好处就是便于调试。

接下来考虑一些意想不到的边缘,虽然我觉得一个程序员写出下面的代码有点自找苦吃,而且应该是在实战中避免的,但作为考试的题目来说是值得一说的。比如对比下面两段代码:

function value(){
return 1;
} var value;
alert(typeof value);//"function" function value(){
return 1;
} var value =1;
alert(typeof value);//"number"

第一段代码想说明的是函数声明会覆盖变量声明,注意是声明,还没有赋值。如代码中,虽然同名变量在函数后再次声明,但是typeof的结果仍然是function

第二段代码想说明的是函数声明不会覆盖变量赋值或者说初始化,如代码所示

Name Resolution Order

为什么会有上面的结果,为什么函数的声明会覆盖变量的声明。就是因为name resolution order。我不知道怎么翻译这个名词,暂且就翻译为名称解析顺序吧。

在javascript中,一个变量名(name)有四种方式进入作用域(scope)中

  • 语言内置,所有的作用域中都有thisarguments关键字
  • 形式参数,函数的参数在整个作用域中都是有效的
  • 函数声明
  • 变量声明

上面列出的四种顺序也正是由高到底的优先级的顺序(关于这点我有所保留,我测试的结果是参数和函数的优先级都会比语言内置的优先级高,你可以把形式参数取名为arguments,或者定义一个函数名为arguments,结果内置的argument说被覆盖了),一旦一个变量名已经声明了,那么它就不可能被其他更低优先级的变量声明形式所覆盖。

谈javascript变量声明的更多相关文章

  1. 浅谈JavaScript变量声明提升

    前段时间阿里实习生内推,一面就被刷了,也是郁闷.今天系统给发通知,大致意思就是内推环节不足以了解彼此,还可以参加笔试,于是赶紧再投一次.官网流程显示笔试时间3月31日,时间快到了,开始刷题.网上搜了一 ...

  2. javascript变量声明 及作用域

    javascript变量声明提升(hoisting) http://openwares.net/js/javascript_declaration_hoisting.html 可能要FQ一下 java ...

  3. 【转】javascript变量声明 及作用域

    javascript变量声明提升(hoisting) javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面. 先看 ...

  4. javascript变量声明及作用域总结

    javascript变量声明及作用域总结 一.总结 一句话总结:还是得好好看书,光看视频是不得行的,浅学无用,要相互印证,要真正理解才有用,比如<Javascript权威指南> 书 1.j ...

  5. JavaScript 变量声明提升

    JavaScript 变量声明提升 一.变量提升的部分只是变量的声明,赋值语句和可执行的代码逻辑还保持在原地不动 二.在基本的语句(或者说代码块)中(比如:if语句.for语句.while语句.swi ...

  6. JavaScript变量声明与变量声明提前

    JavaScript变量声明 JavaScript中存储数据的容器称为变量.用关键字和标识符创建新变量的语句,称为变量声明.可以通过关键字var进行变量声明,在ES6中增加了let.const关键字声 ...

  7. JavaScript变量声明提前

    上周四吃完午饭,leader发了一道JavaScript的题目给我们做,我们Team里面有做前端的,有做后台的,也有做mobile web的,所以大家对题目的理解各自都不一样,然后在QQ讨论组里面进行 ...

  8. JavaScript 变量声明提前

    <JavaScript权威指南>中指出:JavaScript变量在声明之前已经可用,JavaScript的这个特性被非正式的称为声明提前(hoisting),即JavaScript函数中声 ...

  9. JavaScript 变量声明:var、let、const

    1. 概述 1.1 说明 在ES5 声明变量的方法:var命令和function命令. 在ES6 声明变量的方法:var命令.function命令.let命令.const命令.import命令.cla ...

随机推荐

  1. Java基础二

    1 关键字 定义:被java语言赋予了特殊含义的单词. 特点:关键字中的所有字母都为小写. 用于定义数据类型的关键字 class.interface.byte.short.int.long.float ...

  2. Andrew Ng机器学习课程笔记--week5(上)

    Neural Networks: Learning 内容较多,故分成上下两篇文章. 一.内容概要 Cost Function and Backpropagation Cost Function Bac ...

  3. python+selenium自动化软件测试(第10章):测试驱动TDD

    测试驱动开发模式,要求开发在写业务代码的时候,先写出测试代码,同时单元测试例子决定了如何来写产品的代码,并且不断的成功的执行编写的所有的单元测试例子,不断的完善单元测试例子进而完善产品代码, 这样随着 ...

  4. linux(十)之初始化文件

    前面写了很多linux的知识,其实很多都是命令的,所以要去多多的练习才能学的更好,加油为了好工作. 要么现在懒惰,未来讨饭.要么现在努力,未来惬意. 一.初始化文件概述 1.1.概述 系统初始化文件是 ...

  5. docfx (一)

    什么是docFX? DocFX 是一个基于.NET的API文档生成器,当前支持 C# 和 VB.它可以通过你的代码中的三斜杠注释生成 API 参考文档.同样也支持你使用 Markdown 文件创建一些 ...

  6. Mybatis映射文件完整模板参照

    Mybatis映射文件完整模板参照 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE map ...

  7. 深入理解JVM(八)——java堆分析

    上一节介绍了针对JVM的监控工具,包括JPS可以查看当前所有的java进程,jstack查看线程栈可以帮助你分析是否有死锁等情况,jmap可以导出java堆文件在MAT工具上进行分析等等.这些工具都非 ...

  8. CGI + FastCGI(PHP-FPM)联系和区别的图解 + 注释

    一.背景 参考了几篇文章,总结成图解 + 注释方便以后查阅. 参考资料: 1.https://www.zhihu.com/question/19582041 2.https://segmentfaul ...

  9. js中防止全局变量被污染的方法

    (function ($){ var oDiv = $('div1');//获取id为'div1'的页面元素 })(function $(id){ return document.getElement ...

  10. CentOS7中将home迁移到/下的命令 CentOS7中将home迁移到/下的命令

    # mkdir -p /backup # cp -r /home/* /backup # umount /home #  df -hl # fdisk -l # lvremove /dev/cento ...