闭包

闭包是 JavaScript 中最强大的特性之一。JavaScript 允许函数嵌套,并且内部函数可以访问定义在外部函数中的所有变量和函数,以及外部函数能访问的所有变量和函数。但是,外部函数却不能够访问定义在内部函数中的变量和函数。这给内部函数的变量提供了一定的安全性。此外,由于内部函数可以访问外部函数的作用域,因此当内部函数生存周期大于外部函数时,外部函数中定义的变量和函数的生存周期比内部函数执行时间长。当内部函数以某一种方式被任何一个外部函数作用域访问时,一个闭包就产生了。

var pet = function(name) {          //外部函数定义了一个变量"name"
  var getName = function() {            
    //内部函数可以访问 外部函数定义的"name"
    return name; 
  }
  //返回这个内部函数,从而将其暴露在外部函数作用域
  return getName;               
};
myPet = pet("Vivie");

myPet();                            // 返回结果 "Vivie"

如果一个闭包的函数用外部函数的变量名定义了同样的变量,那在外部函数域将再也无法指向该变量。

var createPet = function(name) {  // Outer function defines a variable called "name"
  return {
    setName: function(name) {    // Enclosed function also defines a variable called "name"
      name = name;               // ??? How do we access the "name" defined by the outer function ???
    }
  }
}

arguments 对象

函数的实际参数会被保存在一个类似数组的arguments对象中。在函数内,你可以按如下方式找出传入的参数:

arguments[i]

例如,设想有一个用来连接字符串的函数。唯一事先确定的参数是在连接后的字符串中用来分隔各个连接部分的字符(译注:比如例子里的分号“;”)。该函数定义如下:

function myConcat(separator) {
   var result = ''; // 把值初始化成一个字符串,这样就可以用来保存字符串了!!
   var i;
   // iterate through arguments
   for (i = 1; i < arguments.length; i++) {
      result += arguments[i] + separator;
   }
   return result;
}

你可以给这个函数传递任意数量的参数,它会将各个参数连接成一个字符串“列表”:

// returns "red, orange, blue, "
myConcat(", ", "red", "orange", "blue");

// returns "elephant; giraffe; lion; cheetah; "
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// returns "sage. basil. oregano. pepper. parsley. "
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");

提示:arguments变量只是 ”类数组对象“,并不是一个数组。称其为类数组对象是说它有一个索引编号和length属性。尽管如此,它并不拥有全部的Array对象的操作方法。

函数参数

默认参数

在JavaScript中,函数参数的默认值是undefined。然而,在某些情况下设置不同的默认值是有用的。这时默认参数可以提供帮助。

function multiply(a, b = 1) {
  return a*b;
}

multiply(5); // 5

剩余参数

剩余参数语法允许将不确定数量的参数表示为数组。在下面的例子中,使用剩余参数收集从第二个到最后参数。然后,我们将这个数组的每一个数与第一个参数相乘。

function multiply(multiplier, ...theArgs) {
  return theArgs.map(x => multiplier * x);
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]

箭头函数

this 的词法

在箭头函数出现之前,每一个新函数都重新定义了自己的 this 值(在严格模式下,一个新的对象在构造函数里是未定义的,以“对象方法”的方式调用的函数是上下文对象等)。以面向对象的编程风格,这样着实有点恼人。

function Person() {
  // The Person() constructor defines `this` as itself.
  this.age = 0;

  setInterval(function growUp() {
    // In nonstrict mode, the growUp() function defines `this` 
    // as the global object, which is different from the `this`
    // defined by the Person() constructor.
    this.age++;
  }, 1000);
}

var p = new Person();

在ECMAScript 3/5里,通过把this的值赋值给一个变量可以修复这个问题。

function Person() {
  var self = this; // Some choose `that` instead of `self`. 
                   // Choose one and be consistent.
  self.age = 0;

  setInterval(function growUp() {
    // The callback refers to the `self` variable of which
    // the value is the expected object.
    self.age++;
  }, 1000);
}

另外,创建一个约束函数可以使得 this值被正确传递给 growUp() 函数。

箭头功能捕捉闭包上下文的this值,所以下面的代码工作正常。

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| properly refers to the person object
  }, 1000);
}

var p = new Person();

函数方法

apply(thisArg[, argsArray])

call(thisArg[,[param1,param2])

该方法的作用和 apply() 方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。

结构图

数据类型