闭包
在一个函数内部创建一个子函数,子函数可以访问父函数作用域中的变量。
function f (propertyName) { //创建 f()函数时,会创建一个预先包含全局变量的作用域链,这个作用域链中包含函数f(),并把这个作用域链保存再内部的[[scope]]属性中 return function(obj1, obj2) { //匿名函数(子函数)把父函数(f())的活动对象添加到它自己的作用域链中。 var value1 = obj1[propertyName]; //匿名函数被返回后,它的作用域链被初始化为包含f()函数的活动对象和全局变量对象。 var value2 = obj2[propertyName]; //当函数执行完毕后,f()函数的活动对象(即arguments,和propertyName)不会被销毁,因为匿名函数的作用域链仍然再引用这个活动对象。 if(value1 > value2) { //直到匿名函数被销毁后,f()函数的活动对象才会被销毁 return 1; }else if(value1 < value2) { return -1; }else { return 0; }; }; } var compare = f("name"); var result = compare({name: "Nicholas"}, {name: "Grag"});
// 在全局作用域中调用f()函数时,会为函数创建一个执行环境,通过复制函数的[[scope]]属性中的对象构建起执行环境的作用域链。这里的全局变量对象(变量:compare()和函数 f())// 活动对象(f()函数的arguments, propertyName);
活动对象:是在进入函数执行环境时创建的,它通过函数的arguments属性初始化
当函数被调用时,会创建一个执行环境及其相应的作用域链。使用 arguments 和其他命名参数的值来初始化函数的活动对象。在作用域链中,父函数的活动对象处于第二位,以此类推,直到全局执行环境。
闭包的缺点:子函数会携带父函数的作用域,会比其他函数占用更多的内存。过度使用会导致内存占用过多。
this对象
在运行时基于函数的执行环境绑定的:再全局函数中,this等于window,当函数被作为某个对象的方法调用时,this等于那个对象。
var name = "the window"; var object = { name: "My Object", getNameFunc: function() { return function() { return this.name; //函数被调用时,自动取得俩个特殊变量this和arguments,内部函数搜索这俩个变量时,只会搜索到其活动对象。不能搜索外部函数的这俩个变量。 }; } }; console.log(object.getNameFunc()()); // the window
可以把外部作用域中的this对象保存再一个闭包访问的到的变量里。就可以让this引用正确的对象了
var name = "the window"; var object = { name: "My Object", getNameFunc: function() { var that = this; return function() { return that.name; }; } }; console.log(object.getNameFunc()()); // My Object
模仿块级作用域
匿名函数可以用来模仿块级作用域,也称为私有作用域。
(function() { //这里是块级作用域 }) ();
无论再什么地方,只要临时需要一些变量,就可以使用私有作用域。
function outputNumbers(count) { (function() { for(var i=0; i < count; i++) { console.log(i); } }) (); console.log(i); //出错 }
再匿名函数中的计数器i;再执行完毕后i就被销毁了。只所以可以访问变量count,因为它是个闭包,闭包函数能够访问包含它的函数中所有的活动对象。
私有变量
私有变量:任何在函数中定义的变量,函数的参数,函数内定义的其他函数。
特权方法: 有权访问私有变量和私有函数的公有方法。
function MyObject() { //私有变量和私有函数 var privateVariable = 10; function privateFunction() { return false; } //特权方法 this.publicMethod = function () { privateVariable++; return privateFunction(); }; }
能够在构造函数中定义的特权方法,因为特权方法作为闭包有权访问在构造函数中定义的所有变量和函数。
静态私有变量
以这种方式创建静态私有变量会因为使用原型而增进代码复用,但每个实例都没有自己的私有变量。
(function() { var name = ""; Person = function(value) { name = value; }; Person.prototype.getName = function() { return name; }; Person.prototype.setName = function(value) { name = value; }; }) (); var person1 = new Person("Nicholas"); console.log(person1.getName()); person1.setName("Grag"); console.log(person1.getName()); // Nicholas // Grag var person2 = new Person("Michael"); console.log(person1.getName()); console.log(person2.getName()); // Michael // Michael
模块模式
为单例创建私有变量和特权方法。单例:只有一个实例对象。
如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,就可以使用模块模式。
var application = function () { //私有变量和 数组 var components = new Array(); //new BaseComponent()代表添加一个实例进行初始化。只是用来展示初始化不用深究 components.push(new BaseComponent()); //公有方法和属性 return { getComponentCount: function () { return component.length; }, registerComponent: function() { if(typeof component == "object") { components.push(component); } } }; } ();
增强的模块模式
增强的模块模式适合: 单例必须是某种类型的实例, 同时还必须添加某些属性和方法对其加以增强的情况。
var application = function () { //私有变量和函数 var components = new Array(); //初始化 components.push(new BaseComponent()); //创建application的一个局部副本 var app = new BaseComponent(); //公共接口 app.getComponentCount = function () { return components.length; }; app.registerComponent = function(component) { if(typeof component == "object") { components.push(component); } }; 返回这个副本 return app; }();