详解JavaScript函数


Posted in Javascript onDecember 01, 2015

      函数是一组可以随时随地运行的语句,函数作为ECMAScript的核心是很重要的。函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。也就是函数是定义一次但却可以调用或执行任意多次的一段JavaScript代码。函数有时会有参数,即函数被调用时指定了值的局部变量。函数常常使用这些参数来计算一个返回值,这个值也成为函数调用表达式的值。
一、函数声明
       函数对于任何语言来说都是一个核心的概念。通过函数可以封装任意多条语句,而且可以在任何地方,任何时候调用执行。JS中的函数使用function关键字来声明,后跟一组参数以及函数体。
       函数的基本语法是这样的:

<span style="font-size:18px;">function functionName(arg0, arg1, ... argN) { 
 statements 
}</span>

       ECMAScript规定的函数声明方式有三种:
(1)普通函数声明

<span style="font-size:18px;">function box(num1,num2){ 
 return num1+num2; 
}</span>

 (2)使用变量初始化什声明函数

<span style="font-size:18px;">var box=function(num1,num2){ 
 return num1+num2; 
}</span>

(3)使用Function构造函数声明

<span style="font-size:18px;">vax box=new Function('num1','num2','num1+num2');</span> 
 
二、函数的类型及函数的调用
 ECMAScript语法规定了
 (1)无参数的函数:函数的声明的时候没有参数,调用函数的时候直接使用即可。
function box(){ 
 document.write("我是中国人!"); 
} 
box();//函数调用

运行的结果为:我是中国人!
(2)带参数的函数:函数的声明的时候同时定义了参数变量,参数可以是多个。

function box(name,age) { 
 document.write("你的姓名是:"+name+"你的年龄是:"+age); 
} 
box("张三","24");//函数调用

运行的结果为:你的姓名是:张三
                       你的年龄是:24
(3)带有返回值的函数
       带参数和无参数的函数,都没有定义返回值,而是调用后直接执行的,实际上,任何函数都可以通过return语句跟后面的要返回的值来实现返回值
1)、无参数的函数

function box(){ 
 return "我是中国人!"; 
} 
document.write(box());

同上面的输出结果:我是中国人!
2)、带参数的函数

function box(name,age){ 
 return "你的姓名是:"+name+"<br/>"+"你的年龄是:"+age; 
} 
document.write(box("张三","24"));//函数调用 
document.write("<hr/>"); 
var demo=box("李四","23");//也可以重新赋值新的函数 
document.write(demo);

运行的结果为:

详解JavaScript函数

 (4)作为值的函数(比较特殊)
首先我们来看一个函数作为常规的变量的例子:

function box(sum,num){ 
 return sum+num;//这里传递的是函数的返回值和普通的变量一样 
} 
function sum(num){ 
 return num+10; 
} 
var result=box(sum(10),10); 
document.write("result="+result);

页面的输出结果为:result=30
下面则传递的是函数,仔细和上面的区分:

function box(sum,num){ 
 return sum(num);//这里传递的是函数 
} 
function sum(num){ 
 return num+10; 
} 
var result=box(sum,10); 
document.write("result="+result);

页面的输出结果为:result=20
三、函数的内部属性
       在函数内部,有两个特殊的对象:arguments对象和this对象。arguments对象是类数组对象,包含着传入函数中的所有参数,主要用途是保存函数参数,主要的属性有length,这个属性是动态的判断函数有多少个参数。但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
 (1)arguments对象的length属性
       JS函数不介意传递进来多少参数,也不会因为参数不统一而错误。实际上,函数体内可以通过arguments对象来
接收传递进来的参数。
       我们先来看一个我们在函数传递参数遇到的问题:函数声明时并不知道要定义多少个参数,在调用函数却出现多
出的或不足的问题。

function box(){ 
 return arguments[0]+"|"+arguments[1]; 
} 
document.write(box(1,2,3,4,5,6));

输出的结果为:1|2。因此输出的显然与我们想要做的不符,那么怎么解决呢?
有了arguments对象的length属性我们就能可以得到参数的数量,避免上面的错误出现。

function box(){ 
 return arguments.length; 
} 
document.write(box(1,2,3,4,5,6));

输出:6
       我们还可以利用length属性来智能的判断有多少参数,然后把参数进行合理的应用,比如,实现一个加法运算,将所有传进来的数字累加,而数字的个数又不确定。

function box(){ 
 var sum=0; 
 if(arguments.length==0) 
 { 
 return sum; 
 } 
 for(var i=0;i<arguments.length;i++) 
 { 
 sum=sum+arguments[i]; 
 } 
 return sum;//返回累加结果 
} 
document.write(box(1,2,3,4,5,6));

       输出:21
(2)arguments对象的callee属性
       还是来说问题:对于递归的问题我们很熟悉了,JS中也不例外

