20200225 JavaScript【2】函数

一 函数

JavaScript的函数不但是“头等公民”,而且可以像变量一样使用,具有非常强大的抽象能力。

什么是“抽象”?
抽象是数学中非常常见的概念。比如:连加、连乘符号等可扩展的抽象记法。
借助抽象,我们才能不关心底层的具体计算过程,而直接在更高的层次上思考问题。写计算机程序也是一样,函数就是最基本的一种代码抽象的方式。

1、函数的定义和调用

和c语言几乎一样,略有一丢丢不同。

function abs(x) {   //function指出这是一个函数定义(记)
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}

函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。

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

调用函数时,按顺序传入参数即可:

abs(10); // 返回10
abs(-9); // 返回9
abs(); // 返回NaN
abs(-9, 'haha', 'hehe', null); // 返回9(传入的参数比定义的参数多不会报错)

乱入——不等于号用【!== 】

2、arguments & rest

JavaScript还有一个免费赠送的关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array:

function foo(x) {
    console.log('x = ' + x); // 10
    for (var i=0; i<arguments.length; i++) { //arguments最常用于判断传入参数的个数
        console.log('arg ' + i + ' = ' + 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

rest与arguments的作用差不了太多。rest参数只能写在最后,前面用…标识,从下面的运行结果可知,传入的参数先绑定a、b,多余的参数以数组形式交给变量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参数编写一个sum()函数——

function sum(...rest) {
    var x = 0;   
    for(var k=0;k<rest.length;k++){
        x=x+rest[k];
    }
    return x;
}

注意:JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行(跟c一样)。但如果内部函数定义了与外部函数重名的变量,则内部函数的变量在内部将“屏蔽”外部函数的变量。

3、名字空间

JavaScript实际上只有一个全局作用域。参看这个链接
全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:

// 唯一的全局变量MYAPP:
var MYAPP = {};

// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;

// 其他函数:
MYAPP.foo = function () {
    return 'foo';
};

把自己的代码全部放入唯一的名字空间MYAPP中,会大大减少全局变量冲突的可能。许多著名的JavaScript库都是这么干的。

4、局部作用域

由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的。为了解决块级作用域,ES6引入了新的关键字let,
用let替代var可以申明一个块级作用域的变量

'use strict';

function foo() {
    var sum = 0;
    for (let i=0; i<100; i++) {  //let替代var,申明局部变量i
        sum += i;
    }
    // SyntaxError:
    i += 1;
}

5、常量

ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域:

'use strict';

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

6、解构赋值

可以同时对一组变量进行赋值。

var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
// x = hello, y = JavaScript, z = ES6

解构赋值在很多时候可以大大简化代码。例如,交换两个变量x和y的值,可以这么写,不再需要临时变量:

var x=1, y=2;
[x, y] = [y, x]  //交换x、y的值

7、对象的方法

“方法”功能是为了解决js语言本身的一些缺陷,所以难以用逻辑理解。下面仅记录一种用法,更深的理解参见这个链接

在一个对象中绑定函数,称为这个对象的方法。
例题——
我们给一个对象(xiaoming)绑定一个函数,就可以做更多的事情。比如,写个age()方法,返回xiaoming的年龄:

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var y = new Date().getFullYear();
        return y - this.birth;  //注意:this关键字
    }
};

xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了

this关键字——是一个特殊变量,它始终指向当前对象,也就是xiaoming这个变量。所以,this.birth可以拿到xiaoming的birth属性。

二 高阶函数

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;

廖雪峰教程里有常见的高阶函数的用法(如下),暂时跳过~
Alt

三 闭包

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章