JS变量初始化过程

  1. 顶级变量初始化
  2. 函数变量
  3. 没有scope的块

JavaScript函数与变量机制与大部分语言完全不同,在JS中,所有的局部变量和函数都是特定内部对象的属性,即LexicalEnvironment(LE)

在浏览器中顶级的LE是window对象,也叫做全局变量

顶级变量初始化

当js即将执行时,有一个预处理阶段叫做变量实例化

  1.首先,解释器扫描Function Declarations,也就是function name{},解释器将对每个声明创建一个函数并作为Window的一个变量

      

var a = 5

function f(arg) { alert('f:'+arg) }

var g = function(arg) { alert('g:'+arg) }

  此时,浏览器发现函数f,于是创建Function,将其存为window.f:

// 1. Function Declarations are initialized before the code is executed.
// so, prior to first line we have: window = { f: function }

var a = 5

function f(arg) { alert('f:'+arg) } // <-- FunctionDeclaration

var g = function(arg) { alert('g:'+arg) }

  而这也导致,f可以在声明之前调用

f()
function f() { alert('ok') }

  2.解释器扫描var declarations,作为window的属性。但是此时变量并没有被赋值,所有的变量此时都是undefined。

// 1. Function declarations are initialized before the code is executed.
// window = { f: function }

// 2. Variables are added as window properties.
// window = { f: function, a: undefined, g: undefined }

var a = 5   // <-- var

function f(arg) { alert('f:'+arg) }

var g = function(arg) { alert('g:'+arg) } // <-- var

 虽然g是一个函数表达式,但是解释器不关心这些,只要使用var声明,都会在此阶段处理,这也导致变量不能和函数同名

总结一下:

 

  1. FunctionDeclarations become ready-to-use functions. That allows to call a function before it’s declaration.
  2. Variables start as undefined.
  3. All assignments happen later, when the execution reaches them.

  3.代码执行

  当变量活函数被访问时,解释器从window中获取

 

alert("a" in window) // true, because window.a exists
alert(a) // undefined, because assignment happens below
alert(f) // function, because it is Function Declaration
alert(g) // undefined, because assignment happens below

var a = 5  

function f() { /*...*/ } 
var g = function() { /*...*/ }

  5.赋值过后,a=5,g成为了函数,注意下面代码的区别:

var a = 5  
var g = function() { /*...*/ } 

alert(a) // 5
alert(g) // function

    如果变量声明时未用var,那么在初始化时就不会被创建(或者说成为window的属性),解释器看不到的:

alert("b" in window) // false, there is no window.b
alert(b) // error, b is not defined

b = 5

但赋值之后,b成为window的全局变量:window.b

b = 5 

alert("b" in window) // true, there is window.b = 5

考虑一下:下面代码的执行结果:

if ("a" in window) {
    var a = 1
}
alert(a)
// window = {a:undefined}

if ("a" in window) {
    var a = 1
}
alert(a)
View Answer
if ("a" in window) {
    a = 1
}
alert(a)
The answer is “Error: no such variable”, because there is no variable a at the time of "a" in window check.

So, the if branch does not execute and there is no a at the time of alert.
View Answer

函数变量

当函数执行时,每一个函数调用都会产生新的包含参数、变量、嵌套函数声明的LexicalEnvironment 

变量是内部用来读写变量的,和window不同,函数的LexicalEnvironment 是不能直接访问的

下面我们看一下函数执行的细节:

function sayHi(name) {
  var phrase = "Hi, " + name
  alert(phrase)
}

sayHi('John')

  1.在解释器开始执行函数代码时,会创建一个填充参数、局部变量、嵌套函数的LexicalEnvironment ,很显然参数具有初始值,而局部变量则是未定义的

  2.函数代码开始执行,最后执行赋值语句。内部的变量赋值意味着LexicalEnvironment相应的属性获得了新值

so,phrase="Hi, "+name 改变了LexicalEnvironment 

function sayHi(name) {
// LexicalEnvironment = { name: 'John', phrase: undefined }
  var phrase = "Hi, " + name
// LexicalEnvironment = { name: 'John', phrase: 'Hi, John'}
  alert(phrase)
}

sayHi('John')

最后一行alert(phrase)从LexicalEnvironment 搜获phrase

  3.函数执行之后,LexicalEnvironment会废弃他的内容,因为这些变量不再需要,但是情况并不总是这样:

Specification peculiarities
If we look into the recent ECMA-262 specification, there are actually two objects.

The first is a VariableEnvironment object, which is actually populated by variables and functions, declared by FunctionDeclaration, and then becomes immutable.

The second is a LexicalEnvironment object, which is almost same as VariableEnvironment, but it is actually used during the execution.

A more formal description can be found in the ECMA-262 standard, sections 10.2-10.5 and 13.

It is also noted that in JavaScript implementations, these two objects can be merged into one. So, we evade irrelevant details and use the term LexicalEnvironment everywhere.

  

没有scope的块

下面的代码没有任何区别

var i = 1
{
  i = 5
}

 

i = 1
{
  var i = 5
}

 所有的变量声明在块执行之前就已经初始化

这一点和Java、C++不用,在JS中,变量在循环之后依旧存在,因为他们的scope是函数

for(var i=0; i<5; i++) { }

alert(i) // 5, variable survives and keeps value

在循环中声明变量很方便,但不要认为循环之后变量就不存在了,不要把循环当作其scope

下面的代码的结果是什么:

function test() {
  
  alert(window)

  var window = 5
}
test()
var指令在预处理阶段就执行了

因此,window在alert之前成了局部变量
LexicalEnvironment = {
  window: undefined
}

所以当执行到第一行时,变量window存在但是未定义
View Code

 

posted @ 2013-08-26 12:48  rainbowi  阅读(5306)  评论(0编辑  收藏  举报