function box(num){ 
 if(num<=1) 
 { 
 return 1; 
 } 
 else 
 { 
 return num*box(num-1);//递归 
 } 
} 
document.write(box(4));

       输出:24
       对于阶乘函数一般要用到递归算法,所以函数内部一定对调用自身,如果函数名不改变是没有问题的,但一旦改变函数名,内部的自身调用需要逐一修改。为了解决这个问题,可以使用arguments.callee来代替。

function box(num){ 
 if(num<=1) 
 { 
 return 1; 
 } 
 else 
 { 
 return num*arguments.callee(num-1)//递归 
 } 
} 
document.write(box(4));

       输出:24
(3)this对象
       函数内部另一个特殊的对象时this,其行为与Java和C#中的this大致相似,换句话说,this引用的是函数据以行操作的对象,或者说函数调用语句所处的那个作用域。当在全局作用域中调用函数时,this对象引用的就是window(window是一个对象,是JavaScript中最大的对象,是最外围的对象)。

var color="红色";//这里的color是全局变量,并且这个变量是window的属性 
document.write(window.color+"<br/>"); 
document.write(this.color+"<br/>"); 
var box={ 
 color:"蓝色",//这里的color是box下的属性,是局部变量 
 sayColor:function(){ 
 return this.color;//此时的this只能是box中的color 
 } 
}; 
document.write(box.sayColor()+"<br/>");//局部的 
document.write(this.color);//全局的

       运行的结果为:

详解JavaScript函数

四、函数属性和方法
(1)JavaScript中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length和prototype。其中,length属性表示函数希望接受的命名参数的个数。

function box(num1,num2){ 
 return num1+num2; 
} 
document.write(box.length);

输出的结果;2
       对于prototype属性,它是保存所有实例方法的真正所在,也就是原型。这个属性我们先不做过多的介绍。prototype属性下有两个方法:apply()和call(),每个函数都包含这两个非继承而来的方法。这两个方法的用途都在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。

function box(num1,num2){ 
 return num1+num2; 
} 
function sayBox(num1,num2){ 
 return box.apply(this,[num1,num2]);//this表示作用域,这里是window,[]表示box所需的参数 
} 
function sayBox2(num1,num2){ 
 return box.apply(this,arguments);//arguments对象表示box所需的参数 
} 
document.write(sayBox(10,10)+"<br/>"); 
document.write(sayBox2(10,10));

       输出的结果为:20
                                20
(2)call()方法和apply()方法延伸
       call()方法和apply()方法相同,它们的区别仅仅在于接收参数的方式不同。对于call()方法而言,第一个参数作用域,没有变化,变化的只是其余参数都是直接传递给函数的。

function box(num1,num2){ 
 return num1+num2; 
} 
function callBox(num1,num2){ 
 return box.call(this,num1,num2);//区别apply()方法 
} 
document.write(callBox(10,10));

       输出的结果为:20
       call()方法和apply()方法真正的作用是扩展函数赖以运行的作用域

var color="红色";//全局变量 
var box={ 
 color:"蓝色",//局部变量 
}; 
function sayColor(){ 
 return this.color; 
} 
document.write(sayColor()+"<br/>");//作用域在Window 
document.write(sayColor.call(this)+"<br/>");//作用域在Window下 
document.write(sayColor.call(window)+"<br/>");//作用域在Window下 
document.write(sayColor.call(box));//作用域在box下,对象冒充

        输出的结果为:

详解JavaScript函数

       使用call()方法或者apply()方法来扩充作用域的最大好处就是对象不需要与方法发生任何耦合关系。也就是说,box对象和sayColor()方法之间不会有多余的关联操作,比如;box.sayColor=sayColor;
五、ECMAScript闭包
       ECMAScrip最易让人误解的一点是,它支持闭包。闭包,指的是词法表示包括不被计算的变量的函数,就是说,函数可以使用函数之外定义的变量。
       其实我在前面的博文已经使用到了闭包,比如在轻松学习JavaScript七:JavaScript的流程控制语句中使用的变量time就是全局变量,函数myFunction()使用这个全局变量,并不是函数本身定义的。还是看一下那个实例吧:

var time=new Date().getHours(); 
document.write("当前北京时间:"+time); 
function myFunction() 
{ 
 var x=""; 
 if (time<20) 
 { 
 x="Good day"; 
 } 
 document.getElementById("demo").innerHTML=x; 
}

(1)简单的闭包实例
       在ECMAScript中使用全局变量是一个简单的闭包实例。请思考下面这段代码输出的结果是什么:

var sMessage = "hello world"; 
function sayHelloWorld() { 
 document.write(sMessage); 
} 
sayHelloWorld();

       在上面这段代码中,脚本被载入内存后,并没有为函数sayHelloWorld()计算变量sMessage的值。该数捕 sMessage的值只是为了以后的使用,也就是说,解释程序知道在调用该函数时要检查sMessage的值。sMessage将在函数调用sayHelloWorld()是在(最后一行)被赋值,显示消息"hello world"。
(2)复杂的闭包实例
       在一个函数中定义另一个会使闭包变得更加复杂。例如:

