JavaScript函数式编程——函数的定义与参数

定义与参数

函数式的不同点

函数是程序执行过程中的主要模块单元。除了全局JavaScript代码是在页面构建的阶段执行的。由于我们的大多数代码会作为函数调用来执行,通用强大的构造器赋予代码很大的灵活性和控制力。常用功能:

  • 对象可通过字面量来创建{}
  • 对象可以复制给变量、数组项、或其他对象的属性。
var ninja = {};
ninjaArray.push({});
ninja.data = {};
  • 对象可以作为参数传递给函数
function hide(ninja){
  ninja.visibility = false;
} hide({});
  • 对象可以作为函数的返回值
function returnNewNinja(){
    return {};
}
  • 对象能够具有动态创建和分配的属性
var ninja = {};
ninja.name = "Hanzo";

函数是第一类对象

JavaScript中函数拥有对象的所有能力,也因此函数可被作为任意其他类型对象来对待。函数是第一类对象则函数也能实现以下的功能:

  • 通过字面量创建
function ninjaFunction(){}
  • 复制给变量、数组项或其他对象的属性
var ninjaFunction = function() {};
ninjaArray.push(function(){});
ninja.data = function(){};
  • 作为函数的参数来传递
function call(ninjaFunction){
 ninjaFunction();
} 
call(function(){});
  • 作为函数的返回值
function returnNewNinjaFunction(){
 return function(){};   
}
  • 具有动态创建和分配的属性
var ninjaFunction = function(){};
ninjaFunction.ninja = "Hanzo";

对象能做的任何一件事,函数也能做。函数也是对象,唯一的特殊之处在于它是可调的。即函数会被调用以便执行某项动作。第一类对象的特点之一是,它能够作为参数传入函数。如果我们将某个函数作为参数传入另一个函数,传入函数会在应用程序执行的某个时间点才执行。这就是所谓的回调函数(callback funciton)

回调函数

每当我们建立一个将在随后调用的函数时,无论是在事件处理阶段通过浏览器还是通过其他代码,我们都是建立一个回调。

function useless(ninjaCallback) {
 return ninjaCallback();
}

函数作为另外一个函数的参数,随后通过参数来调用该函数。

var text = "Domo arigato!";
console.log("Before defining functions");
function useless(ninjaCallback) {
  console.log("In useless function");
  return ninjaCallback();
}  
function getText() {
 console.log("In getText function");
 return text;
}
function assert(condition,text) {
    if(condition){
        console.log(text);
    }
}
console.log("Before making all the calls");
assert(useless(getText) === text,"The useless function works! " + text);
console.log("After the calls have been made");

输出:

Before defining functions
Before making all the calls
In useless function
In getText function
The useless function works! Domo arigato!
After the calls have been made

getText函数作为参数传入useless函数。回调函数callback()的调用让getText函数得到执行。

JavaScript的重要特征之一就是可以在表达式出现的的任意位置创建函数,初次之外这种方式能使代码更紧凑和易于理解。

使用比较器排序

var values = [0, 3, 2, 5, 7, 4, 8, 1];
values.sort(function(value1, value2) {
 return value2 - value1;
});
console.log(values)

JavaScript每次需要比较两个值的时候都会调用我们提供的回调函数。

函数作为对象

  • 在集合中存储函数使我们轻易管理关联的函数
  • 记忆让函数能够记住上次计算得到的值,从而提高后续调用的性能。

存储函数

在某些例子中,我们会存储唯一的函数集合。当我们向这样的集合中添加函数时,会面临的两个问题:哪些函数对集合来说是新函数,从而加入该集合中。哪些函数已经存在集合中,从而不需要添加到集合中。管理回调函数集合时,我们并不希望存在重复函数,否则一个事件会导致同一个回调函数被多次调用。

function assert(condition, text) {
    if(condition){
        console.log(text);
    }
}
var store = {
    nextId : 1,
    cache : {},
    add : function(fn){
        if (!fn.id) {
            fn.id = this.nextId++;
            this.cache[fn.id] = fn;
            return true;
        }
    }

};

function ninja() {}

assert(store.add(ninja), "Funciton was safely added.");
assert(!store.add(ninja), "But it was only added once.");
Funciton was safely added.
But it was only added once.

在这程序中我们创建了一个对象赋值给变量store,这个变量中存储的是唯一的函数集合,这个对象有两个属性:一个是下一个可用的id,另外一个缓存着已经保存的函数。函数通过add()方法添加到缓存中。在add函数中,我们首先检查给函数是否已经存在id属性了,如果该函数已经存在id属性了,那么我们假设该函数已经被处理了,如果没有则分配一个id给这个函数,然后添加到缓存中。

自记忆函数

记忆化(memoization)是一种构建函数的处理过程,能够记住上次的计算结果。这样既避免复杂又重复的计算可以显著的提升性能。

function assert(condition, text) {
    if(condition) {
        console.log(text);
    }
}

