《JavaScript高级程序设计》第7章 函数表达式

定义函数的方式有两种:函数声明和函数表达式

// 函数声明
function function_name(argument) {
  // body...
}

// 函数表达式
var function_name = function (argument) {
  // body...
}

由于函数声明提升,在执行之前会先读取函数声明,所以调用的语句可以放在函数声明之前。但是函数表达式则不可以把调用的语句放在之前。

sayHi();  // 不会报错
function sayHi() {
  console.log("Hi!");
}

sayHi();  // 错误:函数不存在
var sayHi = function () {
  console.log("Hi!");
}

7.1 递归

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

function factorial(num) {
  if (num <= 1) {
    return 1;
  } else {
    // arguments.callee是一个指向正在执行的函数的指针
    return num * arguments.callee(num - 1);
  }
}

在编写递归函数时,使用arguments.callee总比使用函数名更保险(避免函数名突然被赋成null之类的导致出错)

但在严格模式下不可以访问arguments.callee,可以使用命名函数表达式来达成同样的效果。

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

7.2 闭包

闭包是一个函数,这个函数可以访问另一个函数作用域中的变量。

function createComparison(propertyName) {
  return function (object1, object2) {
    // 以下两行代码访问了外部函数中的变量propertyName
    // 即使这个函数被返回了,而且在其他地方被调用了,它仍然可以访问变量propertyName
    var value1 = object1[propertyName];
    var value2 = object2[propertyName];

    if (value1 < value2) {
      return -1;
    } else if (value1 > value2) {
      return 1;
    } else {
      return 0;
    }
  };
}

上面的例子中,propertyName是在createComparison函数的作用域中,但里面的匿名函数被返回了之后,就算在其他地方被调用,也能访问到propertyName。

我的理解:通常的函数在执行完毕之后,其局部活动对象(包括arguments和局部变量)就会被销毁。但是如果一个函数体内有闭包的话(也就是它里面有另一个函数,这个函数引用了它的局部活动对象),则在这个函数执行完毕之后,其局部活动对象也不会被销毁,因为它体内的闭包的作用域链仍然在引用这个活动对象。

7.2.1 闭包和变量

function createFunctions() {
  var result = new Array();
  for (var i = 0; i < 10; i++) {
    result[i] = function () {
      return i;
    }
  }
  return result;
}

var iFun = createFunctions();
console.log(iFun[0]());
// 当我调用iFun的时候,createFunctions早就执行完毕了,这时候i的值为10,而由于闭包的缘故i没有被销毁
// 所以实际上iFun数组的所有函数引用的i都是同一个i,值都为10
function createFunctions() {
  var result = new Array();

  for (var i = 0; i < 10; i++) {
    result[i] = function(num) {  // B
      return function() {
        return num;  // A
      }
    }(i);
  }
  return result;
}

var iFun = createFunctions();
console.log(iFun[0]());
// 这个与上一个函数的不同之处在于,当我调用iFun的时候,进的是A位置
// 就算是那个有num参数的匿名函数(B位置)已经执行完了,它的num保存的值没有变过,还是当时传进去的索引i
// 对于iFun数组的每一个函数,他们的外层B都是不同的(num不同),就能达成目的

7.2.2 关于this对象

this问题丢失的实质:闭包通常在另一个作用域中被调用,并且它有自己的this(通常来说是window,因为闭包被调用的作用域通常是全局作用域),会屏蔽掉外层作用域的this(比如说以下例子中的object)。

var object = {
  name: "My Object",
  getNameFunc: function() { // B
    return function() {
      return this.name;  // 调用的时候已经在全局作用域下,屏蔽掉了B位置作用域的this(也就是object)
    }
  };
}

console.log(object.getNameFunc()()); // "The Window"

而that模式能修复这个问题的原因:因为闭包中没有自己的that呀,所以它只能往上找,找到外层作用域中的that了。

7.4 私有变量

任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。

但是通过闭包,我们可以创建用于访问私有变量的公有方法,这种公有方法叫做特权方法。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:DIYgod
链接:https://www.anotherhome.net/2073
来源:Anotherhome

function MyObject() {
    // 私有变量和私有函数,外部无法访问
    var privateVariable = 'DIYgod';
    function privateFunction() {
        console.log('lalala');
    }
 
    // 特权方法
    this.publicMethod = function () {
        console.log(privateVariable);
        privateFunction();
    };
}
var o = new MyObject();
o.publicMethod();  // DIYgod lalala
o.privateFunction();  // Uncaught TypeError: o.privateFunction is not a function

 

posted @ 2016-03-14 14:34  寄生蠕虫  阅读(290)  评论(0编辑  收藏  举报