jaavscript函数和变量的赋值

出处是大名鼎鼎的这里 感觉自己在这方面需要重练下.........

在javascript中, 所有变量和函数都是LexicalEnvironment这个特殊内部对象的属性.浏览器中,最顶层的LexicalEnvironmentwindow,它也叫全局对象.

全局变量的实例化

函数将要执行的时候, 有一个预处理阶段
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.

posted @ 2015-04-25 18:11  supersylph  阅读(352)  评论(0编辑  收藏  举报