JavaScript的函数

JavaScript的函数

[TOC]

函数的定义

  1. 普通函数定义

    1
    2
    3
    4
    5
    6
    7
    function abs(x) {
    if (x >= 0) {
    return x;
    } else {
    return -x;
    }
    }
  2. 指定变量的函数定义

    1
    2
    3
    4
    5
    6
    7
    var abs = function (x) {
    if (x >= 0) {
    return x;
    } else {
    return -x;
    }
    }; //此种方法末尾需要添加;

Arguments关键字

  • 该关键字只在函数内部齐作用,永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array。利用arguments,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值以及参数的数量

    1
    2
    3
    4
    5
    6
    7
    function foo(x) {
    alert(x); // 10
    for (var i=0; i<arguments.length; i++) {
    alert(arguments[i]); // 10, 20, 30
    }
    }
    foo(10, 20, 30);

Rest参数

  • 在ES6中定义了一种方便获取可变参数的方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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 ]
    • rest参数只能写在最后,前面用…标识,从运行结果可知,传入的参数先绑定a、b,多余的参数以数组形式交给变量rest,所以,不再需要arguments我们就获取了全部参数。
    • 如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined)。

变量 的作用域

  • 在JavaScript中,用var申明的变量实际上是有作用域的。(该作用域只包括函数,不包括快作用域)

    只有在作用域内部才可以使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    'use strict';
    function foo() {
    var temp =100;
    for (var i=0; i<100; i++) {
    //
    }
    i += 100; // 仍然可以引用变量i,因为var的局部只局限于函数内部。
    }
    temp=temp+2; // ReferenceError! 无法在函数体外引用变量x

    在es6之后,要想在块级作用域定义局部变量可以使用let关键字。

    1
    2
    3
    4
    5
    6
    7
    function foo() {
    var temp =100;
    for (let i=0; i<100; i++) {
    //
    }
    i += 100; // SyntaxError
    }

    并且在es6之后,可以通过const关键字定义常量

    1
    2
    3
    const PI = 3.14;
    PI = 3; // 某些浏览器不报错,但是无效果!
    PI; // 3.14

装饰器

  • 通过js的装饰起可以动态改变函数的行为,增加我们需要的功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var count = 0;
    var oldParseInt = parseInt; // 保存原函数
    window.parseInt = function () {
    count += 1;
    return oldParseInt.apply(null, arguments); // 调用原函数
    };
    // 测试:
    parseInt('10');
    parseInt('20');
    parseInt('30');
    count; // 3

    这样我们每次调用parseInt()的时候都可以计数一次。

高阶函数

  • 神马叫高阶函数?

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

  • Map函数

    由于map()方法定义在JavaScript的Array中,我们调用Arraymap()方法,传入我们自己的函数,就得到了一个新的Array作为结果。相当于对Array中的每个值都进行一次我们指定的函数操作。

    1
    2
    3
    4
    5
    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]
  • Reduce函数

    Array的reduce()把一个函数作用在这个Array的[x1, x2, x3…]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算,其效果就是:

    1
    2
    3
    4
    var arr = [1, 3, 5, 7, 9];
    arr.reduce(function (x, y) {
    return x + y;
    }); // 将array中的数据进行了累加操作
  • Filter函数

    通过Filter我们可以过滤我们需要的数组元素。和map()类似,Arrayfilter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。

    1
    2
    3
    4
    5
    var arr = [1, 2, 4, 5, 6, 9, 10, 15];
    var r = arr.filter(function (x) {
    return x % 2 !== 0;
    });
    r; // 删除数组中的偶数,返回结果为[1, 5, 9, 15]

    去除数组中的重复元素:

    1
    2
    3
    4
    5
    r = arr.filter(function (element, index, self) {
    return self.indexOf(element) === index;
    });
    //这里的回调函数第一个参数代表数组中的某个元素,第二个代表下标,第三个代表数组本身。
    //去除重复元素依靠的是indexOf总是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,因此被filter滤掉了。
  • 排序函数

    常规定,对于两个元素xy,如果认为x < y,则返回-1,如果认为x == y,则返回0,如果认为x > y,则返回1

    所以自定义排序函数(sort会直接对数组内容进行修改):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var arr = [10, 20, 1, 2];
    arr.sort(function (x, y) {
    if (x < y) {
    return -1;
    }
    if (x > y) {
    return 1;
    }
    return 0;
    }); // [1, 2, 10, 20]

函数的闭包

  • 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

    举个栗子:正常的求和函数

    1
    2
    3
    4
    5
    6
    function sum(arr) {
    return arr.reduce(function (x, y) {
    return x + y;
    });
    }
    sum([1, 2, 3, 4, 5]); // 15

    使用闭包的求和函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function lazy_sum(arr) {
    var sum = function () {
    return arr.reduce(function (x, y) {
    return x + y;
    });
    }
    return sum;
    }
    var f1 = lazy_sum([1, 2, 3, 4, 5]); // function sum()这里并不会立即执行函数
    f1(); // 15 调用函数的时候才会真正的执行计算。
    var f2 = lazy_sum([1, 2, 3, 4, 5]);
    f1 === f2; // false每次返回的都是不同的函数。

    我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为闭包(Closure)。

    匿名函数会有这种功能其主要原因是因为我们创建的匿名函数并没有执行,如果想要创建匿名函数并执行,可以使用以下语法:

    1
    (function (x) { return x * x }) (3);

箭头函数

  • 在ES6中新增了一种匿名函数标准,可以使用箭头语法。

    1
    2
    3
    4
    5
    x => x * x
    //作用相当于
    function (x) {
    return x * x;
    }

    如果包涵多个表示式:

    1
    2
    3
    4
    5
    6
    7
    8
    x => {
    if (x > 0) {
    return x * x;
    }
    else {
    return - x * x;
    }
    }

    包涵多个参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 两个参数:
    (x, y) => x * x + y * y
    // 无参数:
    () => 3.14
    // 可变参数:
    (x, y, ...rest) => {
    var i, sum = x + y;
    for (i=0; i<rest.length; i++) {
    sum += rest[i];
    }
    return sum;
    }