JavaScript基础笔记(五) 函数表达式
函数表达式
一、闭包
概念:闭包是指有权访问另一个函数作用域中变量的函数。
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); //166 console.log(o.pm()); //11
一)静态私有变量
也可以通过在私有作用域中定义私有变量和函数,创建特权方法。
(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
Simple is important!