(第三天)函数

定义函数

关键字function用来定义函数。定义函数有两种方法

(1)函数定义表达式

1 var f = function(x) { return x+1; }

(2)函数声明语句

1 function funcname([arg1 [, arg2 [...,argn]]]) {
2 
3 }

 函数声明语句通常出现在JavaScript代码的最顶层,也可以嵌套在其他函数体内。但在嵌套时,函数声明只能出现在所嵌套函数的顶部。也就是说函数定义不能出现在if语句、while语句后者其他语句中。

二者异同

(1)都创建了相同的新函数对象

(2)函数声明语句中的函数名是一个变量名,变量指向函数对象。函数定义表达式并未声明一个变量,如果一个函数定义表达式包含名称,函数的局部作用域将会包含一个绑定到函数对象的名称。所以名称存在函数体中,并指代该函数本身,也就是说函数的名称将成为函数内部的一个局部变量。【注】函数定义表达式特别适合用来定义那些只会用到一次的函数

(3)函数声明语句中的函数被显式地“提前”到了脚本或函数顶部。因此它们在整个脚本和函数内都是可见的。但以函数表达式定义函数则不同,想要调用此定义的函数,必须要引用它,而要使用一个表达式方式定义的函数之前,必须要把他赋值给一个变量,前面说过:变量的声明提前了,但给变量赋值是不会提前的,所以,以表达式方式定义的函数在定义之前无法调用。如下代码:

1 person();   /*函数声明语句显式提前至顶部,所以能在定义之前调用*/
2 function person() {}
3 person();              /*在函数定义之后调用毋庸置疑正确*/
1 p();
2 
3 var p = function(){
4 
5 }
6 
7 /*
8 打印出:undefined is not a function 变量p还未初始化,因此函数定义表达式无法在函数定义之前调用
9 */

函数调用 

(1)作为函数

1 function person(age,name){
2 
3 }
4 
5 person(12,"嘿嘿");   /*函数调用*/

(2)作为方法

 1 var calculator = {
 2       operator1 : 1,
 3       operator2 : 2,
 4       add: function(){
 5              this.result = this.operator1 + this.operator2;
 6       }
 7 
 8 };
 9 
10 calculator.add();      //方法调用计算1+1的结果 
11 calculator.result      =>2
12  
13 /*
14 通过对象直接量来创建属性和方法,此时calculator作为调用上下文,所以this关键字则引用该对象
15 */

(3)作为构造函数

1 var o = new Object();
2 
3 var o = new Obejct;
4 
5 /*
6 构造函数调用创建一个新的空对象,这个对象继承自构造函数的prototype属性。构造函数试图初始化这个新创建的对象,并将这个对象用做其调用上下文,因此构造函数可以使用this关键字来引用这个新创建的对象
7 */

【注】如果构造函数没有形参,JavaScript构造函数调用的语法是允许省略实参列表和圆括号的。所以凡是没有形参的构造函数调用都可以省略圆括号。

(4)通过它们的call()和apply方法间接调用 【后面讲】

this关键字

