1、概述

  • 函数声明
  • 采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。
var print = function x(){
  console.log(typeof x);
};

x
// ReferenceError: x is not defined
  • 这种写法的用处有两个,一是可以在函数体内部调用自身,二是方便除错。
  • 函数名提升
  • JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。
  • 如果像下面例子那样,采用function命令和var赋值语句声明同一个函数,由于存在函数提升,最后会采用var赋值语句的定义。
var f = function () {
  console.log('1');
}

function f() {
  console.log('2');
}

f() // 1

2、函数的属性和方法

  • length属性
  • 函数的length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。
function f(a, b) {}
f.length // 2
// 上面代码定义了空函数f,它的length属性就是定义时的参数个数。不管调用时输入了多少个参数,length属性始终等于2。

// length属性提供了一种机制,判断定义时和调用时参数的差异,以便实现面向对象编程的“方法重载”(overload)。
  • toString()
  • 函数的toString()方法返回一个字符串,内容是函数的源码。

3、函数作用域

  • 定义
  • 作用域(scope)指的是变量存在的范围。在 ES5 的规范中,JavaScript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。ES6 又新增了块级作用域。
  • 注意,对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。
  • 函数内部变量提升
  • 与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
  • 函数本身作用域
  • 函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关(词法作用域)。
var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();
}

f() // 1

4、参数

  • 参数的省略
  • 函数参数不是必需的,JavaScript 允许省略参数。
function f(a, b) {
  return a;
}

f(1, 2, 3) // 1
f(1) // 1
f() // undefined

f.length // 2

// 没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入undefined。
  • 传递方式
  • 函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)。这意味着,在函数体内修改参数值,不会影响到函数外部。
var p = 2;

function f(p) {
  p = 3;
}
f(p);

p // 2
// 上面代码中,变量p是一个原始类型的值,传入函数f的方式是传值传递。因此,在函数内部,p的值是原始值的拷贝,无论怎么修改,都不会影响到原始值。
  • 如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。
var obj = { p: 1 };

function f(o) {
  o.p = 2;
}
f(obj);

obj.p // 2
  • 如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。
var obj = [1, 2, 3];

function f(o) {
  o = [2, 3, 4];
}
f(obj);

obj // [1, 2, 3]

// 这是因为,形式参数(o)的值实际是参数obj的地址,重新对o赋值导致o指向另一个地址,保存在原地址上的值当然不受影响。
  • 同名参数
  • 如果有同名的参数,则取最后出现的那个值;即使后面的a没有值或被省略,也是以其为准。
function f(a, a) {
  console.log(a);
}

f(1, 2) // 2

function f(a, a) {
  console.log(a);
}

f(1) // undefined
  • arguments对象
  • 定义
  • 正常模式下,arguments对象可以在运行时修改。
  • 严格模式下,arguments对象与函数参数不具有联动关系。也就是说,修改arguments对象不会影响到实际的函数参数。
  • 与数组的关系
  • 它是类似数组的对象,可转换为数组。