JavaScript快速入门(四)——JavaScript函数

函数声明

之前说的三种函数声明中(参见JavaScript快速入门(二)——JavaScript变量),使用Function构造函数的声明方法比较少见,我们暂时不提。function func() { }和var func = function() { }除了在声明提升中有所不同之外也没有其他不同,我们合并起来一起看。我们在这里着重讲一个东西——匿名函数。

匿名函数顾名思义,就是没有名字的函数。它的形式就是function() { }。请注意和之前说的两种方式的区别,这里并没有赋值给任何变量,也就是说,没有指向这个函数的引用,我们无法在其他地方调用这个函数,也就是说,这种函数的使用价值只有一次。当我们把匿名函数赋值给其他变量时,就变成了var func = function() { }。是不是很熟悉?没错,就是我们之前说的变量声明的方法。而如果func是隐式声明的话,那么,这个函数就变成了全局函数。

匿名函数使用非常广泛,它常用于只执行一次的函数,例如排序函数,我们可以这样来写:

 

var a = [2,1,4,7,5];
a.sort(function(num1, num2) {return num1 > num2;
})

我们传了个匿名函数作为参数,因为这个函数只适用于这个地方,而无法用在其他地方。但如果是类似的地方也用了类似的函数,例如我们需要两次排序:

 

 

var a = [2,1,4,7,5],b = [4,2,6,4,1];
function sortDesc(num1, num2) {return num1 > num2;
}
a.sort(sortDesc);
b.sort(sortDesc);

我们就可以把这个匿名函数剥离出来赋给一个function类型的变量,达到重复利用的目的。

 

由上面这个例子我们可以看出匿名函数的优劣。坏处很明显,就是无法再次利用;好处是减少了声明的消耗(当然,如果有两次以上的利用的话,当然是声明的消耗更少)。

 

函数调用

函数的调用和C中差不多,但形式可能有点不同。JavaScript的函数调用形式为:(函数)(参数列表)或者函数名(参数列表)。后者和C是一样的,但前者和C是迥然不同的,因为C中函数声明和函数调用是区分开的。先来看下例子:
function add(num1, num2) {return num1 + num2;
}
var a = add(1, 2); // 3
这种方式就是函数名(参数列表)的形式,我们在C中经常见到,就不详细说了,我们可以把上面那个换种形式来展现:
var a = (function add(num1, num2) {return num1 + num2;
})(1, 2);
console.log(a);  // 3
这样我们就能实现声明和执行一块处理。
但是这样有个问题,我们再看一种情况:
var a = (function add(num1, num2) {return num1 + num2;
})(1, 2);
var b = add(1, 2); // error,add is not defined
console.log(a); // 3
console.log(b); // undefined
这是因为add这个变量的作用域仅限于括号内,这个在之后的作用域讲解中将讲到。
这样的调用,一般是在匿名函数中,为了让函数立即执行才使用这种方式,又或者,利用它的不足,利用JavaScript的作用域特点,将函数内的变量全部转为局部变量,达到封装和防止污染全局的目的。

函数嵌套

JavaScript的函数理论上是可以无限嵌套的,当然并不推荐嵌套太多,原因有很多,无论是性能还是代码简洁要求,都要求不应该嵌套太多。我们举一个嵌套的例子:
function getAbs(num) {function isNegative(num) {return num < 0;}return isNegative(num) ? -num : num;
}
var a = getAbs(-1); // 1
记住一句话,有嵌套就有父子关系(相互嵌套的不在参考范围内,也极度不推荐)。在上面的例子中,父函数即为getAbs,子函数为isNegative。
在JavaScript的嵌套中,涉及到作用域的问题,我们先不讲太复杂的,简单的可以记成:父函数中声明的所有变量,或者说,父函数中能使用的变量,都能在子函数中使用,但反过来,子函数中显式声明的所有变量,都不能在父函数中使用。下面会讲到caller和callee来帮助理解嵌套。

arguments对象

