一、函数表达式

1.定义函数2种方式
  • 函数声明
function functionName(arg0, arg1, arg2) {
  //函数体
}

Firefox、Safari、Chrome 和 Opera 给函数定义了一个非标准的 name 属性,可以访问函数名。
重要特征:函数声明提升(function declaration hoisting)

  • 函数表达式
var functionName = function(arg0, arg1, arg2){
  //函数体
};

这种情况下创建的函数叫做匿名函数(anonymous function)(也叫拉姆达函数)。
匿名函数的 name 属性是空字符串。
函数表达式,使用前必须先赋值。
匿名函数的另一使用:函数作为返回值时。

2.递归

递归函数:在一个函数通过名字调用自身的情况下构成的。

function factorial(num){
  if (num <= 1){
    return 1;
  } else {
    // return num * factorial(num-1);
    return num * arguments.callee(num-1);
  }
}

arguments.callee 是一个指向正在执行的函数的指针,可以用来实现对函数的递归调用。

编写递归函数时,使用 arguments.callee 比使用函数名更保险。但严格模式下会报错。

解决方法:使用命名函数表达式。

var factorial = (function f(num){
  if (num <= 1){
    return 1;
  } else {
    return num * f(num-1);
  }
});
3.闭包

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

函数被调用时发生的事:
当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象(activation object)。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,……直至作为作用域链终点的全局执行环境。

作用域链本质:是一个指向变量对象的指针列表。
它只引用但不实际包含变量对象。

在内部匿名函数从外部函数中被返回后,它的作用域链被初始化为包含外部函数的活动对象和全局变量对象。
–> 匿名函数就可以访问在外部函数中定义的所有变量。
在后台执行环境中,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域。

外部函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。
换句话说:当外部函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁后,外部函数的活动对象才会被销毁。

//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除对匿名函数的引用(以便释放内存)
compareNames = null;

过度使用闭包可能会导致内存占用过多,必要时使用。

闭包与变量
function createFunctions(){
  var result = new Array();
  for (var i=0; i < 10; i++){
    result[i] = function(num){
      return function(){
        return num;
      };
    }(i);
  }
  return result;
}
关于 this 对象

在闭包中使用 this 对象可能会导致一些问题。
匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。

每个函数在被调用时都会自动取得两个特殊变量: this 和 arguments 。
内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。
要想访问到,可以使用that保存this值,arguments同理。

var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc : function(){
    var that = this;
    return function(){
      return that.name;
    };
  }
};
alert(object.getNameFunc()()); //"My Object"

注意:几种特殊情况下, this 的值可能会意外地改变。

var name = "The Window";
var object = {
  name : "My Object",
  getName: function(){
    return this.name;
  }
}
object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); //"The Window",在非严格模式下

最后一行的赋值表达式的值是函数本身,所以 this 的值不能得到维持,结果就返回了 “The Window” 。

内存泄漏

闭包在 IE 9之前版本中会导致一些特殊的问题:如果闭包的作用域链中保存着一个HTML 元素,该元素将无法被销毁。

4.模仿块级作用域

用作块级作用域(通常称为私有作用域)的匿名函数:

(function(){
  //这里是块级作用域
})();

JavaScript 将 function 关键字当作一个函数声明的开始,而函数声明后面不能跟圆括号。而函数表达式的后面可以跟圆括号。
要将函数声明转换成函数表达式,只要给函数声明加上一对圆括号。

私有作用域 - 限制向全局作用域中添加过多的变量和函数

5.私有变量

私有变量:在函数中定义的变量。因为不能在函数的外部访问这些变量。
私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

特权方法(privileged method):有权访问私有变量和私有函数的公有方法。

在对象上创建特权方法的2种方式:
  • 在构造函数中定义特权方法
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;
  };
}
var person = new Person("Nicholas");
alert(person.getName()); //"Nicholas"
person.setName("Greg");
alert(person.getName()); //"Greg"

缺点:每个实例都会创建同样一组新方法。

  • 使用静态私有变量来实现特权方法
(function(){
  //私有变量和私有函数
  var privateVariable = 10;
  function privateFunction(){
    return false;
  }
  //构造函数
  MyObject = function(){
  };
  //公有/特权方法
  MyObject.prototype.publicMethod = function(){
    privateVariable++;
    return privateFunction();
  };
})();

和前一种方式的主要区别:私有变量和函数是由实例共享。
优缺点:因为使用原型而增进代码复用,但每个实例都没有自己的私有变量。

结论:是使用实例变量,还是静态私有变量,根据具体需求而定。

模块模式

前面2种模式 – 为自定义类型创建私有变量和特权方法
模块模式 – 为单例创建私有变量和特权方法
单例(singleton):只有一个实例的对象。通常使用对象字面量创建单例对象。

var singleton = function(){
  //私有变量和私有函数
  var privateVariable = 10;
  function privateFunction(){
    return false;
  }
  // 可以在这里对单例进行一些初始化。。。
  //特权/公有方法和属性
  return {
    publicProperty: true,
    publicMethod : function(){
      privateVariable++;
      return privateFunction();
    }
  };
}();

使用场景:必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法。

增强的模块模式:在返回对象之前加入对其增强的代码。

var singleton = function(){
  //私有变量和私有函数
  var privateVariable = 10;
  function privateFunction(){
    return false;
  }
  //创建对象
  var object = new CustomType();
  //添加特权/公有属性和方法
  object.publicProperty = true;
  object.publicMethod = function(){
    privateVariable++;
    return privateFunction();
  };
  //返回这个对象
  return object;
}();

适应场景:适合那些单例必须是某种类型的实例,同时还必须添加某些属性和方法对其加以增强的情况。=