函数执行环境
简单的代码:
复制代码 代码如下:
function say(msg,other){
var str = "nobody say:";
this.name = '笨蛋的座右铭';
function method(){};//var method = function(){};
alert(str+msg);
}
say('hello world');
alert(name);//笨蛋的座右铭
当调用say方法时,第一步是创建其执行环境,在创建执行环境的过程中,会按照定义的先后顺序完成一系列操作:
1.首先会创建一个'活动对象'(Activation Object)。活动对象是规范中规定的另外一种机制。之所以称之为对象,是因为它拥有可访问的命名属性,但是它又不像正常对象那样具有原型(至少没有预定义的原型),而且不能通过 JavaScript 代码直接引用活动对象。
2.为函数调用创建执行环境的下一步是创建一个 arguments 对象,这是一个类似数组的对象,它以整数索引的数组成员一一对应地保存着调用函数时所传递的参数。这个对象也有 length 和 callee 属性(更深入的内容,请参见《理解Javascript_14_函数形式参数与arguments 》)。然后,会为活动对象创建一个名为“arguments”的属性,该属性引用前面创建的 arguments对象。
3.接着,为执行环境分配作用域。作用域由对象列表(链)组成。(比较复杂,请参见:《理解Javascript_15_作用域分配与变量访问规则,再送个闭包》)
4.之后会发生由 ECMA 262 中所谓'活动对象'完成的'变量实例化'(Variable Instatiation)的过程。此时会将函数的形式参数创建为可变对象的命名属性,如果调用函数时传递的参数与形式参数一致,则将相应参数的值赋给这些命名属性(否则,会给命名属性赋 undefined 值)。对于定义的内部函数,会以其声明时所用名称为可变对象创建同名属性,而相应的内部函数则被创建为函数对象并指定给该属性。变量实例化的最后一步是将在函数内部声明的所有局部变量创建为可变对象的命名属性。注:在这个过程中,除了实际参数有值外和函数定义外,其它都被'预解析'为undefined值.
5.最后,要为使用 this 关键字而赋值。(此时的this指向的是全局对象,即window)
执行环境创建成功后,就进入第二步:在函数体内,从上到下执行代码。
1.当执行到var str='nobody say'会发生称之为'计算赋值表达式'的过程,此时,会将活动对象中key为str的值从undefined设置为'nobody say:'。
2.执行到this.name='笨蛋的座右铭'时,会为作为this的对象添加属性name,并赋值为'笨蛋的座右铭'.
3.然后是执行function innerMethod(){};最后执行'alert(str+msg),输出'nobody say:hello world'.
function method(){}与var method = function(){}的区别
简单的代码:
复制代码 代码如下:
function say(){
method01();//method01
method02();//error
function method01(){
alert('method01');
}
var method02 = function(){
alert('method02');
}
}
say();
为什么调用方法method01能正常运行,而调用方法method02却会报错呢?
首先,你要明确的知道method01为一个函数对象,而method02为一个变量,它指向于另一个函数对象。根据上一节的内容,在'活动对象'完成的'变量实例化'(Variable Instatiation)的过程中,对函数method01进行了正常的'预解析',而对于变量method02解析为undefined值,当进入到执行代码的环节时,因为method02的调用在其计算函数表达式之前,因此将undefined当作方法调来用,必然会报错。解决方案比较简单,就是将var method02=function(){...}放到其method02()的调用之前就可以了或者是用函数声明的方式定义函数(function method(){...})。
注:计算函数表达式就是指程序执行到var method02 = function(){...}。method02此时真正指向一个函数对象。因为在'预解析'时,method02只被赋于了undefined.
全局执行环境(《执行模型浅析》中已经讲过,不再深入)
在一个页面中,第一次载入JS代码时创建一个全局执行环境,全局执行环境的作用域链实际上只由一个对象,即全局对象(window),在开始JavaScript代码的执行之前,引擎会创建好这个Scope Chain结构。全局执行环境也会有变量实例化的过程,它的内部函数就是涉及大部分 JavaScript 代码的、常规的顶级函数声明。而且,在变量实例化过程中全局对象就是可变对象,这就是为什么全局性声明的函数是全局对象属性的原因。全局性声明的变量同样如此全局执行环境会使用 this 对象来引用全局对象。
注:区别'函数执行环境'中的活动对象(Activation Object)和全局执行环境中的可变对象(Vriable Object),Variable Object也叫做Activation Object(因为有一些差异存在,所以规范中重新取一个名字以示区别,全局执行环境/Eval执行环境中叫Variable Object,函数执行环境中就叫做Activation Object)。
Eval执行环境
构建Eval执行环境时的可变对象(Variable Object)就是调用eval时当前执行上下文中的可变对象(Variable Object)。在全局执行环境中调用eval函数,它的可变对象(Variable Object)就是全局对象;在函数中调用eval,它的可变对象(Variable Object)就是函数的活动对象(Activation Object)。
复制代码 代码如下: