JavaScript高级程序设计——第七章(闭包)

深入理解JavaScript闭包与私有变量
本文详细探讨了JavaScript中的函数表达式、闭包和私有变量等概念,解释了闭包如何访问外部函数作用域中的变量,以及闭包在实际应用中的常见问题与解决方案。同时,介绍了如何利用闭包和私有变量实现模块模式,以及在对象上创建特权方法的两种方式。

一.前言

这一章我们讨论函数表达式。主要涉及函数表达式的特征、闭包和私有变量等内容。

二.匿名函数

我们知道创建一个函数有两种方法:

//函数声明
function function1(a,b,c){
......
}

//函数表达式
var function1 = funcition(a,b,c){
......
}

我们将第二种情况下创建的函数叫做匿名函数

三.闭包

什么是闭包:闭包是有权访问另一个函数作用域中的变量的函数
创建闭包的常见方式:在一个函数内部创建另一个函数

function createComparisonFunction(propertyName){
	return function(object1,object2){
		var value1 = object1[propertyName]
		var value2 = object2[propertyName]
		if(value1 < value2){
			return -1;
		}else if(value1 > value2){
			return 1;
		}
		else {
			return 0;
		}
	}
}

在这个例子中,即使这个内部函数在其他的地方被调用,仍然可以访问属性
propertyName,因为内部函数的作用域中包含createComparsionFunction这个函数的作用域

一般来说,当函数执行完毕后,函数内部的活动对象会被销毁,只留下全局作用域。但是当有闭包时,情况有所不同: 在外部函数执行结束后,他的作用域链会被销毁,但是他的活动对象仍然在内存中。只有匿名函数被销毁后,外部函数才会被销毁。在这里插入图片描述

闭包的副作用:闭包只能取得外部函数中任何变量的最后一个值。

function createFunction(){
	var result = new Array()
	for(var i=0;i<10;i++){
		result[i] = function(){
			return i;
		}
	}
	return result;
}

我们希望得到的是一个长度为10,数值分别为0-9的数组,但是实际上循环这么多个的内部函数,每个都是返回10。因为每个闭包函数作用域中都保存着外部函数的活动对象。他们引用的都是同一个i。外部函数返回时,i的值是10,所以每个内部函数的i的值也为10。

那么如何解决这个问题呢:

function createFunction(){
	var result = new Array()
	for(var i=0;i<10;i++){
		result[i] = function(num){
			return function(){
				return num;
			}
		}(i)
	}
	return result;
}

我们又定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给了数组。在这里为什么i是从0-9,因为我们是在最内部定义了一个闭包的匿名函数,用于访问num。而外面一层function(num)是按值传递的。会将i的当前值赋值给参数num。

闭包中this指向的问题:匿名函数的执行具有全局性,因此this对象通常指向window。但因为闭包的编写方式不同,可能会让人产生误解。

var name = 'hjh'
var object = {
	name:'lym',
	getNameFunc:function(){
		return function(){
			return this.name
		}
	}
}
alert(object.getNameFunc()())  //'hjh'

按道理说,我们在object对象中定义了name属性,同样是在object对象中定义的函数,this应该指向object中的name才对,为什么会指向window中的name属性呢?我们之前说过,每个函数调用时,都会取得两个特殊变量:this和argument。内部函数在搜索这两个变量时,只会搜索到活动对象为止。因此不会访问到外部函数中的这两个变量,也无法取到外部函数中的定义的变量值。

那么我们想要取到外部函数中的变量和属性怎么办呢:

var name = 'hjh'
var object = {
	name:'lym',
	getNameFunc:function(){
		var that = this
		return function(){
			return that.name
		}
	}
}
alert(object.getNameFunc()())  //'lym'

注意:我们将this赋值给that,此时的this是在object中定义的,指向的也是object。所以我们此时访问的都是外部函数中的变量和属性

四.模仿块级作用域

我们之前提到,JavaScript没有块级作用域的概念。比如在函数中定义一个循环,变量i在函数内部任何地方都可以获取。那么有没有办法实现私有作用域呢?

function outputNumber(count){
	(function(){
		for(var i=0;i<count;i++){
			alert(i)
		}
	})
}
alert(i)  //报错

我们在for循环外插入了一个私有作用域。在匿名函数中定义的任何变量,都会在执行结束后被销毁。而在私有作用域中之所以能访问count,是因为这个匿名函数是一个闭包,他能访问外部函数作用域中的变量。

五.私有变量

任何在函数中定义的变量,都可以认为是私有变量,因为不能再函数的外部访问这些变量。私有变量包括:函数的参数、局部变量、在函数内部定义的其他函数。

那么怎么在函数外部访问这些变量呢:我们可以创建一个闭包,闭包通过自己的作用域链可以访问这些变量

我们把有权访问私有变量和私有函数的方法称为特权方法。有两种在对象上创建特权方法的方式:
1.构造函数中的特权方法

function Person(name){
	this.getName = function(){
		return name
	};
	this.setName = function(){
		name = value
	};
}
var person = new Person('nico');
alert(person.getName())  //'nico'
person.setName('lym')
alert(person.getName())  //‘lym'

这其实就是我们在第五章讲的构造函数的方法,缺点很明显**:针对每个实例都会创建一组新方法**。

2.静态私有变量

(function()){
	var name = ''
	Person = function(value){
		name = value
	}
	Person.prototype.getName = function(){
		return name
	}
	Person.prototype.setName = function(){
	    name = value
	}
})();

var person1 = new Person('nico')
alert(person1.getName())  //'nico'
person1.setName('lym')
alert(person1.getName())  //'lym'

var person2 = new Person('hjh')
alert(person1.getName())  //'hjh'
alert(person2.getName())  //'hjh'

这个例子中,变量name变成一个静态的,由所有实例共享的属性在一个实例上调用原型上的方法,所有的实例对象都会一起改变,正是和我们之前提到的原型模式相同

六.模块模式

我们刚刚提到的两种方法,都是用于为自定义类型创建私有变量和特权方法的。我们现在要说的模块模式,是为单例创建私有变量和特权方法。所谓单例指的就是只有一个实例的对象。

var singletion = function(){
	//私有变量和函数
	var privateNum = 10
	function privateFunction(){
		return false
	}

    //特权方法
	return{
		publicMethod:function(){
			privateNum ++;
			return privateFunction()
		}
	}
}

这种模式适用于什么情况呢:如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,就可以适用模块模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值