函数中,有一个默认的对象,不需要你去声明,也不需要你去赋值,它叫做arguments,它是一个数组,保存着参数列表。先来看一个例子:
function add(num1, num2) {console.log(arguments); // [1, 2]return num1 + num2;
};
var b = add(1, 2);
注意,arguments对象保存的是实参。接下来,我们要展示JavaScript中非常有意思的一个东西,也是JavaScript灵活性的一大体现。在这之前,我们先来谈下C中的函数重载。
维基中的定义为:函数重载(Function overloading),是Ada、C++、C#、D和Java等编程语言中具有的一项特性,这项特性允许创建数项名称相同但功能的输入输出类型不同的子程序,它可以简单地称为一个单独功能可以执行多项任务的能力。
在函数重载中,输出类型可相同可不同,但参数列表一定要不一样,可以是数量不一样或者类型不一样,或者两者都不一样。
但在JavaScript这类弱类型语言中,类型无法预定义,即输入和输出类型无法从函数定义看出来。那么只剩一项了,参数列表的长度,即参数数量。但这真的有影响吗?
实际上,JavaScript没有函数重载,实参比形参长的后果仅仅是没有给实参一个别名而已。不懂?我们来看下例子:
function add(num1, num2) {console.log(arguments); // [1, 2, 3]return num1 + num2 + arguments[2];
};
var b = add(1, 2, 3); // 6
我们可以巧妙的利用arguments对象,来达到我们的目的。我们甚至可以对上面的做个扩展,让它能把所有参数的和返回,即使形参列表为空。
function add() {var sum = 0;for(var count = 0, length = arguments.length; count < length; count++) {sum += arguments[count];}return sum;
};
var b = add(1, 2, 3, 4); // 10
那如果相反,形参列表长度比实参列表长呢?
function add(num1, num2) {console.log(num1);  // 1console.log(num2);  // undefinedreturn num1 + num2; 
};
var b = add(1); // NaN
我们可以看到,超出实参长度的形参部分,就会是undefined,从而返回我们并不想要的结果(NaN表示应该是个number类型结果却是其他类型)。我们可以稍作修改:
function add(num1, num2) {num2 = num2 || 0;return num1 + num2;
};
var b = add(1); // 1
利用逻辑操作符的特性来将形参实例化,保证使用时形参不为undefined。当然,这样也有个别问题,如果传入的实参逻辑值也是false(例如0、undefined、null)等等,我们就需要用全等符号进行判断了,在此例中不做要求。

caller和callee

