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

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. [js高手之路]jquery插件开发实战-选项卡详解

    在jquery中,插件开发常见的有: 一种是为$函数本身扩展一个方法,这种是静态扩展(也叫类扩展),这种插件一般是工具方法, 还有一种是扩展在原型对象$.fn上面的,开发出来的插件是用在dom元素上面 ...

  2. Linux常见命令(五)——rmdir

      前  言 JRedu 今天我们来介绍第五个命令:rmdir . 命令英文原意:remove empty directories 命令用途: rmdir:删除空目录,非空的目录不能删除 本章内容将详 ...

  3. vue.js基础知识篇(1):简介、数据绑定

    目录第一章:vue.js是什么? 第二章:数据绑定第三章:指令第四章:计算属性第五章:表单控件绑定代码链接: http://pan.baidu.com/s/1qXCfzRI 密码: 5j79 第一章: ...

  4. [读书笔记] 三、搭建基于Spring boot的JavaWeb项目

    一.POM <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3. ...

  5. [2013-01-15]The Little Schemer 学习笔记

    <The Little Schemer> FP编程.lisp入门必备 这书貌似没中文版: 有英文pdf版:完整版下载链接 英文不好的,被前几页噎住的,可以先到这里看翻译好的前言部分 看完人 ...

  6. linux下操作mysql

    有关mysql数据库方面的操作,必须首先登录到mysql中. 开启MySQL服务后,使用MySQL命令可以登录.一般使用mysql -uroot -p即可.如果数据库不是本机,则需要加参数,常用参数如 ...

  7. 给你的网站免费配置上 HTTPS 证书

    现在越来越多的网站或服务增加了 HTTPS 证书,苹果 AppStore.微信小程序等也已强制要求开发者需提供 HTTPS 的后端接口.在阿里云 / 腾讯云上有一年期的免费赛门铁克 SSL 证书可供尝 ...

  8. Centos 7 PXE一键安装

    author:JevonWei 版权声明:原创作品 192.168.198.134作为安装服务器,由httpd服务共享安装程序 192.168.198.134作为dhcp服务器,客户机获取IP 一.安 ...

  9. import和require

    es6 的 import 语法跟 require 不同,而且 import 必须放在文件的最开始,且前面不允许有其他逻辑代码,这和其他所有编程语言风格一致. import不同与require,它是编译 ...

  10. Day-8: 面对对象编程

    面对过程的程序设计方法意在将函数分成子函数,再依次调用这些函数来解决问题. 而面对对象的程序设计方法,来源于自然界,类是实例的抽象,实例是类的具体.自定义出来的对象是类,而所有的数据都可以看成是对象, ...