这两个概念理解起来不容易,翻查了很多资料,几乎耗费了我一天的时间来理解它,感觉自己都快走火入魔了。不过最终有所了悟,一番付出还是有所收获的。
(1)闭包概念
很多书上关于闭包的定义直接阅读的话很难理解,下面我通过实例来讲解这个概念。
1、首先,我们必须了解变量的作用于问题。js的作用于无外乎有两种,一种是全局变量(可以在全局范围内被调用),一种是局部变量(一般在函数内声明,正常情况下只能在函数内调用)。
js语言的特俗之处就在于函数内部可以读取全局变量。(需要注意的是,在函数内部定义变量一定要用到var,否则你定义的就是全局变量)
2、一般情况下,从函数外部无法读取函数内部的局部变量。但是特殊情况下,通过变通的方式可以实现。
我们可以在函数内部在定义一个函数。
function f1() {
var n = 999;
function f2() {
alert(n);
}
return f2;
}
var result = f1();
result();
上述代码中,f2函数就是闭包。我给出的定义是能够读取函数内部变量的函数就叫闭包。
3、闭包的用途
.可以读取函数内部的变量
.让这些变量的值始终保持在内存中。
function f1() {
var n=999;
nAdd = function() {n+=1};
function f2() {
alert(n);
}
return f2;
}
var result = f1();//运行之后返回f2,此时n=1000.注意f2函数并未运行,也就是不会运行alert(n);
result();//运行完后,变量n仍然保存在内存中,值为999
nAdd();//全局函数,n自加1;
result();
result实际上就是等于函数f2.它一共运行了2次,一次是999,一次是1000.这证明了函数f1中的局部变量n一直保存在内存中,并没有在调用后被自动清理机制自动清除。
为什么会这样?原因就在与f2被赋予给一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终保存在内存中,不会在调用结束后被垃圾回收机制回收。
至于nAdd = function() {n+=1}这一行,没有使用var关键字,因此nAdd是一个全局变量而不一个局部变量。这样函数可以在外部对函数内部的局部变量进行操作。
(2)面向对象编程:封装
如果我们要把“属性”和“方法”封装成一个对象,甚至从原型对象生成一个实例对象,我们该怎么做?下面我介绍封装方法的演变过程。
1、生成对象的原始模式
把手机看成一个对象,他有类型和颜色两个属性
var shouji = {
type = “”,
color = “”
}
根据这个原型对象的规格,生成两个实例。
var shouji1 = {};
shouji1.type = “苹果”;
shouji1.color = “red”;
var shouji2 = {};
shouji2.type = “华为”;
shouji2.color = “blue”;
上面是最简单的封装,缺点:多生成几个实力写起来就会非常麻烦,实力和原型之间没有什么办法看出什么联系。
2、原始模式的改进
通过构造一个函数解决函数重复问题。
function Shouji(type,color) {
return {
type:type,
color:color
}
}
生成实例
var shouji1 =new Shouji(“苹果”,“red”);
var shouji2 =new Shouji(“华为”,“blue”);
shouji1和shouji2之间没有内在练习,不能反映出它们是同一个原型对象的实例。
3、构造函数模式
构造函数就是普通函数,除了按约定首字母大写,内部使用了this变量。对构造函数使用new运算符就能生成实例,并且this对象会绑定在实例对象上。
例:function Shouji(type,color) {
this.type = type;
this.color = color;
}
生成实例
var shouji1 =new Shouji(“苹果”,“red”);
var shouji2 =new Shouji(“华为”,“blue”);
这时shouji1和shouji2自动含有一个construcor属性,指向他们的构造函数。
shouji1.constructor == Shouji;
shouji2.constructor == Shouji;
如果上述对象添加一个shouji添加一个不变的属性“gongneng”和一个“internet”上网功能,那么原型对象就变成下面这个样子。
function Shouji(type,color) {
this.type = type;
this.color = color;
this.gongneng = “打电话”;
this.internet = function(){alert(“吃老鼠”)}
}
生成实例
var shouji1 =new Shouji(“苹果”,“red”);
var shouji2 =new Shouji(“华为”,“blue”);
对于每一个实力对象,gongneng属性和internet()方法都是一模一样的内容,每次生成一个实力都必须重复同样的内容,多占用一些内容这样既不环保也缺乏效率。
能不能让gongneng属性和internet方法在内存中只生成一次,然后所有实例都指向那个内存地址?以下的方式可以。
4、prototype模式
js规定每一个构造函数都有一个prototype属性,指向另一个对象。这个对象所有属性和方法都会被构造函数的实例继承。
我们可以把这些不变的属性和方法直接定义在prototype对象上。
function Shouji(type,color) {
this.type = type;
this.color = color;
}
Shouji.prototype.gongneng = “猫科动物”;
Shouji.prototype.nternet = function(){alert(“吃老鼠”)};