JavaScript 编程精解 (3)

第三章 函数

  • 函数是JavaScript中不可或缺的组成部分
  • 函数是构造大型程序的工具,可以用于减少重复性工作、为子程序命名并隔离各个子程序的运行。

3.1 定义函数

创建函数的表达式以关键字function开头。

var fun = function (parameter1, parameter2, ...) {//参数可以为空,也可以为一或多个
    // 函数体,哪怕只有一条语句,也要包含在大括号中,语句会在调用函数时执行
    ...
    // return语句决定了函数的返回值,后面不跟表达式时返回undefined
    return;
}

3.2 参数和作用域

  • 函数的参数初始值由函数调用者提供。

  • 函数内部创建的变量和参数都属于函数的局部变量,这种隔离机制确保了函数间不会相互干扰。

3.3 嵌套作用域

  • 可以在函数中创建其他函数,并产生不同程度的局部作用域。

  • 任何局部作用域都可以访问到包含它的局部作用域

  • 函数内部变量的可见性取决于函数在代码当中的位置,在包含了一个函数定义的代码块中,这个函数可以访问到代码块中的所有变量,即函数上层的代码块中的变量和函数内部的变量。这种控制变量的方法称为词法作用域

  • let关键字作用与var相同,只不过变量作用域是块作用域而非函数局部作用域。

3.4 函数值

函数和函数名的区别函数是一种叫做function引用类型的实例,因此函数是一个对象。对象是保存在内存中的,函数名则是指向这个对象的指针。

JavaScript中函数是一等公民,可以作为参数传入别的函数,也可以作为一个函数的返回值,也可以被重新赋值。

3.5 符号声明

函数声明还有一种更为简洁的方式:

function fun(parameter) {
    //todo
}
  • 函数声明不遵循一般的从上到下的流控制规则。
console.log('are you ok ?', ans());

function ans() {
    return 'yeah, i\'m ok';
}
  • 为了确保函数在不同环境下运行的行为一致,应在最外层的函数或程序作用域中进行函数声明。

3.6 调用栈

由于函数需要在执行结束后跳转回调用该函数的代码位置,因此计算机必须记住函数调用的上下文。我们将计算机存储这个上下文的区域称之为调用栈

  • 当函数调用时,当前的上下文信息就会被存储在栈顶

  • 当函数返回时,系统会删除存储在栈顶的上下文信息,并使用该信息继续执行程序。

// 若计算机空间无限大,循环调用会一直执行下去,但事实上是该程序会耗尽内存空间,导致“栈空间溢出”。
function chicken() {
    return egg();
}
function egg() {
    return chicken();
}
console.log(chicken() + ' came first.');

3.7 可选参数

JavaScript对传送函数的参数数量几乎不做任何限制。如果你传递了过多参数,多余的参数就会被忽略,而如果你传递的参数过少,遗漏的参数将会被赋值成undefined。

缺点:你可能恰好向函数传递了错误数量的参数,但没有人会告诉你这个错误。

优点: 我们可以利用这种行为来让函数接收可选参数。

3.8 闭包

如果函数已经执行结束,那么这些由函数创建的局部变量会如何处理呢?

function wrapValue(n) {
    var localVariable = n;
    return function { return localVariable; };
}
var wrap1 = wrapValue(1);
var wrap2 = wrapValue(2);
console.log(wrap1());
// 1
console.log(wrap2());
// 2

这段代码很好地印证了局部变量会在每次函数调用时重新创建,不同的函数调用是不会对其他函数内的局部变量产生影响的。

这种引用特定的局部变量实例的功能称为闭包。一个包装了一些局部变量的函数是一个闭包。利用闭包的特性,我们不再需要担心变量的生命周期问题,很多高级应用都依靠它来实现。

function multiplier(factor) {
    return function(number) {
        return number * factor;
    }
}

var twice = multiplier(2);
console.log(twice(5));
//10

可以把关键字function当做一种“冻结”代码并将其打包成函数值的模型。所以当看到“return function(...) {...}”这样的代码时,可以将其理解为一个句柄,其中句柄指向一段包装好的计算代码。

3.9 递归

函数完全可以自己调用自己,只要避免栈溢出的问题即可。我们把函数调用自身的行为称为递归

function power(base, exponent) {
    if (exponent == 0)
      return 1;
    else
      return base * power(base, exponent-1);
}

console.log(power(2, 3));
// 8

需要注意的是,在标准的JavaScript实现当中,递归写法的函数执行效率比循环写法的函数慢了大约10倍。如何权衡性能与优雅是一个值得考虑的问题,但有一条基本原则:除非程序执行速度确实太慢,否则先不要关注效率问题。

对于某些问题来说,递归相较于循环更能解决问题。这类问题通常需要执行和处理多个分支,而每个分支又会引出更多的执行分支。

3.10 添加新函数

两种常用的引入函数的方法:

  1. 找出程序中多次出现的相似代码。

  2. 写新功能代码,觉得一些代码应该包含在一个函数时。甚至可以先编写调用函数的代码,然后再具体实现调用的函数。

给函数起名的难易程度取决于我们封装的函数的用途是否明确

3.11 函数及其副作用

可以将函数分为两类:一类调用后产生副作用,而另一类则产生返回值(当然也可以定义同时产生副作用和返回值的函数)。

相比于直接产生副作用的函数,产生返回值的函数更容易集成到新的环境当中使用。但在副作用的帮助下,有些操作则更易、更快实现,因此考虑到运算速度,有时候纯函数并不可取。

3.12 本章小结

  1. 对于关键字function来说,当我们将其作为表达式来使用的时候,可以创建一个函数值。当我们将其作为语句来使用的时候,可以用来声明并将函数赋予变量。
// Create a function value f
var f = function(a) {
    console.log(a + 2);
}

// Declare g to be a function
function g(a, b) {
    return a * b * 3.5;
}
  1. 要理解函数的含义,就必须理解局部作用域的概念。对于一个函数来说,其参数及其内部声明的变量都是局部变量,每当调用函数时,这些变量都会被重新创建,而且对外并不可见。而在函数作用域当中声明的函数,可以访问其外部函数的局部作用域。

  2. 将程序中的任务划分到不同的函数中的做法是非常有用的,而且有助于提高代码的可读性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,139评论 9 118
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,567评论 18 399
  • 与宝宝饭后去买了一本新的note book。不知不觉中他终于完成了第一本日记本。说是日记,也不完全是,不一定是每天...
    无担阅读 236评论 0 0
  • 我发现每每迷茫的时候,总是玩手机到深夜,还常常找成人网站,看完就睡不着了,你有过吗? 为了音乐梦,我找了间一个月2...
    杨啸啸阅读 192评论 0 0
  • 交绩五系统当中的中心圆始终是目标,而在《刻意练习》这本书中也提到了目标对于刻意训练的重要性,‘目标’对于学习力的提...
    多瀚Sean阅读 111评论 0 0