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() 执行了一次, 变量声明语句也执行了一次。
js oop 编程中闭包可以用于模拟私有成员、构造单体类
复制代码 代码如下:
function MakeItem(name,val){
var myName,myVal; //私有属性
//私有方法
function setName(name){
myname=name;
}
//私有 方法
function setVal(val){
myVal=val;
}
//执行new构造对象时调用内部私有方法
setName(name);
setVal(val);
//公共方法
this.getName=function(){
return myName;
}
this.getVal=function(){
return myVal;
}
}
var obj = new MakeItem("name",100);
obj.myname; //undefined 无法在外面访问私有属性
obj.getName(); //ok
下面是一种单体类构建方法
复制代码 代码如下:
var Singleton = (function(){
var instance = null; //在闭包中保存单体类的实例
var args = null;
var f = function(){
if(!instance){
if(this===window){
args = Array.prototype.slice.call(arguments,0);
instance = new arguments.callee();
}else{
this.init.apply(this,args||arguments);
instance = this;
}
}
return instance;
};
f.prototype = {
init:function(a,b,c){
this.a = a;
this.b = b;
this.c = c;
this.method1 = function(){ console.log("method 1"); };
this.method1 = function(){ console.log("method 1"); };
console.log("init instance");
}
};
f.prototype.constructor = f.prototype.init;
return f;
})();
//单体的使用
var obj1 = Singleton(1,2,3);
var obj2 = new Singleton();
var obj3 = new Singleton();
console.log(obj1===obj2,obj2===obj3); //true
console.log(obj1);
//一个单体类声明函数
var SingletonDefine= function(fun){
return (function(){
var instance = null;
var args = null;
var f = function(){
if(!instance){
if(this===window){
args = Array.prototype.slice.call(arguments,0);
instance = new arguments.callee();
}else{
fun.apply(this,args||arguments);
instance = this;
}
}
return instance;
};
f.prototype = fun.prototype;
f.prototype.constructor = fun;
return f;
})();
};
var fun = function(a,b,c){
this.a = a;
this.b = b;
this.c = c;
this.method1 = function(){ console.log("method 1"); };
console.log("init instance");
};
fun.prototype.method2 = function(){ console.log('method 2'); };
//单体类声明函数用法
var Singleton = SingletonDefine(fun);
var obj1 = Singleton(8,9,10);
var obj2 = new Singleton();
var obj3 = new Singleton(3,2,1);
console.log(obj1===obj2,obj2===obj3);
console.log(obj1);
//console.log(obj1.toSource()); //firefox
obj1.method1();
obj1.method2();
IE6 的内存泄露与闭包
在IE 6 中,非原生js对象(DOM 等)的循环引用会导致内存泄露,使用闭包时如果涉及非 js 原生对象引用时要注意。
function fun(){
var node = document.getElementById('a');
node.onclick = function(){ alert(node.value); };
node = null; //打断循环引用防止内存泄露