JS Scoping and Hoisting
参考了这篇文章
http://www.jb51.net/article/30719.htm
var v='Hello World';
(function(){
console.log(v);
})() 输出:
Hello World
但是
var v='Hello World';
(function(){
console.log(v);
var v = 'hi';
})() 输出:
undefined
这里面隐藏了一个陷阱-----JavaScript中的变量提升(Hoisting).在JS中,就是把定义在后面的东东(变量或函数)提升到前面中定义。
首先看变量作用域(scoping),与C++不一样:
var x = ;
console.log(x); //
if (true) {
var x = ;
console.log(x); //
}
console.log(x);// 2
这是因为:
块,就像if语句,并不会创建一个新的作用域。只有函数才会创建新的作用域。
如果你必须在函数中创建一个临时的作用域,请像下面这样做:
function foo() {
var x = ;
if (x) {
(function () {
var x = 2;
// some other code
}());
}
// x is still 1.
}
变量提升,很简单,就是把变量提升提到函数的top的地方。我么需要说明的是,变量提升 只是提升变量的声明,并不会把赋值也提升上来。
比如我们定义三个变量:
(function(){
var a='One';
var b='Two';
var c='Three';
})()
实际上它是这样子的:
(function(){
var a,b,c;
a='One';
b='Two';
c='Three';
})()
这个时候就把变量提升了呀。
函数提升:
是把整个函数都提到前面去。
在我们写js code 的时候,我们有2中写法,一种是函数表达式,另外一种是函数声明方式。我们需要重点注意的是,只有函数声明形式才能被提升。
函数声明方式提升【成功】 代码如下: function myTest(){
foo();
function foo(){
console.log("我来自 foo");
}
}
myTest(); 结果:
我来自 foo
另一种函数表达式,就不行:
函数表达式方式提升【失败】
代码如下: function myTest(){
foo();
var foo =function foo(){
console.log("我来自 foo");
}
}
myTest(); 结果:
foo();
^
TypeError: foo is not a function
另外,下面文章里面还有几个例子
http://www.jb51.net/article/30718.htm
例子一:
var foo = ;
function bar() {
if (!foo) {
var foo = ;
}
console.log(foo);
}
bar(); 结果:
但是
var a = ;
function b() {
a = ;
return;
function a() {}
}
b();
console.log(a); 结果:
另外注意,把function a注释掉:
/**
* Created by baidu on 16/10/24.
*/
var a = ;
function b() {
a = ;
return;
//function a() {}
}
b();
console.log(a); 输出:
10
原来,function b里面的a没有加var,表示用的是全局变量。如果加上var,那结果会不一样:
var a = ;
function b() {
var a = ;
return;
//function a() {}
}
b();
console.log(a); 结果:
再回到上面第2段代码,其实是因为函数提升,把function a 提到了前面
var a = ;
function b() {
a = ;
return;
function a() {}
}
b();
console.log(a); 结果:
在JavaScript中,一个作用域(scope)中的名称(name)有以下四种:
1. 语言自身定义(Language-defined): 所有的作用域默认都会包含this和arguments。
2. 函数形参(Formal parameters): 函数有名字的形参会进入到函数体的作用域中。
3. 函数声明(Function decalrations): 通过function foo() {}的形式。
4. 变量声明(Variable declarations): 通过var foo;的形式。
函数声明和变量声明总是被JavaScript解释器隐式地提升(hoist)到包含他们的作用域的最顶端。很明显的,语言自身定义和函数形参已经处于作用域顶端。这就像下面的代码:
function foo() {
bar();
var x = ;
}
实际上被解释成这样:
function foo() {
var x;
bar();
x = ;
}
注意到声明的赋值部分并没有被提升(hoist)。只有声明的名称被提升了。
这和函数声明不同,函数声明中,整个函数体也都会被提升。
但是请记住,声明一个函数一般来说有两种方式。
function test() {
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { // 函数表达式被赋值给变量'foo'
alert("this won't run!");
}
function bar() { // 名为'bar'的函数声明
alert("this will run!");
}
}
test();
在这里,只有函数声明的方式会连函数体一起提升,而函数表达式中只会提升名称,函数体只有在执行到赋值语句时才会被赋值。
Name Resolution Order
需要记住的最最重要的特例就是名称解析顺序(name resolution order)。
上面列出的就是解析的顺序:
1. 语言自身定义(Language-defined): 所有的作用域默认都会包含this和arguments。
2. 函数形参(Formal parameters): 函数有名字的形参会进入到函数体的作用域中。
3. 函数声明(Function decalrations): 通过function foo() {}的形式。
4. 变量声明(Variable declarations): 通过var foo;的形式。
总的来说,如果一个名称已经被定义了,他绝不会被另一个拥有不用属性的同名名称覆盖。
这就意味着,函数声明比变量声明具有更高的优先级。但是这却不意味着对这个名称的赋值无效,仅仅是声明的部分会被忽略而已。
内置的名称arguments的行为有些怪异。他似乎是在形参之后,函数声明之前被声明。这就意味着名为arguments的形参会比内置的arguments具有更高的优先级,即使这个形参是undefined。这是一个不好的特性,不要使用arguments作为形参。
任何地方试图使用this作为一个标识都会引起语法错误,这是一个好的特性。
如果有多个同名形参,那位于列表最后的形参拥有最高的优先级,即使它是undefined。
Name Function Expressions
你可以在函数表达式中给函数定义名称,就像函数声明的语句一样。但这并不会使它成为一个函数声明,并且这个名称也不会被引入到作用域中,而且,函数体也不会被提升(hoist)。这里有一些代码可以说明我说的是什么意思:
foo(); // TypeError "foo is not a function"
bar(); // valid
baz(); // TypeError "baz is not a function"
spam(); // ReferenceError "spam is not defined"
var foo = function () {}; // 匿名函数表达式('foo'被提升)
function bar() {}; // 函数声明('bar'和函数体被提升)
var baz = function spam() {}; // 命名函数表达式(只有'baz'被提升)
foo(); // valid
bar(); // valid
baz(); // valid
spam(); // ReferenceError "spam is not defined"
How to Code With This Knowledge
现在你明白了作用域和提升,那么这对编写JavaScript代码意味着什么呢?最重要的一条是声明变量时总是使用var语句。我强烈的建议你在每个作用域中都只在最顶端使用一个var。如果你强制自己这么做,你永远也不会被提升相关的问题困扰。尽管这么做会使的跟踪当前作用域实际声明了哪些变量变得更加困难。我建议在JSLint使用onevar选项。如果你做了所有前面的建议,你的代码看起来会是下面这样:
/*jslint onevar: true [...] */
function foo(a, b, c) {
var x = ,
bar,
baz = "something";
}
我发现直接参考ECMAScript Standard (pdf)来理解这些东西是如何运作的总是很有用。下面是关于变量声明和作用域的一段摘录(section 12.2.2):
If the variable statement occurs inside a FunctionDeclaration, the variables are defined with function-local scope in that function, as described in section 10.1.3. Otherwise, they are defined with global scope (that is, they are created as members of the global object, as described in section 10.1.3) using property attributes { DontDelete }. Variables are created when the execution scope is entered. A Block does not define a new execution scope. Only Program and FunctionDeclaration produce a new scope. Variables are initialised to undefined when created. A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.
JS Scoping and Hoisting的更多相关文章
- 【repost】JavaScript Scoping and Hoisting
JavaScript Scoping and Hoisting Do you know what value will be alerted if the following is executed ...
- js 变量提升(JavaScript Scoping and Hoisting)
原文网址:http://www.cnblogs.com/betarabbit/archive/2012/01/28/2330446.html 这是一篇作者翻译过来的文章,未翻译的原文网址在这里:htt ...
- JavaScript Scoping and Hoisting
JavaScript中所有变量及函数名会自动提升 http://www.cnblogs.com/betarabbit/archive/2012/01/28/2330446.html
- 容易答错的JS笔试题
1,考察this var length = 10 function fn(){ alert(this.length) } var obj = { length: 5, meth ...
- 蛮考验基础的JS笔试题(有坑小心!)
1. 考察this var length = 10 function fn(){ alert(this.length) } var obj = { length: 5, method: functi ...
- JSHint Options 翻译
Enforcing options When set to true, these options will make JSHint produce more warnings about your ...
- JavaScript 语言精粹读书笔记
最近在看 赵泽欣 / 鄢学鹍 翻译的 蝴蝶书, 把一些读后感言记录在这里. 主要是把作者的建议跟 ES5/ES5.1/ES6 新添加的功能进行了对比 涉及到的一些定义 IIFE: Immediatel ...
- 每个JavaScript工程师都应懂的33个概念
摘要: 基础很重要啊! 原文:33 concepts every JavaScript developer should know 译文:每个 JavaScript 工程师都应懂的33个概念 作者:s ...
- 我希望我知道的七个JavaScript技巧
如果你是一个JavaScript新手或仅仅最近才在你的开发工作中接触它,你可能感到沮丧.所有的语言都有自己的怪癖(quirks)——但从基于强类型的服务器端语言转移过来的开发人员可能会感到困惑.我就曾 ...
随机推荐
- 如何撰写SCI论文的讨论部分?——经典结构 – 俗称“倒漏斗型。
- 无法解决 equal to 运算中 "Chinese_PRC_CI_AS" 和 "SQL_Latin1_General_CP1_CI_AS" 之间的排序规则冲突。
什么是排序规则(collation) 关于SQL Server的排序规则,估计大家都不陌生,在创建数据库时我们经常要选择一种排序规则(conllation),一般我们会留意到每一种语言的排序规则都有许 ...
- 亚马逊 在线测试题目 amazon
分析:其实就是求矩形中某一个点到其他点的距离加权最小 方法一: 对每一个点求其到其他点的加权距离,然后比较最小.由于有M*N个点,对每一个点求加权距离是O(M*N)的,所以整体时间复杂度是O(M*M* ...
- java集合之ArrayList的实现原理
1. ArrayList概述: ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部 ...
- 被 Windows 10 SDK 坑了
抓包抓到的配置文件与完整版的VS一致,虽然显示出来的只有 Windows 10 通用工具,我还以为是只更新这些(因为列出来的几个,我确实之前没装),就愉快地点了安装,结果……新建项目时,发现 Wind ...
- BZOJ 3224: Tyvj 1728 普通平衡树 vector
3224: Tyvj 1728 普通平衡树 Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除 ...
- JSTL标签库中fmt标签,日期,数字的格式化
首先介绍日期的格式化:(不要嫌多哦) JSTL格式化日期(本地化) 类似于数字和货币格式化,本地化环境还会影响生成日期和时间的方式. <%@ page pageEncoding="UT ...
- Linux中crontab的坑爹环境变量问题
手动在CentOS中执行sh脚本,调用java程序,一切正常: 将该sh加入crontab中定时调度之后,挂了,完全没有执行到的感觉啊!!! 查看crontab执行日志: cat /var/log/c ...
- ubuntu下搭建cocos2dx编程环境-下
前两篇介绍了cocos2d-x 下linux开发环境配置和android 环境配置问题.在这其中遇到很多问题,所以最后一篇分享一下在处理这些问题时,我是如何解决的,是怎么想的.同时总结一些解 ...
- 简单的自绘CListBox(多行显示)(覆盖DrawItem函数,然后用CDC绘制)
之前写过一个自绘的CListBox类,详细请参考http://blog.csdn.net/VisualEleven/archive/2010/10/12/5935430.aspx现在修改这之前的代码, ...