函数的声明

JavaScript 有三种声明函数的方法。

(1)function 命令

 

function命令声明的代码区块,就是一个函数。function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。

 

function print(s) {

  console.log(s);

}

上面的代码命名了一个print函数,以后使用print()这种形式,就可以调用相应的代码。这叫做函数的声明(Function Declaration)。

 

(2)函数表达式

 

除了用function命令声明函数,还可以采用变量赋值的写法。

 

var print = function(s) {

  console.log(s);

};

这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression),因为赋值语句的等号右侧只能放表达式。

 

 

3Function 构造函数

 

第三种声明函数的方式是Function构造函数。

 

var add = new Function(

  'x',

  'y',

  'return x + y'

);

 

// 等同于

function add(x, y) {

  return x + y;

}

上面代码中,Function构造函数接受三个参数,除了最后一个参数是add函数的“函数体”,其他参数都是add函数的参数。

 

不能在条件语句中声明函数

根据 ES5 的规范,不得在非函数的代码块中声明函数,最常见的情况就是if和try语句。

 

if (foo) {

  function x() {}

}

 

try {

  function x() {}

} catch(e) {

  console.log(e);

}

上面代码分别在if代码块和try代码块中声明了两个函数,按照语言规范,这是不合法的。但是,实际情况是各家浏览器往往并不报错,能够运行。

 

函数的属性和方法

name 属性

函数的name属性返回函数的名字。

 

function f1() {}

f1.name // "f1"

如果是通过变量赋值定义的函数,那么name属性返回变量名。

 

length属性

函数的length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。

 

function f(a, b) {}

f.length // 2

 

 

toString()

函数的toString方法返回一个字符串,内容是函数的源码。

 

function f() {

  a();

  b();

  c();

}

 

f.toString()

// function f() {

//  a();

//  b();

//  c();

// }

 

函数作用域

定义

全局

函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。

 

var v = 1;

 

function f() {

  console.log(v);

}

 

f()

// 1

上面的代码表明,函数f内部可以读取全局变量v。

 

 

局部

在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)。

 

function f(){

  var v = 1;

}

 

v // ReferenceError: v is not defined

上面代码中,变量v在函数内部定义,所以是一个局部变量,函数之外就无法读取。

 

 

参数

arguments 对象

由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。

 

arguments对象包含了函数运行时的所有参数,arguments[0]就是第

一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。

 

var f = function (one) {

  console.log(arguments[0]);

  console.log(arguments[1]);

  console.log(arguments[2]);

}

 

f(1, 2, 3)

// 1

// 2

// 3

 

闭包

function f1() {

  var n = 999;

  function f2() {

    console.log(n);

  }

  return f2;

}

 

var result = f1();

result(); // 999

上面代码中,函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了。

 

闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

 

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

 

 

function createIncrementor(start) {

  return function () {

    return start++;

  };

}

 

var inc = createIncrementor(5);

 

inc() // 5

inc() // 6

inc() // 7

上面代码中,start是函数createIncrementor的内部变量。通过闭包,start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包inc使得函数createIncrementor的内部环境,一直存在。所以,闭包可以看作是函数内部作用域的一个接口。

 

为什么会这样呢?原因就在于inc始终在内存中,而inc的存在依赖于createIncrementor,因此也始终在内存中,不会在调用结束后,被垃圾回收机制回收。

 

闭包的另一个用处,是封装对象的私有属性和私有方法。

 

function Person(name) {

  var _age;

  function setAge(n) {

    _age = n;

  }

  function getAge() {

    return _age;

  }

 

  return {

    name: name,

    getAge: getAge,

    setAge: setAge

  };

}

 

var p1 = Person('张三');

p1.setAge(25);

p1.getAge() // 25

上面代码中,函数Person的内部变量_age,通过闭包getAge和setAge,变成了返回对象p1的私有变量。

 

 

立即调用的函数表达式(IIFE)

解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。

 

(function(){ /* code */ }());

// 或者

(function(){ /* code */ })();

上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称 IIFE。

 

posted on 2018-02-24 10:48  Sharpest  阅读(111)  评论(0编辑  收藏  举报