js Function 函数

函数

var abs = function (x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
};

函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。所以,函数内部经过条件判断和循环能够实现很是复杂的逻辑。数组

在这种方式下,function (x) { ... }是一个匿名函数,它没有函数名。可是,这个匿名函数赋值给了变量abs,因此,经过变量abs就能够调用该函数函数

若是没有return语句,函数执行完毕后也会返回结果,只是结果为undefined。rest

要避免收到undefined,能够对参数进行检查:code

function abs(x) {
    if (typeof x !== 'number') {
        throw 'Not a number';
    }

    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}

arguments

它只在函数内部起做用,而且永远指向当前函数的调用者传入的全部参数。arguments相似Array但它不是一个Array:对象

function foo(x) {
    alert(x); // 10
    for (var i=0; i<arguments.length; i++) {
        alert(arguments[i]); // 10, 20, 30
    }
}

foo(10, 20, 30);

利用arguments,你能够得到调用者传入的全部参数。也就是说,即便函数不定义任何参数,仍是能够拿到参数的值:索引

function abs() {
    if (arguments.length === 0) {
        return 0;
    }
    var x = arguments[0];
    return x >= 0 ? x : -x;
}

abs(); // 0
abs(10); // 10
abs(-9); // 9

实际上arguments最经常使用于判断传入参数的个数。你可能会看到这样的写法:ip

// foo(a[, b], c)
// 接收2~3个参数,b是可选参数,若是只传2个参数,b默认为null:
function foo(a, b, c) {
    if (arguments.length === 2) {
        // 实际拿到的参数是a和b,c为undefined
        c = b; // 把b赋给c
        b = null; // b变为默认值
    }
    // ...
}

要把中间的参数b变为“可选”参数,就只能经过arguments判断,而后从新调整参数并赋值。字符串

rest

因为JavaScript函数容许接收任意个参数,因而咱们就不得不用arguments来获取全部参数:io

function foo(a, b) {
    var i, rest = [];
    if (arguments.length > 2) {
        for (i = 2; i<arguments.length; i++) {
            rest.push(arguments[i]);
        }
    }
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

为了获取除了已定义参数a、b以外的参数,咱们不得不用arguments,而且循环要从索引2开始以便排除前两个参数,这种写法很别扭,只是为了得到额外的rest参数,有没有更好的方法?console

ES6标准引入了rest参数,上面的函数能够改写为:

function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

foo(1);
// 结果:
// a = 1
// b = undefined
// Array []

rest参数只能写在最后,前面用...标识,从运行结果可知,传入的参数先绑定a、b,多余的参数以数组形式交给变量rest,因此,再也不须要arguments咱们就获取了所有参数。

若是传入的参数连正常定义的参数都没填满,也没关系,rest参数会接收一个空数组(注意不是undefined)。

当心你的return语句

前面咱们讲到了JavaScript引擎有一个在行末自动添加分号的机制,这可能让你栽到return语句的一个大坑:

function foo() {
    return { name: 'foo' };
}

foo(); // { name: 'foo' }

若是把return语句拆成两行:

function foo() {
    return
        { name: 'foo' };
}

foo(); // undefined

要当心了,因为JavaScript引擎在行末自动添加分号的机制,上面的代码实际上变成了:

function foo() {
    return; // 自动添加了分号,至关于return undefined;
        { name: 'foo' }; // 这行语句已经无法执行到了
}

因此正确的多行写法是:

function foo() {
    return { // 这里不会自动加分号,由于{表示语句还没有结束
        name: 'foo'
    };
}

变量提高

JavaScript的函数定义有个特色,它会先扫描整个函数体的语句,把全部申明的变量“提高”到函数顶部:

'use strict';

function foo() {
    var x = 'Hello, ' + y;
    alert(x);
    var y = 'Bob';
}

foo();

虽然是strict模式,但语句var x = 'Hello, ' + y;并不报错,缘由是变量y在稍后申明了。可是alert显示Hello, undefined,说明变量y的值为undefined。这正是由于JavaScript引擎自动提高了变量y的声明,但不会提高变量y的赋值。

对于上述foo()函数,JavaScript引擎看到的代码至关于:

function foo() {
    var y; // 提高变量y的申明
    var x = 'Hello, ' + y;
    alert(x);
    y = 'Bob';
}

因为JavaScript的这一怪异的“特性”,咱们在函数内部定义变量时,请严格遵照“在函数内部首先申明全部变量”这一规则。最多见的作法是用一个var申明函数内部用到的全部变量:

function foo() {
    var
        x = 1, // x初始化为1
        y = x + 1, // y初始化为2
        z, i; // z和i为undefined
    // 其余语句:
    for (i=0; i<100; i++) {
        ...
    }
}

高阶函数

高阶函数英文叫Higher-order function。那么什么是高阶函数?

JavaScript的函数其实都指向某个变量。既然变量能够指向函数,函数的参数能接收变量,那么一个函数就能够接收另外一个函数做为参数,这种函数就称之为高阶函数。

一个最简单的高阶函数:

function add(x, y, f) {
    return f(x) + f(y);
}

当咱们调用add(-5, 6, Math.abs)时,参数x,y和f分别接收-5,6和函数Math.abs,根据函数定义,咱们能够推导计算过程为:

x = -5;
y = 6;
f = Math.abs;
f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11;
return 11;

用代码验证一下:

add(-5, 6, Math.abs); // 11

编写高阶函数,就是让函数的参数可以接收别的函数。

举例说明,好比咱们有一个函数f(x)=x2,要把这个函数做用在一个数组[1, 2, 3, 4, 5, 6, 7, 8, 9]上,就能够用map实现以下:

因为map()方法定义在JavaScript的Array中,咱们调用Array的map()方法,传入咱们本身的函数,就获得了一个新的Array做为结果:

function pow(x) {
    return x * x;
}

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]

map()传入的参数是pow,即函数对象自己。

你可能会想,不须要map(),写一个循环,也能够计算出结果:

var f = function (x) {
    return x * x;
};

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var result = [];
for (var i=0; i<arr.length; i++) {
    result.push(f(arr[i]));
}

的确能够,可是,从上面的循环代码,咱们没法一眼看明白“把f(x)做用在Array的每个元素并把结果生成一个新的Array”。

因此,map()做为高阶函数,事实上它把运算规则抽象了,所以,咱们不但能够计算简单的f(x)=x2,还能够计算任意复杂的函数,好比,把Array的全部数字转为字符串:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']

只须要一行代码。

相关文章
相关标签/搜索