💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
1.递归函数是在一个函数通过调用自身的情况下构成的。 ~~~ function factorial(num){ if (num<=1){ return 1; } else { return num*arguments.callee(num-1); } } ~~~ ~~~ var factorial = (function f(num){ if (num<=1){ return 1; } else { return num*f(num-1); } }); ~~~ 以上代码创建了一个名为f()的**命名函数表达式**,然后将它赋给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正确完成。这种方式在严格模式和非严格模式下都行得通。 2.闭包是指有权访问另一个函数作用域中的变量的**函数**。 3.创建闭包的常见方式,就是在一个函数内部创建另一个函数。 ~~~ function createComparisionFunction(propertyName){ return function (object1, object2) { var value1 = object1[propertyName]; var value2 = onbect2[propertyName]; if (value1 < value2){ return -1; } else if (value1 > value2){ return 1; } else { return 0; } }; } ~~~ **当createComparisionFunction()函数返回后,其执行环境的作用域会被销毁,但他的活动对象仍然会留在内存中;直到匿名函数被销毁后,createComparisionFunction()的活动对象才会被销毁。** ~~~ //创建函数 var compareNames = createComparisionFunction('name'); //调用函数 var result = compareNames({name:'Nicolas'},{name:'Greg}); //解除对匿名函数的引用(以便释放内存) compareNames = null; ~~~ 4.匿名函数的执行环境具有全局性,因此其this对象通常指向window。但有时候由于编写闭包的方式不同,这一点可能不回那么明显。 ~~~ var name = 'The window'; var object = { name: 'My Object', getNameFunc: function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()()); //'The window'(在非严格模式下) ~~~ 5.对已经没有用处的、闭包外部函数(保存着对象)的变量,应该将其设置为null以避免内存泄露。 ~~~ function assignHandler(){ var element = document.getElementById('someElement'); var id = element.id; element.onclick = function(){ alert(id); }; element = null; } ~~~ 6.用作块级作用域(通常称为私有作用域)的匿名函数语法如下: ~~~ (function(){ //这里是块级作用域 })(); ~~~ 无论什么时候,只要临时需要一些变量,就可以使用私有作用域。这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。 7.私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。 8.有权访问私有变量和私有函数的公有方法称为**特权方法**。有两种在对象上创建特权方法的方式。第一种是在构造函数中定义特权方法。 ~~~ function MyObject(){ //私有变量和私有函数 var privateVariable = 10; function privateFunction(){ return false; } //特权方法 this.publicMethod = function(){ privateVariable++; return privateFunction(); }; } ~~~ 利用私有和特权成员,可以隐藏那些不应该被直接修改的数据: ~~~ function Person(name){ this.getName = function(){ return name; }; this.setName = function(value){ name = value; }; } ~~~ 以上代码的构造函数定义了两个特权方法:getName()和setName()。这两个方法都可以在构造函数外部使用,而且都有权访问私有变量name。但在Person构造函数外部,没有任何办法访问name。不过,在函数中定义特权方法也有一个缺点,那就是必须使用构造函数模式来达到这个目的。(方法无法复用) 9.通过在私有作用域中定义私有变量或函数,同样也可以创造特权方法,即**静态私有变量**。 ~~~ (function(){ //私有变量和私有函数 var privateVariable = 10; function privateFunction(){ return false; } //构造函数 MyObject = function(){}; //公有、特权方法 MyObject.prototype.publicMethod = function(){ privateVariable++; return privateFunction(); }; })(); ~~~ 这个模式与在构造函数中定义特权方法的主要区别,就在于私有变量和函数是由实例共享的。由于特权方法是在原型上定义的,因此所有实例都是用同一个函数。而这个特权方法,作为一个闭包,总是保存着对包含作用域的引用。以这种方式创建静态私有变量会因为使用原型而增加代码复用,但每个实例都没有自己的私有变量了。 10.前面的模式是用于为自定义类型创建私有变量和特权方法的。而**模块模式**则是为单例创建私有变量和特权方法。所谓单例,指的就是只有一个实例的对象。模块模式通过为单例添加私有变量和特权方法能够使其得到增强,其语法形式如下: ~~~ var singleton = function(){ //私有变量和私有函数 var privateVariable = 10; function privateFunction(){ return false; } //特权/公有方法和属性 return { publiceProperty: true, publicMethod: function(){ privateVariable++; return privateFunction(); } }; }(); ~~~ 由于这个返回的对象是在匿名函数内部定义的,因此它的公有方法有权访问私有变量和函数。从本质上讲,这个对象字面量定义的是单例的公共接口。这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的。例如: ~~~ var application = function(){ //私有变量和函数 var conponents = new Array(); //初始化 components.push(new BaseComponent()); //公共 return { getComponentCount: function(){ return components.length; }, registgerComponent: function(component){ if (typeof component == 'object'){ components.push(component); } }; }(); ~~~ 在web应用程序中,经常需要使用一个单例来管理应用程序级的信息。这个简单的例子创建了一个用于管理组件的application对象。在创建这个对象的过程中,首先声明了一个私有的components数组,并向这个数组添加了一个BaseComponent的新实例。而返回对象的getComponentCount()和registerComponent()方法,都是有权访问数组components的特权方法。前者只是返回已注册的组件数目,后者用于注册新组件。 简而言之,如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。 11.**增强的模块模式**适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。改写前面的例子: ~~~ var application = function(){ //私有变量和函数 var conponents = new Array(); //初始化 components.push(new BaseComponent()); //创建application的一个局部副本 var app = new BaseComponent(); //公共接口 app.getComponentCount = function(){ return components.length; }; app.registgerComponent = function(component){ if (typeof component == 'object'){ components.push(component); } }; return app; }(); ~~~