函数表达式
一、闭包
概念:闭包是指有权访问另一个函数作用域中变量的函数。
function createCompareFun(propertyName) {
return function (obj1, obj2) {
var a = obj1[propertyName];
var b = obj2[propertyName]; if (a < b) {
return -1;
}
if (a > b) {
return 1;
} else {
return 0;
}
}
} //创建匿名函数
var compare = createCompareFun("age");
//调用该函数
var result = compare({age:3},{age:8});
console.log(result); //-1
如果一个闭包能访问的函数被执行完毕,作用域链(本质上是一个指向变量对象的指针列表)被销毁后,
其活动对象也不会被销毁,因为它们被闭包引用,知道闭包函数被销毁后,它们才被销毁。
由于闭包会携带包含它函数的作用域,所以会占用更多内存,建议能不使用闭包就不要使用闭包。
作用域链的这种配置机制引出了另外一个重要问题:闭包只能取得包含函数中任何一个变量的最后一个值。
function createFun() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
}; function getResult(fun, array) {
for (var j = 0; j < fun.length; j++) {
array[j] = fun[j]();
}
return array;
} var fun = createFun();
var array = new Array();
var a = getResult(fun,array);
console.log(a); //[ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 ]
注意该问题的本质:注意函数实际执行时i变量的值,定义时的值不能说明问题。
解决办法:
function createFun() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function (num) {
return function () {
return num;
};
}(i);
}
return result;
}; /*var b = function rightNow(a) {
return a;
}(5);
console.log(b);*/
闭包中this的问题:
var name = "Window";
var obj = {
name: "Object",
getFun: function () {
return function () {
return this.name;
};
}
};
console.log(obj.getFun()()); //Window //解决:
var obj2 = {
name: "Obj2",
getFun: function () {
console.log("Fun's name: "+this.name);
var v = this.name;
return function () {
return v;
}
}
}
console.log(obj2.getFun()());
var name = "Window";
var obj = {
name: "Object",
getFun: function () {
return this.name;
}
}; var a = obj.getFun;
console.log(a()); //Window
console.log(obj.getFun()); //Object
内存泄漏问题:
window.onload = function () {
function outer() {
var element = document.getElementById("testId");
element.onclick = function () {
//闭包的作用域链中保存了一个html元素
//只要匿名函数存在,element的引用数最少是1,因此它所占用的内存永远不会被回收。
console.log(element.id);
};
}
outer(); //解决
function outer2() {
var element = document.getElementById("testId");
var id = element.id;
element.onclick = function () {
console.log(id);
};
//闭包会引用包含函数的整个活动对象,闭包即使不直接引用element,
//包含函数的活动对象任然会保存一个引用,所以这步必须有
element = null;
}
}
二、模仿块级作用域
ECMAScript很操蛋地没有块级作用域:
function Afun() {
for (var i = 0; i < 10; i++){
console.log(i);
}
//在外面重新声明i,并没有什么卵用
var i;
console.log("Outer i is "+i);
}
Afun(); //Outer i is 10
function Afun() {
for (var i = 0; i < 10; i++){
console.log(i);
}
//
var i = 100;
console.log("Outer i is "+i);
}
Afun(); //Outer i is 100
可以用匿名函数来模仿块级作用域:
function Afun() {
//由于函数声明后不能直接跟员括号来执行它
//所以我们用()包裹函数声明把它转换一个指向函数的引用
(function () {
for (var i = 0; i < 10; i++){
console.log(i);
}
})();
console.log(i); //导致Error:i is not defined.
}
这种技术经常被用来限制在全局作用域中添加过多的变量和函数。
三、私有变量
私有变量:函数的参数、局部变量和在函数内部定义的函数。
共有方法 == 特权方法
//在构造函数定义特权方法
function MyObject() {
//私有变量
var v = 10;
//私有函数
function f() {
return false;
} //特权方法
this.pm = function () {
++v;
return v;
}
} var o = new MyObject();
//除了pm()没有任何途径能访问到真正的o.v
console.log(o.v); //undefined
o.v = 166;
//假的
console.log(o.v); //
console.log(o.pm()); //
一)静态私有变量
也可以通过在私有作用域中定义私有变量和函数,创建特权方法。
(function () {
var v = 10;
function f() {
return false;
} //注意这里没有var,是全局的
//为了保证全局,也没有使用声明函数
MyObject = function () {
}; MyObject.prototype.pm = function () {
v++;
return f();
};
})(); console.log(MyObject);
console.log(new MyObject().pm()); //false