http://openwares.net/js/javascript_declaration_hoisting.html 可能要FQ一下javascript
javascript的变量声明具备hoisting机制,JavaScript引擎在执行的时候,会把全部变量的声明都提高到当前做用域的最前面。html
先看一段代码java
1
2
3
4
5
|
var
v =
"hello"
;
(
function
(){
console.log(v);
var
v =
"world"
;
})();
|
这段代码运行的结果是什么呢?
答案是:undefined
这段代码说明了两个问题,
第一,function做用域里的变量v遮盖了上层做用域变量v。代码作少些变更浏览器
1
2
3
4
5
|
var
v =
"hello"
;
if
(
true
){
console.log(v);
var
v =
"world"
;
}
|
输出结果为”hello”,说明javascript是没有块级做用域的。函数是JavaScript中惟一拥有自身做用域的结构。函数
第二,在function做用域内,变量v的声明被提高了。因此最初的代码至关于:工具
1
2
3
4
5
6
|
var
v =
"hello"
;
(
function
(){
var
v;
//declaration hoisting
console.log(v);
v =
"world"
;
})();
|
声明、定义与初始化this
声明宣称一个名字的存在,定义则为这个名字分配存储空间,而初始化则是为名字分配的存储空间赋初值。
用C++来表述这三个概念spa
1
2
3
|
extern
int
i;
//这是声明,代表名字i在某处已经存在了
int
i;
//这是声明并定义名字i,为i分配存储空间
i = 0;
//这是初始化名字i,为其赋初值为0
|
javascript中则是这样.net
1
2
|
var
v;
//声明变量v
v =
"hello"
;
//(定义并)初始化变量v
|
由于javascript为动态语言,其变量并无固定的类型,其存储空间大小会随初始化与赋值而变化,因此其变量的“定义”就不像传统的静态语言同样了,其定义显得可有可无。调试
声明提高
当前做用域内的声明都会提高到做用域的最前面,包括变量和函数的声明
1
2
3
4
5
6
|
(
function
(){
var
a =
"1"
;
var
f =
function
(){};
var
b =
"2"
;
var
c =
"3"
;
})();
|
变量a,f,b,c的声明会被提高到函数做用域的最前面,相似以下:
1
2
3
4
5
6
7
|
(
function
(){
var
a,f,b,c;
a =
"1"
;
f =
function
(){};
b =
"2"
;
c =
"3"
;
})();
|
请注意函数表达式并无被提高,这也是函数表达式与函数声明的区别。进一步看两者的区别:
1
2
3
4
5
6
7
8
9
|
(
function
(){
//var f1,function f2(){}; //hoisting,被隐式提高的声明
f1();
//ReferenceError: f1 is not defined
f2();
var
f1 =
function
(){};
function
f2(){}
})();
|
上面代码中函数声明f2被提高,因此在前面调用f2是没问题的。虽然变量f1也被提高,但f1提高后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。因此只有声明是被提高的。
名字解析顺序
javascript中一个名字(name)以四种方式进入做用域(scope),其优先级顺序以下:
一、语言内置:全部的做用域中都有 this 和 arguments 关键字
二、形式参数:函数的参数在函数做用域中都是有效的
三、函数声明:形如function foo() {}
四、变量声明:形如var bar;
名字声明的优先级如上所示,也就是说若是一个变量的名字与函数的名字相同,那么函数的名字会覆盖变量的名字,不管其在代码中的顺序如何。但名字的初始化倒是按其在代码中书写的顺序进行的,不受以上优先级的影响。看代码:
1
2
3
4
5
6
7
8
9
|
(
function
(){
var
foo;
console.log(
typeof
foo);
//function
function
foo(){}
foo =
"foo"
;
console.log(
typeof
foo);
//string
})();
|
若是形式参数中有多个同名变量,那么最后一个同名参数会覆盖其余同名参数,即便最后一个同名参数并无定义。
以上的名字解析优先级存在例外,好比能够覆盖语言内置的名字arguments。
命名函数表达式
能够像函数声明同样为函数表达式指定一个名字,但这并不会使函数表达式成为函数声明。命名函数表达式的名字不会进入名字空间,也不会被提高。
1
2
3
4
5
|
f();
//TypeError: f is not a function
foo();
//ReferenceError: foo is not defined
var
f =
function
foo(){console.log(
typeof
foo);};
f();
//function
foo();
//ReferenceError: foo is not defined
|
命名函数表达式的名字只在该函数的做用域内部有效。
===
在认识一切事物以后,人才能认识本身,由于事物仅仅是人的界限。 —— 尼采
http://blog.csdn.net/yueguanghaidao/article/details/9568071
一直对Js的做用域有点迷糊,今天偶然读到Javascript权威指南,立马被吸引住了,写的真不错。我看的是第六版本,至关的厚,大概1000多页,Js博大精深,要熟悉精通须要大毅力大功夫。
一:函数做用域
先看一小段代码:
(PS: console.log()是firebug提供的调试工具,很好用,有兴趣的童鞋能够用下,比浏览器+alert好用多了)
第一句输出的是: "undefined",而不是 "global"
第二讲输出的是:"local"
你可能会认为第一句会输出:"global",由于代码还没执行var scope="local",因此确定会输出“global"。
我说这想法彻底没错,只不过用错了对象。咱们首先要区分Javascript的函数做用域与咱们熟知的C/C++等的块级做用域。
在C/C++中,花括号内中的每一段代码都具备各自的做用域,并且变量在声明它们的代码段以外是不可见的。而Javascript压根没有块级做用域,而是函数做用域.
所谓函数做用域就是说:-》变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
因此根据函数做用域的意思,能够将上述代码重写以下:
咱们能够看到,因为函数做用域的特性,局部变量在整个函数体始终是由定义的,咱们能够将变量声明”提早“到函数体顶部,同时变量初始化还在原来位置。
为何说Js没有块级做用域呢,有如下代码为证:
都输出是“local",若是有块级做用域,明显if语句将建立局部变量name,并不会修改全局name,但是没有这样,因此Js没有块级做用域。
如今很好理解为何会得出那样的结果了。scope声明覆盖了全局的scope,可是尚未赋值,因此输出:”undefined“。
因此下面的代码也就很好理解了。
输出:2 ”ifscope"
二:变量做用域
仍是首先看一段代码:
就是上面的翻版,知识将声明s中的var去掉。
程序会报错仍是输出“ifscope"呢?
让我揭开谜底吧,会输出:”ifscope"
这主要是Js中没有用var声明的变量都是全局变量,并且是顶层对象的属性。
因此你用console.log(window.s)也是会输出“ifconfig"
当使用var声明一个变量时,建立的这个属性是不可配置的,也就是说没法经过delete运算符删除
var name=1 ->不可删除
sex=”girl“ ->可删除
this.age=22 ->可删除
三:做用域链
先来看一段代码:
当执行s时,将建立函数s的执行环境(调用对象),并将该对象置于链表开头,而后将函数t的调用对象连接在以后,最后是全局对象。而后从链表开头寻找变量name,很明显
name是"slwy"。
但执行ss()时,做用域链是: ss()->t()->window,因此name是”tlwy"
下面看一个很容易犯错的例子:
当文档加载完毕,给几个按钮注册点击事件,当咱们点击按钮时,会弹出什么提示框呢?
很容易犯错,对是的,三个按钮都是弹出:"Button4",你答对了吗?
当注册事件结束后,i的值为4,当点击按钮时,事件函数即function(){ alert("Button"+i);}这个匿名函数中没有i,根据做用域链,因此到buttonInit函数中找,此时i的值为4,
因此弹出”button4“。
四:with语句
说到做用域链,不得不说with语句。with语句主要用来临时扩展做用域链,将语句中的对象添加到做用域的头部。
看下面代码
with语句将person.wife添加到当前做用域链的头部,因此输出的就是:“lwy".
with语句结束后,做用域链恢复正常。