这两个对象,是用于判断函数调用和执行的对象函数的。其中,arguments.callee返回当前正在执行的函数,func.caller返回函数的调用体所在函数。而arguments.caller永远返回undefined。如果调用函数是在全局进行,那么func.caller将返回null。注意,在严格模式下这两个对象将被禁用。
我们举刚才的一个代码为例:
function getAbs(num) {function isNegative(num) {console.log(isNegative.caller); // getAbsconsole.log(arguments.callee);  // isNegativereturn num < 0;}return isNegative(num) ? -num : num;
}
var a = getAbs(-1);
你可以将这段代码运行一下,会发现,arguments.callee永远指向函数本身,而函数名.caller将指向调用该函数的代码所在函数,例如本例中即为getAbs。不过如果通过函数名.caller来寻找的话,耦合度太高。我们可以把两个结合起来,
function getAbs(num) {function isNegative(num) {console.log(arguments.callee)console.log(arguments.callee.caller)return num < 0;}return isNegative(num) ? -num : num;
}
var a = getAbs(-1);
有人问这个有什么用?这个严格的来说不是太有用,而且其安全性有问题,否则严格模式也不会禁用掉这两个对象了。但说没用也是不可能的,要不然也不会出现这两个东西了。
首先,这个在调试的时候特别有效,可以帮我们理清代码执行顺序,或者寻找bug;
其次,可以用这两个变量实现一些花哨的技巧,例如我们实现斐波那契数,正常做法是这样:
function fib(num) {if(num == 1 || num == 2) {return 1;}return fib(num - 1) + fib(num - 2);
}
var b = fib(6); // 8
但是这样的坏处在于我们如果要更改个函数名,我们将同时修改三个地方(调用的暂时不论)。我们可以用我们刚学到的东西来解决这个问题:
function fib(num) {if(num == 1 || num == 2) {return 1;}return arguments.callee(num - 1) + arguments.callee(num - 2);
}
var b = fib(6); // 8
但是,投机取巧也是有其弊端的,这会让别人在看你的代码的时候很费劲。用不用,取决于具体情况。

转载于:https://www.cnblogs.com/smght/p/4369549.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/431926.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql 5.6 ibdata1_mysql 里的 ibdata1 文件不断的增长?

我们在 Percona 支持栏目经常收到关于 MySQL 的 ibdata1 文件的这个问题。当监控服务器发送一个关于 MySQL 服务器存储的报警时&#xff0c;恐慌就开始了 —— 就是说磁盘快要满了。一番调查后你意识到大多数地盘空间被 InnoDB 的共享表空间 ibdata1 使用。而你已经启用了 inno…

HashMap解读

hashcode()方法和equals()方法。使用这两个方法&#xff0c;一个对象能够存储或从一个Hashtable&#xff0c;HashMap&#xff0c;HashSet 被检索。 hashcode(): This method is used to get unique integer for given object. This integer is used to find bucket when storin…

mysql查询两个小时前的数据_ORACEL数据库获取两个时间之前的小时数

一、获取两个时间之前的小时数select ceil((To_date(2008-05-01 02:00:00 , yyyy-mm-dd hh24-mi-ss) - To_date(2008-04-30 23:59:59 , yyyy-mm-dd hh24-mi-ss)) * 24 ) 在厂小时数 FROM SCM_GDHJLD2二、截取字符串select substr(DIAODAOJIAOWANSHIJIAN,1,18) from SCM_GDHJLD2…

windows下git bash乱码问题

1,/etc/gitconfig&#xff1a; [gui]encoding utf-8 #代码库统一用urf-8,在git gui中可以正常显示中文 [i18n]commitencoding GB2312 #log编码&#xff0c;window下默认gb2312,声明后发到服务器才不会乱码 [svn]pathnameencoding GB2312 #支持中文路径 2,/etc/git-completio…

redmine两个mysql_Redmine3.4.2安装记(Win10+MySql)

一、准备工具二、安装安装railsinstaller-3.3.0.exe&#xff0c;解压redmine-3.4.2.zip到Sites目录下(默认在系统盘C:\下)创建空数据库和用于访问redmine数据库的用户MySql5.7.18CREATE DATABASE redmine CHARACTER SET utf8;CREATE USER redminelocalhost IDENTIFIED BY redmi…

python删除文件夹中的jpg_Python简单删除目录下文件以及文件夹的方法

本文实例讲述了python简单删除目录下文件以及文件夹的方法。分享给大家供大家参考。具体如下&#xff1a; #!/usr/bin/env pythonimport osimport shutilfilelist[]rootdir"/home/zoer/aaa"filelistos.listdir(rootdir)for f in filelist:filepath os.path.join( ro…

关于生活

最近的实习生活&#xff0c;以及遇到的几个小伙伴让我真正意识到了“生活”。在此记录一下。以后我会每周至少一篇博文&#xff0c;记载这周所学&#xff0c;所思。 转载于:https://www.cnblogs.com/istudy2012/p/4376649.html

python去重且顺序不变_Python实现嵌套列表去重方法示例

发现问题python嵌套列表大家应该都不陌生&#xff0c;但最近遇到了一个问题&#xff0c;这是工作中遇到的一个坑&#xff0c;首先看一下问题raw_list [["百度", "CPY"], ["京东", "CPY"], ["黄轩", "PN"], [&quo…

【原创】Kakfa utils源代码分析(一)

Kafka.utils&#xff0c;顾名思义&#xff0c;就是一个工具套件包&#xff0c;里面的类封装了很多常见的功能实现——说到这里&#xff0c;笔者有一个感触&#xff1a;当初为了阅读Kafka源代码而学习了Scala语言&#xff0c;本以为Kafka的实现会用到很多函数编程(Functional Pr…

redhad yum 安装mysql_redhat7通过yum安装mysql5.7.17教程

rhel/centos系列linux操作系统自身没有mysql的源&#xff0c;需要自行下载安装。本文介绍如何安装mysql5.7.x数据库。第一步&#xff1a;下载源[rootclient ~]# wget http://repo.mysql.com/mysql57-community-release-el7-8.noarch.rpm注意&#xff1a;选择mysql57-community-…

codechef Polo the Penguin and the Tree

一般xor 的题目都是用trie解决。 那这道题是在树上的trie; 首先&#xff1a;从root1,遍历树得到1到所有节点的xor 值。 然后对于每个点我们把其插入二进制树中。 对于每一个点查找其二进值异或值最大的数 依次遍历下来。 注意&#xff1a;边的数量开两倍以上&#xff0c;RE很多…

mysql memcached 使用场景_memcache 应用场景

一..memcache应用场景1.应用场景一&#xff1a; 缓解数据库压力&#xff0c;提高交互速度。它的一个总原则是将经常需要从数据库读取的数据缓存在memcached中。这些数据也分为几类&#xff1a;(1)、经常被读取并且实时性要求不强可以等到自动过期的数据。例如网站首页最新文章列…

link2001错误无法解析外部符号metaObject

http://blog.sina.com.cn/s/blog_791f544a0100r01b.html1>MainWindowBottomWidget.obj : error LNK2001: 无法解析的外部符号 "public: virtual struct QMetaObject const * __thiscall MainWindowBottomWidget::metaObject(void)const " (?metaObjectMainWindow…

mysql主从和dump_MySQL主从同步--原理及实现(一)

1、什么是mysql主从同步&#xff1f;当master(主)库的数据发生变化的时候&#xff0c;变化会实时的同步到slave(从)库。2、主从同步有什么好处&#xff1f;水平扩展数据库的负载能力。容错&#xff0c;高可用。Failover(失败切换)/High Availability数据备份。3、主从同步的原理…

【转】Mybatis/Ibatis,数据库操作的返回值

该问题&#xff0c;我百度了下&#xff0c;根本没发现什么有价值的文章&#xff1b;还是看源代码&#xff08;详见最后附录&#xff09;中的注释&#xff0c;最有效了&#xff01;insert&#xff0c;返回值是&#xff1a;新插入行的主键&#xff08;primary key&#xff09;&am…

解密多媒体封装解封装框架

上一篇文章我们搭好了环境并编译出所需的ffmpeg库&#xff0c;本篇我们讨论如何利用ffmpeg提供的API函数进行多媒体文件的解封装&#xff08;demux&#xff09;过程。在讲解之前&#xff0c;我们需要了解一些基本的多媒体文件知识&#xff0c;大虾请飘过。 容器格式&#xff1a…

python入门及日常应用_python的日常应用-入门篇02

大部分人在编写自己第一个程序的时候会做什么&#xff1f;当然是让你的程序对我们的世界大喊一声“Hello world!”了。今天我们来学习的便是Python中的输出语句。如何让你的程序“说话”&#xff1f;我们想要让程序帮我们做事之前首先要教会程序怎么“说话”&#xff0c;这样我…

bzoj 3611

和BZOJ消耗站一样&#xff0c;先将那个询问的简图构建出来&#xff0c;然后就是简单的树形DP。 &#xff08;倍增数组开小了&#xff0c;然后就狂WA&#xff0c;自己生成的极限数据深度又没有那么高&#xff0c;链又奇迹般正确&#xff09; 1 #include <cstdio>2 #includ…

vscode添加源文件_VSCode自制的IDE编译多个源文件

文/EdwardVSCode的预定义变量我们上一篇文章中讲述了如何将MinGW工具嵌入到VSCode文本编辑器中&#xff0c;在这个配置的过程中&#xff0c;我们只需要通过修改VSCode生成的“luanch.json”和“task.json”两个JSON文件中的特定字段&#xff0c;就可以实现开发环境的搭建。那么…

c# 第四课 interfaces

An interface is a contract(协定) that guarantees to a client how a class or struct will behave.When a class implements an interface(实现一个接口), it tells any potential(可能的) client “I guarantee I’ll support all the methods, properties, events, and in…