this是 一个关键字,不是变量,也不是属性名。JavaScript的语法不允许给this赋值。和变量不同,关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。对此我们用实例说明

 1 var o ={
 2       m: function() {
 3              var self = this;
 4              console.log(this === o);  (1 5              f();
 6             
 7              function() {
 8                      console.log(this === o);  (2 9                      console.log(self == o);     (310              }
11       }
12 }
13 
14 /*
15 上述(1)中打印出true,因为this就指代o。(2)中输出false,此时this是全局对象或undined。(3)输出true,self指代外部函数的this值
16 */

综上所知:

(1)如果嵌套函数作为方法调用,其this的值指向调用它的对象。

(2)如果嵌套函数做为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下)。

(3)如果想访问外部函数的this的值,需要将this的值保存在一个变量里,这个变量和内部函数都同在一个作用域内。通常使用变量self来保存this。

作为命名空间的函数

引入

当有一段JavaScript模块代码,这段代码将要用在不同的JavaScript程序中(对于客户端JavaScript来讲通常是用在各种各样的网页中)。假如这段代码定义了一个用以存储中间计算结果的变量。如此就出现一个问题,当模块代码放在不同的程序中时,你无法得知这个变量是否已经建好,如果已经存在这个变量,那么将会和代码发生冲突。解决办法就是将代码放入一个函数内,然后调用这个函数。这样全局变量就变成了函数内的局部变量。用匿名函数来定义,用法如下

1 (function(){
//模块代码中所使用的变量都是局部变量
2 console.log(12) (13 }()); //结束函数定义并立即调用它 4 5 或者 6 7 (function(){ 8 console.log(12); (29 })();

这里有个疑问尚未解决,如果有园友知道,希望能帮助我解决,谢谢!不知道大家注意到上述两个匿名函数的不同之处,对,在(1)中输出的最后没有加分号,若加分号则错误,无法输出,不加分号则正常输出,(2)中则是好使的!希望得到各位园友的帮助,在此表示感谢!

这种定义匿名函数并立即在单个表达式中调用它的写法很常见,如代码检测中是否出现了一个bug,如果出现这个bug,就返回一个带补丁的函数的版本! 

函数属性、方法

(1)length属性

 length到没什么可说的,最主要是要属函数的arguments的属性了,在函数体内,arguments.length表示传入函数的实参的 个数,以此来模拟函数重载。请看下面代码

 1                          function person(age, name, gender, addr) {
 2                              this.age = age;
 3                              this.name = name;
 4                              this.gender = gender;
 5                              this.addr = addr;
 6                                 /*获得传入实参的个数*/ 
 7                              switch(arguments.length)
 8                              {
 9                                   case  1:
//
10 break; 11 case 2:
//
12 break; 13 case 3:
//
14 break; 15 case 4:
//
16 break; 17 } 18 } 19 20 var p = new person(1,'嘿嘿'); 21 var p1 = new person(1,'嘿嘿',false); 22 var p2 = new person(1,'嘿嘿',false,'hunan'); 23 24

补充:arguments的callee属性

引入

1             var factorial = function(x) {
2                 if (x <= 1) return 1;
3                 return x * factorial(x - 1);
4             }
5             console.log(factorial(5));
6                 /*打印出120*/

上述代码为求一个数的阶乘,毫无疑问没有错误。现在进行一点小改动,如下

1             var factorial = function(x) {
2                 if (x <= 1) return 1;
3                 return x * factorial(x - 1);
4             }
5             var fact2 = factorial;
6             factorial = function() { 7 return 0; 8  } 9 console.log(fact2(5));

上述很明显fact2变量指向两个函数,当要求调用fact2(5)时,先执行 第一个 factorial函数,当执行到 return x * factorial(x - 1); 时,这时执行的就是 第二个 factorial函数,所以此时打印出0。很显然这不是我们想要的结果,我们需要的是求阶乘即执行的函数接下来还是它本身也就是第一个,所以这个时候callee()方法就派上了用场:用于调用自身。所以上述代码这样修改即可 

1             var factorial = function(x) {
2                 if (x <= 1) return 1;
3                 return x * arguments.callee(x - 1);
4             }
5             var fact2 = factorial;
6             factorial = function() { 7 return 0; 8  } 9 console.log(fact2(5));

 

【注】arguments还有一个长得相似callee的属性就是caller,而在非严格模式下,ECMAScript标准规范规定callee属性指代当前正在执行的函数。caller是非标准的但大多数浏览器都 实现了这个属性,它指代当前正在执行的函数的函数。通过caller属性可以访问调用栈。而通过callee来进行递归调用自身,因为它保存了当前执行方法的地址,而不会出差错。

(2) prototype属性

每个函数都包含一个prototype属性,这个属性指向一个对象的引用,这个对象叫做原型对象。每一个函数都包含不同的原型对象。当将函数用做构造函数的时候,新创建的对象会从原型对象上继承属性。有关原型对象前面已讲,请参考原型、继承这一讲

(3)call()和apply()方法

这两种方法可以用来间接地调用函数,两个方法都允许显式指定调用所需的this的值,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。两个方法都可以指定调用的实参。call()方法使用它自有的实参列表作为函数的实参,apply()方法则要求以数组的形式传入参数。在ECMAScript5的严格模式中,call()和apply()的第一个实参都会变为this的值,哪怕传入的实参是原始值甚至是null或undifined。在ECMAScript3和非严格模式中,传入的null和undefined都会被全局对象代替,而其他原始值则会被相应的包装对象所替代。

 下面用代码来解释上述概念

(1)call()方法

 1                 function person(age,name){
 2                     this.age=age;
 3                     this.name=name;
 4                 }
 5    
 6                 var obj=new Object();
 7                 person.call(obj,12,'小黑');
 8                 console.log(obj.age);
 9                 console.log(obj.name);
10 
11                /*
12                创建空对象obj,此时调用person类的call()方法,并将obj传递进去,此时obj成为其调用上下文,此时this即obj,最终能打印出12和小黑
13                */

(2)apply()方法 

 更多用法请参考:apply()详情

(3)bind()方法

bind()是在ECMAScript5中新增的方法,但在ECMASript3中可以轻易模拟bind(),从名字可以看出,这个方法的主要作用就是将函数绑定至某个对象上。

 1 function f(y){
 2        return this.x + y;
 3 }
 4 var o = { x : 1 };
 5 var g =  f.bind(o);
 6 console.log(g(2));
 7 
 8 function bind(f,o){
 9        if(f.bind) return f.bind(o);
10        else return function(){
11               return f.apply(o, arguments);
12        };
13 }

上述将函数f绑定至对象o上,当在函数f()上调用bind()方法并传入一个对象o作为参数,这个方法将返回一个新的函数。调用新的函数将会把原始的函数f()当做o的方法来调用。传入新函数的任何实参都将传入原始函数。上述中 f.bind(o); 则此时函数f()中的this即为o,此时this.x=1,绑定后返回新的函数g,再调用函数g(2),此时函数f()即为其参数,所以打印出3。

函数传参

(1)传递原始类型(所谓c#中的值类型)

1                          function person(age){
2                              age++;
3                          }
4                          var age = 12;
5                          person(age);
6                          console.log(age);

根据上述代码你觉得会打印出多少呢?我们一句一句分析:

 (1)var age = 12;栈上开辟一段空间地址假设为0x1,此时0x1存的是变量age并且其值为12。

 (2)person(age);调用函数person并将实参传进去,此时person中的形参相当于是局部变量,所以同样在栈上开辟一段空间地址为0x2,变量为age,将上述(1)中的值赋给(2)中的age所以同样为12,然后进入函数体内,将age++,此时相加的是新创建的局部变量的值,并未改变(1)中的age值。

(3)综上打印出12。

(2)传递对象(所谓c#中的引用类型)

1                          function person(p){
2                                 p.age++;
3                          }
4                          var p = { age : 12 };
5                          person(p);
6                          console.log(p.age);

同理进行分析

(1) var p = { age : 12 };首先在栈上开辟一段空间地址为0x1值为null,然后右边在堆上首先开辟一段空间地址为0x2的空对象,然后定义属性age并且其值为12,并且此对象指向变量p,所以此时变量的值为0x2。

(2)person(p);同样p此时相当于是person类的局部变量,首先在栈上开辟一段空间地址为ox3名字为p的变量,此时将(1)中p的存的值0x2赋给0x3中p的值,所以此时0x3中的值为0x2,也就是此时ox2指向了堆上那个地址为0x2,属性为age值为12的对象。然后进入函数体内部,对其年龄进行age++,此时堆上对象的属性age变为13。

(3)综上,此时打印出的P中的age为13。

 总结传参

无论是传递原始类型(值类型)还是对象(引用类型)只需要看栈上变量的值是存的值还是地址,若存的是值,则相当于复制一份即副本不会改变原来的值,若是变量的值存的是地址,若在函数体内改变值则原来对象中的值也会受影响。

 

转载于:https://www.cnblogs.com/CreateMyself/p/4692357.html

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

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

相关文章

python 使用 asyncio 包处理并发

文章目录1. 线程与协程对比2. 使用 asyncio 和 aiohttp 下载3. 避免阻塞型调用4. 使用 asyncio.as_completed5. 使用Executor对象&#xff0c;防止阻塞事件循环6. 从回调到期物和协程learn from 《流畅的python》 1. 线程与协程对比 threading import threading import iter…

ai预测占比_2019-2021年中国AI芯片市场预测与展望数据

来源&#xff1a;雪球App&#xff0c;作者&#xff1a; IC咖啡&#xff0c;(https://xueqiu.com/1118999957/139121699)预计未来三年AI芯片市场规模仍将保持50%以上的增长速度&#xff0c;到2019年中国AI芯片市场规模将达到124.1亿元。从细分市场结构来看&#xff0c;云端训练芯…

计算机专业英语第2版郭涛翻译,计算机专业英语

计算机专业英语作  者&#xff1a;郭涛 主编出版时间&#xff1a;2007年01月定  价&#xff1a;21.60I S B N &#xff1a;9787560939209所属分类&#xff1a; 大中专教材 > 研究生/本科/专科教材 &nbsp大中专教材 &nbsp语言学习 > 大学英语 &nbsp语…

ODS与数据仓库

数据仓库是目前主要的数据存储体系。数据仓库之增W.H.Inmon认为&#xff0c;数据仓库是指支持管理决策过程的、面向主题的、集成的、随时间而变的、持久的数据的集合。简单地说&#xff0c;一个数据仓库就一个自数据库的商业应用系统&#xff0c;该数据库的数据来自于其它的运作…

LeetCode 1991. 找到数组的中间位置(前缀和)

文章目录1. 题目2. 解题1. 题目 给你一个下标从 0 开始的整数数组 nums &#xff0c;请你找到 最左边 的中间位置 middleIndex &#xff08;也就是所有可能中间位置下标最小的一个&#xff09;。 中间位置 middleIndex 是满足 nums[0] nums[1] ... nums[middleIndex-1] n…

mysq命令行导出sql_mysql 命令行导入导出 sql

命令行source 导入数据库&#xff1a;代码如下复制代码1&#xff0c;将要导入的.sql文件移至bin文件下&#xff0c;这样的路径比较方便2&#xff0c;同上面导出的第1步3&#xff0c;进入MySQL&#xff1a;mysql -u 用户名 -p如我输入的命令行:mysql -u root -p (输入同样后会…

计算机应用基础第六章,《计算机应用基础》第六章习题

周南岳版第六章习题及答案《计算机应用基础》教材第6章习题一、填空题(1)计算机网络中共享资源指的是硬件、软件和___数据___资源。(2)计算机网络中&#xff0c;通信双方必须共同遵守的规则或约定&#xff0c;称为__协议__。(3)在计算机局域网中&#xff0c;将计算机连接到网络…

discuz MVC结构分析

Discuz软件经解压后产生的三个文件夹中的一个叫upload的成为网站的根目录。里面的内容可以在某些网站上在线阅读&#xff0c;如用好库编程网。也可以离线在本地阅读&#xff0c;如用VS.Php for Visual Studio。这里面的内容安排的井然有序。不同用途的文件都放在了不同的文件夹…

postforobject 设置代理_Spring RestTemplate和代理身份验证

小编典典经过许多不同的选择之后&#xff0c;由于能够在创建时为RestTemplate设置代理&#xff0c;因此我可以选择以下代码&#xff0c;因此我可以将其重构为单独的方法。只是要注意&#xff0c;它还具有其他依赖性&#xff0c;因此请记住这一点。private RestTemplate createR…

LeetCode 1992. 找到所有的农场组(BFS)

文章目录1. 题目2. 解题1. 题目 给你一个下标从 0 开始&#xff0c;大小为 m x n 的二进制矩阵 land &#xff0c;其中 0 表示一单位的森林土地&#xff0c;1 表示一单位的农场土地。 为了让农场保持有序&#xff0c;农场土地之间以矩形的 农场组 的形式存在。 每一个农场组都…

flash html5 chrome,为了支持 HTML5 ,谷歌 Chrome 浏览器将“封杀”Flash

关于谷歌 Chrome 将不再支持 Flash 的话题&#xff0c;已经争论了很长一段时间了。不过现在&#xff0c;这家互联网巨头终于确认&#xff0c;为了支持 HTML5&#xff0c;Chrome 浏览器将不再运行 Flash。据谷歌表示&#xff0c;由于 Flash 会减慢网页浏览速度并可能引发信息安全…

VS2015开发Android,自带模拟器无法调试、加载程序,算是坑吗

VS2015出来后&#xff0c;确定变化很大&#xff0c;什么android、ios的&#xff0c;不在话下。对于我这样传统型的人&#xff0c;也第一时间试用了一下&#xff08;vs2003->vs2008->vs2012->vs2015&#xff09;。以前用eclipse开发过android小程序&#xff0c;现在想试…

amd cpu 安卓模拟器_夜神模拟器常见问题解答_v20201025

MAC版本常见问题Mac版本模拟器常见问题MAC模拟器出现“您应该将它移到废纸篓”解决办法MAC版本模拟器功能介绍MAC版本模拟器界面Mac版模拟器可能无法启动(卡99%)的原因及解决方式其他问题如何在夜神模拟器中安装自己想要的游戏/应用Sample CA 2证书没网解决办法GlobalSignature…

LeetCode 502. IPO(优先队列)

文章目录1. 题目2. 解题1. 题目 假设 力扣&#xff08;LeetCode&#xff09;即将开始 IPO 。 为了以更高的价格将股票卖给风险投资公司&#xff0c;力扣 希望在 IPO 之前开展一些项目以增加其资本。 由于资源有限&#xff0c;它只能在 IPO 之前完成最多 k 个不同的项目。 帮助…

计算机水平考试ppt试卷,计算机等级考试一级笔试卷库.ppt

计算机等级考试一级笔试卷库计算机等级考试一级笔试题库 全国计算机等级考试一级题库 1&#xff0e;微机中1K字节表示的二进制位数是( )。? A、1000 B、8x 1000 C、1024 D、8x1024?? 2&#xff0e;计算机硬件能直接识别和执行的只有( )。 A、高级语言 B、符号语言 C、汇编语…

mysql中ibatis的limit动态传参

param.put("pageNo",pageNo); param.put("pageSize",pageSize); sqlMap中的用法 limit ${pageNo},${pageSize} 我今天在敲代码的时候这么用结果就报错了:limit #pageNo#,#pageSize# 后来查资料才知道 limit 后面不应该用#&#xff08;我传进来的是Intege…

牛客网软通动力软件测试机试_软通动力软件测试笔试题

.;.1&#xff0e;软件测试是软件开发的重要环节&#xff0c;进行软件测试的目的是()A)证明软件错误不存在B)证明软件错误的存在C)改正程序所有的错误D)发现程序所有的错误2&#xff0e;对于软件质量描述不正确的是&#xff1a;()A)高质量的过程产生高质量的产品B)软件质量是测试…

公安计算机技能测试题库,2018公安文职考试题库:行政职业能力测验

【导语】中公重庆招警考试网为大家带来公安文职考试练习题&#xff1a;行政职业能力测验&#xff0c;帮助各位考生顺利备考公安文职考试笔试。1.美国中央情报局和国家保安局根据计算机专家提供的数据&#xff0c;已充分认识到军用电子系统的一个致命弱点——容易遭到计算机病毒…

OpenGL于MFC使用汇总(三)——离屏渲染

有时直接创建OpenGL形式不适合&#xff0c;或者干脆不同意然后创建一个表单&#xff0c;正如我现在这个项目&#xff0c;创建窗体不显示&#xff0c;它仅限于主框架。而我只是ActiveX里做一些相关工作&#xff0c;那仅仅能用到OpenGL的离屏渲染技术了~即不直接绘制到窗体上&…

python 动态属性和特性

文章目录1. 使用动态属性转换数据2. property2.1 help() 文档3. 特性工厂函数4. 属性删除操作5. 处理属性的重要属性和函数5.1 处理属性的内置函数5.2 处理属性的特殊方法learn from 《流畅的python》 1. 使用动态属性转换数据 在 Python 中&#xff0c;数据的属性和处理数据…