function isPrime(value){
    //如果不存在属性就创建
    if (!isPrime.answers) {
        isPrime.answers = {};
    }
    if (isPrime.answers[value] !== undefined) {
        return isPrime.answers[value];
    }
    //0和1 不是素数
    var prime = value !==0 && value !==1 ;
    for (var i  = 2; i <value; i++) {
        if (value %i === 0) {
            prime = false;
            break;
        }
    }
    return isPrime.answers[value] = prime;
}

assert(isPrime(5),"5 is prime!");
assert(isPrime.answers[5], "The answer was cached")

输出

5 is prime!
The answer was cached

优点:

  • 性能较高
  • 不需要额外的初始化

缺点:

  • 任何的缓存都是以牺牲内存为代价的
  • 缓存逻辑不应该和业务逻辑混合
  • 很难估计算法的复杂度

函数的定义

JavaScript函数通常由函数数字面量(function literal)来创建函数值,就像数字字面量创建一个数字值一样。

函数定义:

  • 函数声明(function declarations)和函数表达式(function expressions)
  • 箭头函数(lambda函数)ES6新增的JavaScript标准
  • 函数构造函数
  • 生成器函数——ES6新增功能,不同与普通函数的函数,在应用程序执行过程中,这种函数能够退出再重新进入。

函数声明和函数表达式

JavaScript中定义函数最常用的方式是函数声明和函数表达式。

function myFunctionName(myFirstArg, mySecondArg ) {
	myStatement1;
    myStatement2;
}

示例

function samurai() {
    return "samurai here";
}

function ninja() {
    function hiddenNinja() {
        return "ninja here";
    }
    return hiddenNinja();
}

可以看到在函数中定义函数。

函数表达式
JavaScript中的函数是第一类对象。

使用数字字面量

var a = 3;
myFunction(4);

使用函数字面量

var a = function(){};
myFunction(function(){});

这种叫做函数表达式。

var myFunc = function(){};

对于函数声明来说,函数名是强制的,但是函数表达式来说,函数名则完全是可选的。

函数声明必须具有函数名是因为它们是独立语句。一个函数的基本要求是它应该能够被调用,必须具有一种被引用方式。

立即函数

函数表达式可以放在有些奇怪的位置上。

myFunctionNmae(3);//标准函数调用
(function(){})(3);//立即调用

这种函数叫做立即调用函数表达式(IIFE)或者简写为立即函数。这个特性能够模拟JavaScript中的模块化。

加括号的函数表达式

JavaScript解析器必须能够轻易区分函数声明和函数表达式之间的区别,如果去掉包裹函数表达式的扩展,把立即调用作为一个独立语句function(){}(3),JavaScript开始解析就会结束。

在JavaScript库中经常会见到的几种形式:

+function(){}();
-function(){}();
!function(){}();
~function(){}();

不同于加括号的方式区分函数表达式还是函数声明,这里使用的是一元操作符+,-,!,~。这里应用一元操作符得到的结果没有存储到任何地方并不重要,只有调用IIFE才重要。

箭头函数

箭头函数是JavaScript标准中ES6新增项。

由于JavaScript中会使用大量的函数,增加简化创建函数方式的语法十分有意义。

var values = [0, 3, 2, 5, 7, 4, 8, 1];
values.sort((value1,value2)=>value1-value2);
console.log(values)

最简形式:

param => expression

在这里插入图片描述

函数的实参与形参

  • 形参是我们定义函数时所列举的变量
  • 实参是我们调用函数时所传递给函数的值

在这里插入图片描述
如果实参的数量大于形参,则额外的实参不会赋值给任何形参,如果形参大于实参,则多余的形参则会被设为undefined。

剩余参数

function multMax(first, ...remainingNumbers) {
    var sorted = remainingNumbers.sort((a,b)=>b-a);
    return first * sorted[0];
}
if(multMax(3,1,2,3) === 9)
    console.log("3 * 3 =9")

为函数的最后一个命名参数前加上省略号(…)前缀,这个参数就变成了一个叫做剩余参数的数组,数组内包含着传入的剩余的参数。

默认参数

默认参数已经加入ES6标准

var assert = (condition, text)=>{
    if(condition) {
        console.log(text);
    }
}
function performAction(ninja ,action = "skulking") {
    return ninja + " " + action;
}

assert(performAction("Fuma") === "Fuma skulking","The default value is used for Fuma");
assert(performAction("Yoshi") === "Yoshi skulking","The default value is used for Yoshi");
assert(performAction("Hattori") === "Hattori skulking", "The default value is used for Hattori");
assert(performAction("Yagyu", "sneaking") === "Yagyu sneaking", "Yagyu can do whatever he pleases, even sneak!");
The default value is used for Fuma
The default value is used for Yoshi
The default value is used for Hattori
Yagyu can do whatever he pleases, even sneak!

默认的参数可以是任何值,包括数字、字符串、数组、对象,甚至是函数。每次函数的调用都是从左到右求得参数的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值