函数进阶

XiLaiTL大约 6 分钟

函数进阶

函数“提升”

在之前的教程中我们已经了解了 "hoisting(提升)"。提升是 JavaScript 默认将当前作用域提升到前面去的行为。提升应用在变量的声明与函数的声明。因此,函数与变量一样,可以在声明之前调用:

myFunction(5);

function myFunction(y) {
    return y * y;
}

使用表达式定义函数时无法提升。

ES6箭头函数

ES6 新增了箭头函数。箭头函数等价于其他语言的lambda表达式。

箭头函数表达式的语法比普通函数表达式更简洁。

(参数1, 参数2,, 参数N) => { 函数声明; }

(参数1, 参数2,, 参数N) => 表达式;
// 相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }//表达式只有一个

当只有一个参数时,圆括号是可选的:

(单一参数) => {函数声明}
单一参数 => {函数声明}

没有参数的函数应该写成一对圆括号:

() => {函数声明}

例子:

var x = function(x, y) {
     return x * y;
}// ES5

const x = (x, y) => x * y;// ES6

var p=x(1,2);//调用方式

嵌套函数

嵌套函数:在函数中声明的函数就是嵌套函数,嵌套函数只能在当前函数中可以访问,在当前函数外无法访问。

function fu() {
    function zi() {
    	console.log("我是儿子")
    }
    zi();
}
fu();

匿名函数

匿名函数:没有名字的函数就是匿名函数,它可以让一个变量来接收,也就是用 “函数表达式” 方式创建和接收。

var fun = function () {
	alert("我是一个匿名函数");
}
fun();

立即执行函数

立即执行函数:函数定义完,立即被调用,这种函数叫做立即执行函数,立即执行函数往往只会执行一次。同样的,箭头函数也有如此效果。

(function () {
	alert("我是一个匿名函数");
})();

函数返回类型

函数做值

值类型的返回值让函数可以当成值来调用

function myFunction(a, b) {
    return a * b;
}

var x = myFunction(4, 3);

函数为函数

函数类型的返回值可以让函数当成新函数来调用。

在C语言中,只能使用函数指针来形成类似的操作。

var add = function(x){
    return function(y){
        return x + y
    }
}
//ES6
var add = x => (y => x + y);
//调用
var add2 = add(2);
var add200 = add(200);

add2(2); // =>4
add200(50); // =>250

函数是对象

作为对象的函数,自然有它的属性和方法可供调用。JavaScript 函数有个内置的对象 arguments 对象。

argument 对象包含了函数调用的参数数组。

arguments.length 属性返回函数调用过程接收到的参数个数

//通过这种方式你可以很方便的找到最大的一个参数的值:
x = findMax(1, 123, 500, 115, 44, 88);
function findMax() {
    var i, max = arguments[0];
    if(arguments.length < 2) return max;
    for (i = 0; i < arguments.length; i++) {
        if (arguments[i] > max) {
            max = arguments[i];
        }
    }
    return max;
}

call()和apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身。

function myFunction(a, b) {
    return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray);  // 返回 20

function myFunction(a, b) {
    return a * b;
}
myObject = myFunction.call(myObject, 10, 2);     // 返回 20

两个方法都使用了对象本身作为第一个参数。 两者的区别在于第二个参数: apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。

在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。

在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。

函数闭包

闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。直观的说就是形成一个不销毁的栈环境。

计数器困境:

var counter = 0;
 
function add() {
   return counter += 1;
}
add();
add();
add();
// 计数器现在为 3
//但问题来了,页面上的任何脚本都能改变计数器,即便没有调用 add() 函数。
/*——————————————————————————————————*/
function add() {
    var counter = 0;
    return counter += 1;
}
add();
add();
add();
// 如果我在函数内声明计数器,如果没有调用函数将无法修改计数器的值:
//但是 现在 本意是想输出 3, 但事与愿违,输出的都是 1 !
/*——————————————————————————————————*/
function add() {
    var counter = 0;
    function plus() {counter += 1;}
    plus();    
    return counter; 
}
//这样可以解决函数调用的问题,但是,不能在外界被调用
/*——————————————————————————————————*/
var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
 
add();
add();
add();
//此时由于函数自调用,本身add=function () {return counter += 1;}
//调用add()计数器+1,而计数器受匿名函数的作用域保护,只能通过 add 方法修改,也只能通过add()访问到值
// 计数器为 3

在ES6中,闭包书写为:

var add=(()=>{var counter=0; return ()=>counter+=1;})()

默认参数

ES5 中如果函数在调用时未提供隐式参数,参数会默认设置为: undefined

有时这是可以接受的,但是建议最好为参数设置一个默认值:

function myFunction(x, y) {
    if (y === undefined) {
          y = 0;
    } 
}
//或者
function myFunction(x, y) {
    y = y || 0;
}

如果y已经定义 , y || 返回 y, 因为 y 是 true, 否则返回 0, 因为 undefined 为 false。

如果函数调用时设置了过多的参数,参数将无法被引用,因为无法找到对应的参数名。 只能使用 arguments 对象来调用。

ES6 函数可以自带参数

ES6 支持函数带有默认参数,就可以省略了判断 undefined 和 || 的操作:

function myFunction(x, y = 10) {
    // y is 10 if not passed or undefined
    return x + y;
}
myFunction(0, 2) // 输出 2
myFunction(5); // 输出 15, y 参数的默认值

函数式编程思维

函数式编程与命令式编程最大的不同其实在于:函数式编程关心数据的映射,命令式编程关心解决问题的步骤。即函数式编程关心类型(代数结构)之间的关系,命令式编程关心解决问题的步骤

这里的映射就是数学上「函数」的概念——一种东西和另一种东西之间的对应关系。这也是为什么「函数式编程」叫做「函数」式编程。函数式编程中,函数才是一等公民,一切都是函数。(与一切都是对象相对应)。一定意义上,函数式对应lambda演算而命令式对应图灵机。而lambda演算则有如下规则:

  1. 变量
  2. 函数
  3. 替代(并非赋值)

函数式编程有以下特点:

  • 变量不可变:闭包。
  • 无法实现循环:依赖递归(循环是递推)
  • 高阶函数:返回值为函数

函数的柯里化

函数柯里化(curry)的定义很简单:传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。例如:

var add = x => (y => x + y);

var add2 = add(2);
var add200 = add(200);

add2(2); // =>4
add200(50); // =>250

将加法变成了两个执行函数的步骤。

函数组合

//两个函数的组合
var compose = function(f, g) {
    return function(x) {
        return f(g(x));
    };
};

//ES6
var compose = (f, g) => (x => f(g(x)));

var add1 = x => x + 1;
var mul5 = x => x * 5;

compose(mul5, add1)(2);
// =>15 

参考资料:

上次编辑于:
贡献者: XiLaiTL