posts - 43,comments - 3,trackbacks - 0
可遍历的调用栈:(arguments.callee, arguments.callee.caller)
某函数被调用时刻:
function _A(){
_stack_push()
F.arguments = new BaseArray(x,y,z...);
F.arguments.callee = F;
F.caller = _A;
执行 F...
_stack_pop()
}
虽然可以利用 callee,caller进行调用栈遍历,但是可能发生死循环!
 1 function enumStack(callback)
 2 {
 3 var f = arguments.callee;
 4 while (f.caller)
 5 {
 6 callback(f = f.caller);
 7 }
 8 }
 9 var _r_f = /^function \b\s*([$\w\u00FF-\uFFFF]+)\s*\(/m;
10 function showIt(func){
11 var name = (!(func instanceof Function)? 'unknow type':_r_f.test(func.toString()) ? RegExp.$1:'anonymous');
12 alert('-> ' + name);
13 }
14 
15 var i = 0;
16 var i_max = 1// i_max > 1 时发生死循环
17 function f1()
18 {
19 f2();
20 }
21 
22 function f2()
23 {
24 if (++<i_max){
25 f1();
26 }
27 enumStack(showIt);
28 }
29 
30 f1();

闭包:javascript中有两种闭包,一种为函数闭包,一种为对象闭包(with语句);函数闭包是与函数执行过程成对出现的数据,它在函数运行结束后,保持运行过程的最终数据状态。闭包包括的是函数运行实例的引用、环境、以及一个由所有upvalue引用组成的数组,由此可知
1 function MyObject(){
2 function func(){}
3 return func;
4 }
5 var obj1 = MyObject();
6 var obj2 = MyObject();
7 obj1 != obj2 //!!!!!!!!!!!!!
  obj1.toString() == obj2.toString() //!!!!!!!!!!!!!!!!!!!
在被调用时(函数运行产生函数实例 ),每个函数实例至少拥有一个闭包,也可能有多个:
var checker;
function myFunc()
{
if (checker){
checker();
}

alert('
do myFunc: ' + str);
var str = 'test.';
if (!checker){
checker 
= function(){alert('do Check : ' + str);}
}
return arguments.callee;

}

myFunc()();
//myFunc();myFunc()

//do myFunc: undefined
//
do Check: test. 第二个闭包生效
//
do myFunc: undefined
javascript 执行环境描述如下:
TheContext = {type:'Function', name:'myFunc', params:[...], body:ScriptObject}; ScriptOjbect = {type:'SCRIPT', varDecls:[...], funcDecls:[....], source:'....'}, TheContext结构描述函数作为对象时的外在表现,如函数名,参数,等等。其body指向一个ScriptObject, 这个ScriptObject就包含了函数代码体中的全部语法分析结构,包括内部变量表和内嵌函数表以及除此之外的全部代码。此外还有两个概念即 调用对象和全局对象;调用对象是指当前正在调用函数的ScriptObject结构;而全局对象是指系统全局环境下的一个ScriptObject结构。全局变量就是全局对象的属性,局部变量就是调用对象的属性。

变量(闭包)的维护规则:
1.在函数开始执行时,varDecls中所有的值将被置为undefined, varDecls总是在语法分析阶段被创建好
2.函数执行结束并退出时,varDecls不被重置。
3.函数内的数据持续的生存周期,取决于该函数实例是否为活动引用,如果没有,则调用对象可被销毁。
4.全局对象的相应动作同理
调用对象在语法分析期可以得到,以其ScriptObject作为原型,接下来在函数运行期若该函数实例有一个函数闭包,则引擎将会:
1.创建一个函数实例
2.为该函数创建一个闭包
3.为该函数实例(及其闭包)的运行环境从ScriptObject复制一个‘调用对象’

闭包中标识符的优先级:
this > 局部变量(varDecls) > 函数形式参数名(argsName) > ( (arguments > 函数名(funcName)) FireFox  || (arguments > 函数名(funcName)) IE)
function foo(foo){
alert(foo);
}
foo('hi') 
// alert('hi');

////////////////////
function foo2(arguments){
alert(
typeof arguments);
}
foo2(
1000);//'number'

/////////////////////
function arguments(){
alert(
typeof arguments);
}
//function ==> IE JScript
//
object FireFox SpiderMonkey
arguments();

由Function()构造出来的函数对象与函数直接量声明,匿名函数不同,它在任意位置创建的实例,都处于全局闭包中!Function()的实例的upvalue总是指向全局闭包,所以不会对其他闭包内的变量产生引用!!
1 var val = 'this is global.'
2 function my(){
3 var val = 'this is local.'
4 var foo = new Function('\
5 alert(val);\ // 这里会被toString
6 ');
7 foo(); // 'this is global.
8 }

所谓对象闭包是指使用with语句时与with()所指示对象相关的闭包,该闭包被动态创建并添加到执行环境当前闭包链顶端:
(IE 和 FireFox)
 1 var aObj = {val:'hello'}
 2 function foo(){
 3 with(aObj)
 4 {
 5 var val = 1000; //声明局部变量val, 运行时为aObj.val 赋值 
 6 alert(aObj.val); // 1000
 7 }
 8 alert(val) //undefined
 9 }
10 foo();
11 

具名函数的闭包与对象闭包
1 var obj = { val:200};
2 var val =1000;
3 with(obj){
4 function foo(){val*=2;}//具名函数的闭包依赖于函数静态位置所决定; 语法分析期决定 obj对象闭包与foo()函数闭包处于并列位置
5 foo();
6 }
7 alert(obj.val); // 200
8 alert(val); // 2000
9 

匿名函数闭包与对象闭包
var obj = { val:200};
var val =1000;
with(obj){
void function(){val*=2;}();//匿名函数的闭包依赖于其所在的当前闭包链
/*
var a = function(){val*=2;};//匿名函数的闭包依赖于其所在的当前闭包链
a();


*/
}
alert(obj.val); 
// 400
alert(val); // 1000
eg2:
function foo(){
function foo2(){
var msg = 'hello';
= function (varName){return eval(varName);}
}
foo2();
var aFormatStr = 'the value is : ${msg}';
var m;
var rx = /\$\{(.*?)\}/g;
alert(aFormatStr.replace(rx, 
function($0, varName){return m(varName);}));
}
foo(); 
// alert('the value is : hello');
 
eval()与闭包 (使用当前函数的闭包)
1 var i = 100;
2 function f(){
3 var i = 'test';
4 eval('i="hello"');
5 }
6 f();
7 
8 alert(i); // 100

var i = 100;
function my(){
eval('var i = "eee"');
return i
}
alert(my());// alert('eee')
若使用 window.eval() 则IE 和 Firefox 不一致
1 var i = 100;
2 function f(){
3 var i = 'test';
4 window.eval('i="hello"'); // eval.call(window, 'i="hello"');
5 }
6 f();
7 
8 alert(i); // IE为100, Firefox为hello
9 //IE 为了函数内执行的eval()尽量不产生副作用, 即eval访问不到全局的闭包 但window.execScript()总是在全局闭包中 (Firefox 没有这个函数)
// execScript()的第二个参数为脚本语言的种类 eg: 'JScript', 'VBScript', 'Python', 'Lua' (FireFox 中的eval有2个参数)

eval('..')总是试图返回值,由被执行代码块的最后一个语句决定 alert(eval('for(var i = 0; i < 10; ++i)')) //alert('undefined')
也就是说 eval('..')的返回值是javascript中语句的副作用导致的结果,eval本质是一个动态的语句执行系统。
所以我们可以用 var b = eval('true');得到一个布尔值,却不能用var ob = eval('{b:true, i:1}');得到一个直接量,因为其内容不是合法
语句,而eval('{b:1}');返回值确是1, ‘b:'被当作标签声明。
解决方法是使用强制表达式运算符'()':  如下 eval('({b:true,i:1})') 但是在IE(JScript)中存在一个例外:函数直接量(匿名函数)不能通过这种方式来获得 eval('(function(){})') == undefined ; 但可以这样 eval('([function(){}][0])')

eval的存在也倒是javascript代码是不可编译的(二进制的,而非虚拟机代码)

javascript动态方法调用:
javascript中有三种执行体:一种是eval()函数入口参数中指定的字符串,该字符串总是被作为当前函数上下文中的语句来执行;第二种是new Function()中传入的字符串,该字符串总是被作为一个全局的,匿名函数闭包中的语句行被执行;第三种情况,执行体就是一个函数,可以通过函数调用运算符"()"来执行,也可以通过apply(this,array or argument),或者 call(this,$0,$1,..);两者区别在于参数不同,且apply做了优化。它们主要是用来改变function中this引用的。 若第一参数为null或undefined表明执行中传入默认的宿主对象。

用eval来模拟Function.call 和 Function.apply
 1 Fucntion.prototype.apply = function(thisArg, argArray){
 2 thisArg = (thisArg==null)?window:Object(thisArg);
 3 thisArg.__applyTemp__=this;
 4 var parameters = [], length=(argArray || '').length;
 5 for(var i = 0; i < length; ++i) parameters[i]='argArray['+i+']';
 6 
 7 var functionCall = 'thisArg.__applyTemp__('+parameters+')';
 8 tryreturn eval(functionCall)}
 9 finally{try{delete thisArg.__applyTemp__}catch(e){}
10 }
11 }
12 
13 Function.prototype.call=function(thisArg){
14 return this.apply(thisArg,
15 Array.prototype.slice.apply(arguments,1));
16 }
17 
18 }
19 }

javascript中的重写:
javascript中包括原型重写(完全重写后改变构造器!!),构造器重写, 对象成员重写。如果构造器在运行时刻被覆盖,将会给对象检测带来麻烦!
eg1:
function F(){}
var a = new F();
function F(){this.name="myF"} //这种情况的重写发生在语法分析阶段,导致全局只有一个F()存在,即第二个声明,同时a.name == "myF"

eg2:
function F(){}
var a = new F();
F = function(){this.name="myF"}//这种情况的重写发生在运行阶段,从此处开始,到变量F的作用域结束,其覆盖同名函数,所以a.name == undefined

对象成员重写可以用 hasOwnProperty来里检测,该方法不检查一个实例的父类代码,或原型代码
posted on 2009-10-25 18:44 RUI 阅读(341) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理