js高程-7 函数表达式

定义函数的方式有两种:1.函数声明:

function functionName(arg0,arg1,arg2){
    //函数体
}

2.函数表达式:

var functionName=function(arg0,arg1,arg2){
    //函数体
}

7.1递归

function factorial(num) {
			if (num <= 1) {
				return 1;
			} else {
				return num * factorial(num - 1);
			}
		}
		var anotherFactorial = factorial;
		factorial = null;
		alert(anotherFactorial(4));//出错

在递归中直接引用函数名会报错,更保险的方式是使用arguments.callee代替函数名。但是在严格模式下使用arguments.callee会报错,不过可以使用命名函数表达式来达到相同的效果。

var factorial = function f(num) {
			if (num <= 1) {
				return 1;
			} else {
				return num * f(num - 1);
			}
		};

7.2闭包

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式是在一个函数的内部创建另一个函数。当某个函数被调用时,会创建一个执行环境及相应的作用域链。例如这样一段代码:

function compare(value1, value2) {
			if (value1 < value2) {
				return -1;
			} else if (value1 > value2) {
				return 1;
			} else {
				return 1;
			}
		}
		var result = compare(5, 10);

在作用域链中创建了两类对象:1.调用compare()函数时创建了一个包含arguments、values1、values2的活动对象,2.全局执行环境的变量对象(包含result和compare)在compare()的执行环境的作用域链中则处于第二位。
在这里插入图片描述
作用域链本质上是一个指向变量对象的指针列表。
闭包中的情况:在另一个函数内部定义的函数会将外部函数的活动对象添加到它的作用域链中。

var compare = createComprisonFunction('name');
var result = compare({ name: 'Nicholas' }, { name: 'Greg' });

调用compare()的过程中产生的作用域链之间的关系:
在这里插入图片描述
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。

7.2.1闭包与变量
function createFunctions() {
			var result = new Array();
			for (var i = 0; i < 10; i++) {
				result[i] = function() {
					return i;
				};
			}
			return result;
		}
		console.log(createFunctions()[2]());//10

这个函数会返回一个函数数组,似乎每个函数都应该返回它的索引值,但事实是函数数组每一个位置都返回10。解决方法:通过创建一个匿名函数强制让闭包的行为符合预期,如下:

function createFunctions() {
			var result = new Array();
			for (var i = 0; i < 10; i++) {
				result[i] = (function(num) {
					return function() {
						return num;
					};
				})(i);
			}
			return result;
		}
		console.log(createFunctions()[2]());//2

这样每个函数都会返回自己的索引值了。

7.2.2关于this对象

在全局函数中,this等于window。而当函数被当做某个对象的方法被调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此this通常指向window。

var name = 'window';
		var object = {
			name: 'My Object',
			getNameFunc: function() {
				return function() {//匿名函数
					return this.name;
				};
			}
		};
		alert(object.getNameFunc()());//window

如何让它返回My Object呢,如下:

var name = 'window';
		var object = {
			name: 'My Object',
			getNameFunc: function() {
				var that = this;
				return function() {
					return that.name;
				};
			}
		};
		alert(object.getNameFunc()());//My Object
7.2.3内存泄露

如果闭包的作用域链中包含着html元素,就意味着该元素无法被销毁。

扫描二维码关注公众号,回复: 8962909 查看本文章
function assignHandler() {
			var element = document.getElementById('someElement');
			element.onclick = function() {
				alert(element.id);
			};
		}

解决办法:通过给id赋值element.id,把element.id的一个副本保存在一个变量中,并且在闭包中引用该变量消除了循环引用。但是仅仅做到这一步还是不能解决内存泄露问题。要记住闭包会引用包含函数的整个活动对象,而其中包含着element。即使闭包不直接引用element,包含函数的活动对象中也仍然会保存一个引用。因此有必要把elemen设置为null。这样就能解除对DOM对象的引用,顺利的减少其引用数,确保正常的内存回收。

function assignHandler() {
			var element = document.getElementById('someElement');
			var id = element.id;
			element.onclick = function() {
				alert(id);
			};
			element = null;
		}

7.3模仿块级作用域

如前所述,javascript没有块级作用域的概念。这意味着在块语句中定义的变量,实际上可以作用于整个函数,而不仅仅作用于块语句。如下:

function outputNambers(count) {
			for (var i = 0; i < count; i++) {
				alert(i);
			}
			alert(i);
		}

在java,c++语言中,变量i只会在for循环的语句中有定义,循环一旦结束,变量i就会被销毁。可是在javascript中,变量i是定义在outputNambers()的活动对象中的,因此从它有定义开始就可以在函数内随处访问它。即使错误的重新声明同一个变量,也不会改变它的值。

function outputNambers(count) {
			for (var i = 0; i < count; i++) {
				alert(i);
			}
			var i;//重新声明变量
			alert(i);
		}

javascipt从来不会告诉你是否多次声明了同一个变量,因此可以使用匿名函数模拟块级作用域来解决这个问题。语法如下:

(function() {
})();

使用方法:

function outputNambers(count) {
			(function() {
				for (var i = 0; i < count; i++) {
					alert(i);
				}
			})();
			alert(i);//导致一个错误
		}

我们在for循环外部插入了一个私有作用域,在匿名函数中定义的任何变量,都会在执行结束时销毁。因此变量i只能在循环中使用,使用会即被销毁。

发布了26 篇原创文章 · 获赞 6 · 访问量 5079

猜你喜欢

转载自blog.csdn.net/Blablabla_/article/details/104044791
今日推荐