JavaScript 函数

函数的多变来源于参数的灵活多变和返回值的多变。如果参数是一般的数据类型或一般对象,这样的函数就是普通函数;如果函数的参数是函数,这就是我们所要知道的高级函数;如果创建的函数调用另外一部分(变量和参数已经预置),这样的函数就是偏函数。

此外,还有一点就是可选参数(optional parameter)的使用。

一、函数分类

普通函数

有函数名,参数,返回值,同名覆盖。示例代码如下:

1
2
3
function add(a, b) {
return a + b;
}

匿名函数

没有函数名,可以把函数赋值给变量和函数,或者作为回调函数使用。非常特殊的就是立即执行函数和闭包。立即执行函数示例代码如下:

1
2
3
(function(){
console.log(1)
})()

闭包示例代码如下:

1
2
3
4
5
6
var func = (function() {
var i = 1;
return function() {
console.log(i);
}
})()

高级函数

高级函数就是可以把函数作为参数和返回值的函数。如上面的闭包。ECMAScript 中也提供大量的高级函数如 forEach, every, some, reduce 等等。

偏函数

1
2
3
4
5
6
7
8
function isType(type) {
return function(obj) {
return toString.call(obj) === "[object " + type + "]"
}
}

var isString = isType('String');
var isFunction = isType('Function');

箭头函数

箭头函数表达式的语法比函数表达式更简洁,并且没有自己的 this,arguments,super 或 new.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。但它的特点就是更短,和解决匿名函数中 this 指向全局作用域的问题

本身不创建 this
  • 箭头函数中的 this 是在箭头函数定义时就决定的,而且不可修改 (call, apply, bind)
  • 箭头函数的 this 指向定义的时候,外层第一个普通函数的 this(或者说,其声明时所在的上下文的 this)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let obj = {
  a: 10,
  b: function(n) {
      let f = (n) => n + this.a;
      return f(n);
  },
  c: function(n) {
      let f = (n) => n + this.a;
      let m = {
          a: 20
      };
      return f.call(m,n);
  }
};
console.log(obj.b(1)); // 结果:11
console.log(obj.c(1)); // 结果:11
无法作为构造函数

当对构造函数 new 一个实例时,发生了如下步骤:

  • 创建了一个空对象({})
  • 为创建的空对象添加属性__proto__,并指向构造函数的原型
  • 将创建的对象作为构造函数的上下文(改变 this 指向)
  • 如果该函数没有返回对象,则返回 this

因为箭头函数并没有 prototype(即没有原型),没法让它的实例的__proto__属性指向构造函数的 prototype,所以箭头函数不能作为构造函数,否则用 new 调用时会报错!

没有 arguments

箭头函数没有自己的 arguments 对象,在箭头函数中访问 arguments 实际上获得的是上一层函数执行环境中的值。

1
2
3
4
let fn = (arr) => {
  console.log(arguments);
}
fn([1,3,9]); //Uncaught ReferenceError: arguments is not defined

因为传入箭头函数的参数数量不一,为了像 arguments 一样能获取全部参数,这里需要借助扩展运算符:

1
2
3
4
let fn = (...arg) => {
console.log(arg);
}
fn(3,82,32,11323); // [3, 82, 32, 11323]

想了解更多箭头函数可以看MDN

二、函数参数

传入明确的参数

1
2
3
function add(a, b) {
return a + b;
}

使用 arguments 对象

1
2
3
4
function add() {
var argv = Array.prototype.slice.apply(arguments);
return argv.length > 0 ? argv.reduce(function(acc, v) { return acc+=v}): '';
}

省略参数,参数默认值

1
2
3
4
5
function sub(a, b) {
a = a || 0;
b = b || 0;
return a - b;
}

对象参数

1
2
3
4
5
6
7
8
9
10
var option = {
width: 10,
height: 10
}

function area(opt) {
this.width = opt.width || 1;
this.height = opt.height || 1;
return this.width * this.height
}

ES6 中的函数参数

在 ES6 中,参数默认值,省略参数操作使用比较简便。示例代码如下:

1
var area = (width=1, height=1) => width*height

在 ES6 中,使用可选参数。示例代码如下:

1
2
3
4
var add = (...nums) => {
var numArr = [].concat(nums)
return numArr.reduce((acc, v) => acc += v)
}

解构参数

1
2
3
4
5
6
myFunc = function({x = 5,y = 8,z = 13} = {x:1,y:2,z:3}) {
console.log(x,y,z);
};

myFunc(); //1 2 3 (默认值为对象字面量)
myFunc({}); //5 8 13 (默认值为对象本身)

三、函数返回值

  1. 函数的返回值为基本数据类型,如字符串,数字,Boolean,null,undefined。示例代码如下:
1
2
3
function add(a, b) {
return a + b
}
  1. 函数的返回值为对象。示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Robot(name) {
this.name = name
}

Robot.prototype.init = function() {
return {
say: function () {
console.log('My name is ' + this.name)
}.bind(this),
dance: function(danceName) {
console.log('My dance name is ' + danceName)
}
};
}

var robotA = new Robot('A');
robotA.init().say(); // "My name is A"
var robotB = new Robot('B');
robotB.init().say(); // "My name is B"

不管是写原生还是 jQuery 插件,亦或其他插件,这种情况都不少见。更深入的了解可以参考 jQuery 源码。

  1. 返回值为函数

这个我们最为熟悉的莫过于闭包。具体可参考 老生常谈之闭包