var iBaseNum = 10;//全局变量 
function addNum(iNum1, iNum2) { 
 function doAdd() { 
 return iNum1 + iNum2 + iBaseNum; 
 } 
 return doAdd(); 
} 
document.write(addNum(10,10));

       这里,函数addNum()包括函数doAdd()(闭包)。内部函数是一个闭包,因为它将获取外部函数的参iNum1和iNum2以及全局变量iBaseNum的值。 addNum()的最后一步调用了doAdd(),把两个参数和全局变量相加,并返回它们的和。这里要掌握的重要概念是,doAdd()函数根本不接受参数,它使用的值是从执行环境中获取的,因此输出的结果为:30。
        可以看到,闭包是 ECMAScript 中非常强大多用的一部分,可用于执行复杂的计算。就像使用任何高级函数一样,使用闭包要小心,因为它们可能会变得非常复杂。

以上就是本文的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
JavaScript Event学习补遗 addEventSimple
Feb 11 Javascript
javascript中获取下个月一号,是星期几
Jun 01 Javascript
javascript 判断中文字符长度的函数代码
Aug 27 Javascript
iScroll中事件点击触发两次解决方案
Mar 11 Javascript
jQuery与Ajax以及序列化
Feb 01 Javascript
基于BootStrap Metronic开发框架经验小结【三】下拉列表Select2插件的使用
May 12 Javascript
Bootstrap模态框水平垂直居中与增加拖拽功能
Nov 09 Javascript
Vue.js项目模板搭建图文教程
Sep 20 Javascript
JavaScript 异步调用
Oct 25 Javascript
jQuery EasyUI 折叠面板accordion的使用实例(分享)
Dec 25 jQuery
Vue实例的对象参数options的几个常用选项详解
Nov 08 Javascript
微信小程序实现页面监听自定义组件的触发事件
Nov 01 Javascript
javascript定义类和类的实现实例详解
Dec 01 #Javascript
深入JavaScript高级程序设计之对象、数组(栈方法,队列方法,重排序方法,迭代方法)
Dec 01 #Javascript
js跨域请求数据的3种常用的方法
Dec 01 #Javascript
jQuery插件实现多级联动菜单效果
Dec 01 #Javascript
基于Jquery实现焦点图淡出淡入效果
Nov 30 #Javascript
轻松学习jQuery插件EasyUI EasyUI创建RSS Feed阅读器
Nov 30 #Javascript
轻松学习jQuery插件EasyUI EasyUI创建树形菜单
Nov 30 #Javascript
You might like
用windows下编译过的eAccelerator for PHP 5.1.6实现php加速的使用方法
2007/09/30 PHP
PHP 截取字符串函数整理(支持gb2312和utf-8)
2010/02/16 PHP
一个图片地址分解程序(用于PHP小偷程序)
2014/08/23 PHP
php header函数的常用http头设置
2015/06/25 PHP
php实现中文转数字
2016/02/18 PHP
PHP实现网站应用微信登录功能详解
2019/04/11 PHP
event对象的方法 兼容多浏览器
2009/06/27 Javascript
使用JavaScript库还是自己写代码?
2010/01/28 Javascript
JavaScript 空位补零实现代码
2010/02/26 Javascript
别了 JavaScript中的isXX系列
2012/08/01 Javascript
用javascript对一个json数组深度赋值示例
2014/07/27 Javascript
JS实现从表格中动态删除指定行的方法
2015/03/31 Javascript
详解百度百科目录导航树小插件
2017/01/08 Javascript
nodejs操作mongodb的填删改查模块的制作及引入实例
2018/01/02 NodeJs
详解VUE里子组件如何获取父组件动态变化的值
2018/12/26 Javascript
推荐15个最好用的JavaScript代码压缩工具
2019/02/13 Javascript
[42:20]Secret vs Liquid 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
详解Django框架中用context来解析模板的方法
2015/07/20 Python
python用装饰器自动注册Tornado路由详解
2017/02/14 Python
Python 爬虫图片简单实现
2017/06/01 Python
python中利用zfill方法自动给数字前面补0
2018/04/10 Python
Python大数据之网络爬虫的post请求、get请求区别实例分析
2019/11/16 Python
Django集成celery发送异步邮件实例
2019/12/17 Python
python正则表达式匹配IP代码实例
2019/12/28 Python
python 解压、复制、删除 文件的实例代码
2020/02/26 Python
Python Sqlalchemy如何实现select for update
2020/10/12 Python
电子信息专业自荐书
2014/02/04 职场文书
对公司合理化的建议书
2014/03/12 职场文书
文案策划求职信
2014/03/18 职场文书
学生会宣传部部长竞选演讲稿
2014/04/25 职场文书
最新离婚协议书范本
2014/08/19 职场文书
竞选大学学委演讲稿
2014/09/13 职场文书
2014年检察院个人工作总结
2014/12/09 职场文书
2019教师的学习计划
2019/06/25 职场文书
使用Golang的channel交叉打印两个数组的操作
2021/04/29 Golang
python ConfigParser库的使用及遇到的坑
2022/02/12 Python