深入理解javascript的作用域--函数声明为什么会前置

来源:互联网 发布:linux系统模拟下载 编辑:程序博客网 时间:2024/04/30 21:01

这篇博文解决了以下迷惑

  • 函数声明为什么前置
  • 函数声明前置和变量前置优先级问题
  • 为什么js文件开头就可以使用Math,String等库,而不需要导入头文件

1.变量对象VO

变量对象(Variable Object, 缩写为VO)是一个抽象
概念中的“对象”,它用于存储执行上下文中的:
1. 变量
2. 函数声明
3. 函数参数

js解释器就是通过变量对象(VO)来找到我们定义的变量和函数的。

举个例子:

var a = 10;function test(x) {var b = 20;}test(30);

针对例子的浏览器js引擎的VO记录:

//全局作用域VO(globalContext) = {a : 10,//变量test : <ref to function>//函数声明};//test函数的函数作用域VO(test functionContext) = {x : 30,//函数参数b: 20//函数声明};

2.js开头不需要加入头文件谜解

在js代码执行前,js引擎为我们初始化了如下全局作用域的VO。

在全局上下文中

//在全局上下文中 ,vo===this===global
VO(globalContext) === [[global]];[[global]] = {Math : <...>,String : <...>,isNaN : function() {[Native Code]}......window : global // applied by browser(host)};

那么String(10)就相当于[[global]].String(10);了。这就是js代码执行前就可以调用Math等库的原因。

String(10); //[[global]].String(10);window.a = 10; // [[global]].window.a = 10this.b = 20; // [[global]].b = 20;GlobalContextVO (VO === this === global)

其中有趣的是,我们发现global的window指向global自身,所以我们可以在浏览器中尝试window.window.window…一直循环调用下去,可以证明window是一个无限循环调用。

3.函数中的VO–AO

在函数上下文中,我们在进入函数上下文的时候创建vo,这时候称呼他为ao(activation object)。
AO可以看作是VO的激活对象。

VO(functionContext) === AO;AO = {arguments : <Arg0>};arguments = {callee,length,properties-indexes};

函数的AO经历两个阶段

  1. 变量的初始化阶段
  2. 代码执行阶段

3.1变量初始化阶段

VO按照如下顺序填充:
函数参数(若未传⼊入,初始化该参数值为undefined)
函数声明(若发⽣生命名冲突,会覆盖)
变量声明(初始化变量值为undefined,若发⽣生命名冲突,会忽略。)

举个例子:

function test(a, b) {var c = 10;function d() {}var e = function _e() {};(function x() {});b = 20;}test(10);

VO扫描创建过程:
添加所有传入参数:a:undefined,b:undefined
添加所有函数声明,名字重复就覆盖:d:func
添加所有变量声明,名字重复就忽略:c:undefined,e:undefined

因此它的VO:

AO(test) = {a: 10,b: undefined,c: undefined,d: <ref to func "d">e: undefined};

再来个例子:

function foo(x,y,z){    function x(){};    alert(x);}foo(100);

结果alert:function x(){}

分析:扫描传入参数:x:undefined,y:undefined,z:undefined
扫描函数声明:x:func覆盖undefined
扫描变量声明:无

最终x是func。

function foo(x,y,z){    function func(){};    var func;    consoel.log(func);}foo(100);

结果console.log:func(){}

分析:扫描传入参数:x:undefined,y:undefined,z:undefined
扫描函数声明:x:func覆盖undefined
扫描变量声明:x名字冲突不更改,直接忽略。

最终x还是func。

3.2代码执行阶段

还是这个例子

function test(a, b) {var c = 10;function d() {}var e = function _e() {};(function x() {});b = 20;}test(10);

之前分析之后的VO:

AO(test) = {a: 10,b: undefined,c: undefined,d: <ref to func "d">e: undefined};

十分好理解的为每个VO变量添加上数值

VO['c'] = 10;VO['e'] = function _e() {};VO['b'] = 20;

结果是:

AO(test) = {a: 10,b: 20,c: 10,d: <reference to FunctionDeclaration "d">e: function _e() {};};

3.3练习

题目:

alert(x); // functionvar x = 10;alert(x); // 10x = 20;function x() {}alert(x); // 20if (true) {var a = 1;} else {var b = true;}alert(a); // 1alert(b); // undefined

解析:
变量初始化阶段:
寻找函数声明:x:func
寻找变量声明:x不覆盖(还是func) a:undefined,b:undefined

所以第一行alert(x)返回function

代码执行阶段:
x:10
alert结果就是10
x:20
alert结果就是20

if语句没有产生新作用域,true永远为真,赋值a为1

最后两句打印a为1,b永远得不到执行,打印为undefined

0 0
原创粉丝点击