JavaScript函数及作用域的小结(5)

IE8及IE8以下浏览器中,a 函数的内的 arguments.caller( IE9之后这个属性被移除) 指向 a.caller 执行时的 arguments (arguments.caller.callee === a.caller)。

八、字符串实时解析中的函数调用:eval()、new Function()、setTimeout()、setInterval()

eval() 与 window.eval()

代码如下:


function a(){
    console.log('out of b');
}
function b(){
    function a(){ console.log("in b"); }
    var f = function(){ a(); };
    eval('a()'); // in b
    window.eval('a()'); //out of b ,ie 6\7\8 in b, ie 9 out of b
    (new Function('a();'))(); //out of b
    setTimeout('a()',1000);  // out of b
    setTimeout(f,2000);// in b
}
b();


eval() 中的代码执行于eval() 语句所处的作用域内:

代码如下:


var Objinit = function(){
  var param = 123;
  return {
          execute:function(codes){
                eval(codes);
          },
          setCallback:function(f){
              this.callback = f;
          },
          fireCallback:function(){
              this.callback && this.callback.call(this);
          },
        getParam:function(){
            return param;
        }
  }
};

var obj = Objinit ();
var param = 'outerParam';
console.log(param,obj.getParam()); //outerParam 123
obj.execute('param = 456');
console.log(param,obj.getParam()); //outerParam 456
obj.setCallback(function(){ eval("param = 8888")});
obj.fireCallback();
console.log(param,obj.getParam()); //8888 456
obj.setCallback(function(){ eval("eval(param = 9999)")});
obj.fireCallback();
console.log(param,obj.getParam()); //9999 456eval()


字符串中解析出的代码运在 eval 所在的作用域,window.eval() 则是运行在顶级作用域(低版本 chrome 和 低于IE9 则同 eval()).

IE 中 ,window.execScript();  相当于 window.eval()

new Function()、setTimeout()、setInterval()  的第一个字符串参数所解析得到的代码,都是在顶级作用域执行。

九、函数闭包

要理解函数闭包,先了解 js 的垃圾自动回收机制。

number、string、boolean、undefined、null 在运算和赋值操作中是复制传值,而对象类型的数据按引用传值。

js 的同一个对象型数据可能被多次引用,如果某个对象不再被引用,或者两个对象之间互相引用之外不在被第三方所引用,浏览器会自动释放其占用的内存空间。

函数被引用:函数被赋为其他对象的属性值,或者函数内部定义的数据在该函数外被使用,闭包的形成基于后一种情形。

闭包就是一个函数有权限访问另一个函数。

代码如下:


var f;

function fun(){

var a =1;

f = function(){ return ++a;};

}

fun(); //产生一个闭包

f(); //  闭包中 a=2

f(); // 闭包中 a =3  ,模拟静态变量


在 fun 内 声明的匿名函数赋给 fun 外的变量 f,该匿名函数内使用了在 fun 内声明的变量 a,于是 f可以访问变量 a,为了维持这种访问权限(f执 行时需要访问a,但何时执行未定), fun() 执行完毕产生的变量 a 不能被释放(除非f 中的函数被释放),于是产生了一个闭包(变量 a 被封 闭了,供 f 使用)。

产生闭包的关键是,一个在函数 A内的声明的函数 B被传出 A 之外,并且 B 函数内使用了在 函数A 内生成的数据(声明或按值传参)。

函数B传出函数A之外的方式有多种,如:

代码如下:


function fun(){   
var a =1;

return {a:123,b:456, c: function(){ return ++a;} };

}

var f = fun();

f.c(); //a=2

广义上来说,函数运行时都会形成闭包,没有数据在函数外被引用时,闭包的生命周期很短:函数执行完毕即释放。

闭包的独立性:即使由同一个函数产生���多个闭包也是相互独立的。

 代码如下:


function fun(){

var a =1;

return function(){ return ++a;};

}

var f1 =  fun(); //一份闭包

var f2 = fun(); //另一份闭包

alert(f1()); //2

alert(f1()); //3

alert(f2()); //2

alert(f2()); //3

这两份闭包中的变量 a 是不同的数据,每产生一份闭包, fun() 执行了一次,  变量声明语句也执行了一次。

 

最后说一下IE6的内存泄漏和闭包

在IE 6 中,非原生js对象(DOM 等)的循环引用会导致内存泄露,使用闭包时如果涉及非 js 原生对象引用时要注意。

function fun(){

var node = document.getElementById('a');
node.onclick = function(){ alert(node.value); };

node = null; //打断循环引用防止内存泄露

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/ac1c531387274afa27ca18113ddd6789.html