jaavscript函数和变量的赋值
出处是大名鼎鼎的这里 感觉自己在这方面需要重练下.........
在javascript中, 所有变量和函数都是LexicalEnvironment
这个特殊内部对象的属性.浏览器中,最顶层的LexicalEnvironment
是window
,它也叫全局对象
.
全局变量的实例化
函数将要执行的时候, 有一个预处理
阶段
1.首先,解释器从主代码流中找出所有函数声明, 从中生成函数,放入window
对象中, 例如
var a = 5;
function f(arg) { alert('f:'+arg) }
var g = function(arg) { alert('g:'+arg) }
在这步中, 浏览器找到function f
, 生成函数并且作为window.f
存储.
// 1.函数声明在代码被执行前初始化
// 2.所以在第一行代码前就有 window = { f: function };
var a = 5;
function f(arg) { alert('f:'+arg) }; // <--函数声明
var g = function(arg) { alert('g:'+arg) }
副作用就是, f可以在声明之前被调用
f();
function f() { alert('ok') }
2.然后, 解释器查找变量声明并生成相应window属性,这一步并没有给变量赋值.所有的变量值都是undefined.
// 1.代码执行前先初始化函数
// window = {f:function}
// 2.变量作为window的属性被添加进来
// window = {f:function, a:undefined, b:undefined}
// var a = 5; <-- var
function f(arg) { alert('f:'+arg) }
var g = function(arg) { alert('g:'+arg) } // <-- var
g的值是函数表达式, 但解释器不在乎这个. 它生成变量,但不给它们赋值.
总结:
1.函数在准备调用之前就预先声明了, 在函数声明语句之前调用函数是允许的.
2.变量初始是undefined
3.赋值是在初始化以后,执行到赋值语句时才发生的
副作用:无法生成同名的函数和变量.
3.函数开始运行. 变量或函数被读取时,解释器从window
中获取它们.
<!-- lang: js -->
alert("a" in window); // true, 因为window.a存在
alert(a); // undefined, 因为尚未赋值
alert(f); // function, 声明了函数
alert(g); // undefined, 尚未赋值
var a = 5;
function f(); { /*...*/ }
var g = function(); { /*...*/ }
4.在赋值之后, a的值是5,g是函数. 下面的代码中,alert被移下去了. 注意它们与上面代码的不同.
var a = 5;
var g = function(); { /*...*/ }
alert(a); // 5
alert(g); // function
如果变量没有用var
声明, 它就不会有初始化阶段. 解释器不会在一开始处理它.
alert("b" in window); // false, there is no window.b
alert(b); // error, b is not defined
b = 5;
但在赋值执行之后, b成为了变量window.b, 和进行var声明一样.
b = 5;
alert("b" in window); // true, there is window.b = 5
下题的答案?
if ("a" in window) {
var a = 1;
}
alert(a);
答案是1.让我们追踪下代码:
1.初始化阶段, window.a
生成
// window = {a:undefined};
if ("a" in window) {
var a = 1;
}
alert(a);
2."a" in window值为真;
// window = {a:undefined};
if (true) {
var a = 1;
}
alert(a);
所以执行后a的值是1.
下题的答案?
if ("a" in window) {
a = 1;
}
alert(a);
答案是 “Error: no such variable”,检查"a" in window
时不存在变量a, if分支没有执行.
函数
函数运行时, 新的LexicalEnvironment被创建, 并添加入参数, 变量, 和嵌套的函数声明. 和window
不一样, 函数的LexicalEnvironment
无法直接访问.
让我们思索下下面这个函数的执行细节.
function sayHi(name) {
var phrase = "Hi, " + name;
alert(phrase);
}
sayHi('John');
当解释器准备开始执行函数, 在第一行代码执行前, 会先生成空的LexicalEnvironment
并向内添加参数,局部变量和内部函数.
function sayHi(name) {
// LexicalEnvironment = { name: 'John', phrase: undefined }
var phrase = "Hi, " + name;
alert(phrase);
}
sayHi('John');
所以, 参数初始就有赋值, 变量没有.
2.函数运行时, 执行最终的赋值.
内部变量的赋值意味着LexicalEnvironment
中相应的属性获得了新值.
所以, phrase = "Hi, "+name
改变了LexicalEnvironment
.
最后一行alert(phrase)
查找LexicalEnvironment
中phase的值并输出.
特性说明
ECMA262事实上规定了2种对象.
第一个是VariableEnvironment
对象, 由函数和变量构成,经函数声明生成后它的指向就不可变了.
另一个是LexicalEnvironment
对象, 特性和VariableEnvironment
相似, 可能根据代码的控制流而改变.
更多详细描述可以在ECMA-262规定中找到.
在javascript执行中,这两个对象可以被合并为一. 所以我们回避了这些细节统一使用LexicalEnvironment
不存在块级作用域
下面两段代码其实没有区别:
var i = 1;
{
i = 5;
}
i = 1;
{
var i = 5;
}
在这两段代码中, 变量都预先在执行之前声明了. 和其他JAVA, C之类语言不同, 循环内的变量在循环外仍然有效.
javascript是函数作用域.
for(var i=0; i<5; i++) { }
alert(i); // 5, 变量存在并且有赋值
在循环中定义变量很方便,但不会生成局部变量.
function test() {
alert(window);
var window = 5;
}
test();
变量声明在在函数的预处理阶段就存在, window
在被alert前就已经是局部变量LexicalEnvironment = {window: undefined}
,因此函数执行到alert时, 变量window
已存在并且undefined.
var value = 0;
function f() {
if (1) {
value = 'yes';
}
else {
var value = 'no';
}
alert(value);
}
f();
函数预处理阶段, 变量声明就作为LexicalEnvironment
的属性. 所以函数被执行时, value='yes'
赋值给局部变量, 最终输出值是yes
.