函数定义
定义函数方法有两种:声明式 和 表达式
1.声明式(普通形),略
2.表达式(赋值形)
匿名函数(anonymous function)、有时候也叫拉姆达函数。存在函数声明提升问题、闭包this指针问题
函数声明提升问题(function declaration hoisting): 声明式放在哪里都没问题。但是如果是表达式的函数,调用时不能放在定义之后的!
sayHi(); //错误:函数还不存在 var sayHi = function(){ alert("Hi!"); };
递归(带名字的lambda函数实现)
arguments.callee是当前执行函数的指针,但是在严格模式下不允许访问。所以通过带名字的lambda函数实现
var factorial = (function f(num){ if (num <= 1){ return 1; } else { return num * f(num-1); } });
闭包 (解决GC回收内存释放、块级作用域等问题)
闭包是指有权访问其他函数作用域中的变量的函数。换言之就是有不是自己变量的函数,就是闭包。
创建闭包的常见方式,就是在一个函数内部创建另一个函数。
详细理解作用域链: 在后台执行环境中,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域
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; } }; } var compareNames = createComparisonFunction("name"); //创建函数 var result = compareNames({ name: "Nicholas" }, { name: "Greg" }); //调用函数 compareNames = null; //解除对匿名函数的引用(以便释放内存)
var name = "The Window"; var object = { name : "My Object", getName: function(){ return this.name; } , getNameFunc : function(){ var that = this; //这一行是关键,通常也有let self = this的默认命名 return function(){ return that.name; }; } }; alert(object.getNameFunc()()); //"My Object"
模仿块级作用域
其中的变量 now 现在是匿名函数中的局部变量,而我们不必在全局作用域
中创建它。
(function(){ var now = new Date(); if (now.getMonth() == 0 && now.getDate() == 1){ alert("Happy new year!"); } } )();
模拟私有变量(模块模式)
JavaScript 是以对象字面量的方式来创建单例对象的。简言之,如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。
var application = function(){ var components = new Array(); //私有变量和函数 components.push(new BaseComponent()); //初始化 var app = new BaseComponent(); //创建 application 的一个局部副本 //公共接口 app.getComponentCount = function(){ return components.length; }; app.registerComponent = function(component){ if (typeof component == "object"){ components.push(component); } }; return app; }(); //返回这个副本
在 Web 应用程序中,经常需要使用一个单例来管理应用程序级的信息。这个简单的例子创建了一个用于管理组件的 application 对象。在创建这个对象的过程中,首先声明了一个私有的 components数组,并向数组中添加了一个 BaseComponent 的新实例(在这里不需要关心 BaseComponent 的代码,我们只是用它来展示初始化操作)。而返回对象的 getComponentCount()和 registerComponent()方法,都是有权访问数组 components的特权方法。前者只是返回已注册的组件数目,后者用于注册新组件。