JavaScript入门之基本函数详解


Posted in Javascript onOctober 21, 2011

总的来说,函数在JavaScript中可以:

◆ 被赋值给一个变量

◆ 被赋值为对象的属性

◆ 作为参数被传入别的函数

◆ 作为函数的结果被返回

◆ 用字面量来创建

函数对象

1.1 创建函数

创建JavaScript函数的一种不长用的方式(几乎没有人用)是通过new操作符来作用于Function“构造器”:

var funcName = new Function( [argname1, [... argnameN,]] body );

参数列表中可以有任意多的参数,然后紧跟着是函数体,比如:
var add = new Function("x", "y", "return(x+y)"); 
print(add(2, 4));

将会打印结果:

6

但是,谁会用如此难用的方式来创建一个函数呢?如果函数体比较复杂,那拼接这个String要花费很大的力气,所以JavaScript提供了一种语法糖,即通过字面量来创建函数:

function add(x, y){ 
return x + y; 
}

或:
var add = function(x, y){ 
return x + y; 
}

事实上,这样的语法糖更容易使传统领域的程序员产生误解,function关键字会调用Function来new一个对象,并将参数表和函数体准确的传递给Function的构造器。

通常来说,在全局作用域(作用域将在下一节详细介绍)内声明一个对象,只不过是对一个属性赋值而已,比如上例中的add函数,事实上只是为全局对象添加了一个属性,属性名为add,而属性的值是一个对象,即function(x, y){return x+y;},理解这一点很重要,这条语句在语法上跟:

var str = "This is a string";

并无二致。都是给全局对象动态的增加一个新的属性,如此而已。

为了说明函数跟其他的对象一样,都是作为一个独立的对象而存在于JavaScript的运行系统,我们不妨看这样一个例子:

function p(){ 
print("invoke p by ()"); 
} p.id = "func"; 
p.type = "function"; 
print(p); 
print(p.id+":"+p.type); 
print(p());

没有错,p虽然引用了一个匿名函数(对象),但是同时又可以拥有属性,完全跟其他对象一样,运行结果如下:

function (){
print("invoke p by ()");
}
func:function
invoke p by ()

1.2 函数的参数

在JavaScript中,函数的参数是比较有意思的,比如,你可以将任意多的参数传递给一个函数,即使这个函数声明时并未制定形式参数,比如:

function adPrint(str, len, option){ 
var s = str || "default"; 
var l = len || s.length; 
var o = option || "i"; s = s.substring(0, l); 
switch(o){ 
case "u": 
s = s.toUpperCase(); 
break; 
case "l": 
s = s.toLowerCase(); 
break; 
default: 
break; 
} 
print(s); 
} 
adPrint("Hello, world"); 
adPrint("Hello, world", 5); 
adPrint("Hello, world", 5, "l");//lower case 
adPrint("Hello, world", 5, "u");//upper case

函数adPrint在声明时接受三个形式参数:要打印的串,要打印的长度,是否转换为大小写的标记。但是在调用的时候,我们可以按顺序传递给adPrint一个参数,两个参数,或者三个参数(甚至可以传递给它多于3个,没有关系),运行结果如下:

Hello, world
Hello
hello
HELLO

事实上,JavaScript在处理函数的参数时,与其他编译型的语言不一样,解释器传递给函数的是一个类似于数组的内部值,叫arguments,这个在函数对象生成的时候就被初始化了。比如我们传递给adPrint一个参数的情况下,其他两个参数分别为undefined.这样,我们可以才adPrint函数内部处理那些undefined参数,从而可以向外部公开:我们可以处理任意参数。

我们通过另一个例子来讨论这个神奇的arguments:

function sum(){ 
var result = 0; 
for(var i = 0, len = arguments.length; i < len; i++){ 
var current = arguments[i]; 
if(isNaN(current)){ 
throw new Error("not a number exception"); 
}else{ 
result += current; 
} 
} return result; 
} 
print(sum(10, 20, 30, 40, 50)); 
print(sum(4, 8, 15, 16, 23, 42));//《迷失》上那串神奇的数字 
print(sum("new"));

函数sum没有显式的形参,而我们又可以动态的传递给其任意多的参数,那么,如何在sum函数中如何引用这些参数呢?这里就需要用到arguments这个伪数组了,运行结果如下:

150
108
Error: not a number exception

函数作用域

作用域的概念在几乎所有的主流语言中都有体现,在JavaScript中,则有其特殊性:JavaScript中的变量作用域为函数体内有效,而无块作用域,我们在Java语言中,可以这样定义for循环块中的下标变量:

public void method(){
for(int i = 0; i < obj1.length; i++){
//do something here;
}
//此时的i为未定义
for(int i = 0; i < obj2.length; i++){
//do something else;
}
}
而在JavaScript中:

function func(){ 
for(var i = 0; i < array.length; i++){ 
//do something here. 
} 
//此时i仍然有值,及I == array.length 
print(i);//i == array.length; 
}

JavaScript的函数是在局部作用域内运行的,在局部作用域内运行的函数体可以访问其外层的(可能是全局作用域)的变量和函数。JavaScript的作用域为词法作用域,所谓词法作用域是说,其作用域为在定义时(词法分析时)就确定下来的,而并非在执行时确定,如下例:
var str = "global"; 
function scopeTest(){ 
print(str); 
var str = "local"; 
print(str); 
} scopeTest();

运行结果是什么呢?初学者很可能得出这样的答案:

global
local

而正确的结果应该是:

undefined
local

因为在函数scopeTest的定义中,预先访问了未声明的变量str,然后才对str变量进行初始化,所以第一个print(str)会返回undifined错误。那为什么函数这个时候不去访问外部的str变量呢?这是因为,在词法分析结束后,构造作用域链的时候,会将函数内定义的var变量放入该链,因此str在整个函数scopeTest内都是可见的(从函数体的第一行到最后一行),由于str变量本身是未定义的,程序顺序执行,到第一行就会返回未定义,第二行为str赋值,所以第三行的print(str)将返回”local”。

函数上下文

在Java或者C/C++等语言中,方法(函数)只能依附于对象而存在,不是独立的。而在JavaScript中,函数也是一种对象,并非其他任何对象的一部分,理解这一点尤为重要,特别是对理解函数式的JavaScript非常有用,在函数式编程语言中,函数被认为是一等的。

函数的上下文是可以变化的,因此,函数内的this也是可以变化的,函数可以作为一个对象的方法,也可以同时作为另一个对象的方法,总之,函数本身是独立的。可以通过Function对象上的call或者apply函数来修改函数的上下文:

call和apply

call和apply通常用来修改函数的上下文,函数中的this指针将被替换为call或者apply的第一个参数,我们不妨来看看JavaScript入门之对象与JSON中的例子:

//定义一个人,名字为jack
var jack = {
name : "jack",
age : 26
}

//定义另一个人,名字为abruzzi
var abruzzi = {
name : "abruzzi",
age : 26
}

//定义一个全局的函数对象
function printName(){
return this.name;
}

//设置printName的上下文为jack, 此时的this为jack
print(printName.call(jack));
//设置printName的上下文为abruzzi,此时的this为abruzzi
print(printName.call(abruzzi));

print(printName.apply(jack));
print(printName.apply(abruzzi));
只有一个参数的时候call和apply的使用方式是一样的,如果有多个参数:

setName.apply(jack, ["Jack Sept."]);
print(printName.apply(jack));

setName.call(abruzzi, "John Abruzzi");
print(printName.call(abruzzi));
得到的结果为:

Jack Sept.
John Abruzzi
apply的第二个参数为一个函数需要的参数组成的一个数组,而call则需要跟若干个参数,参数之间以逗号(,)隔开即可。

使用函数

前面已经提到,在JavaScript中,函数可以

◆ 被赋值给一个变量

◆ 被赋值为对象的属性

◆ 作为参数被传入别的函数

◆ 作为函数的结果被返回

我们就分别来看看这些场景:

赋值给一个变量:

//声明一个函数,接受两个参数,返回其和
function add(x, y){
return x + y;
}

var a = 0;
a = add;//将函数赋值给一个变量
var b = a(2, 3);//调用这个新的函数a
print(b);
这段代码会打印”5”,因为赋值之后,变量a引用函数add,也就是说,a的值是一个函数对象(一个可执行代码块),因此可以使用a(2, 3)这样的语句来进行求和操作。

赋值为对象的属性:

var obj = { 
id : "obj1" 
} obj.func = add;//赋值为obj对象的属性 
obj.func(2, 3);//返回5

事实上,这个例子与上个例子的本质上是一样的,第一个例子中的a变量,事实上是全局对象(如果在客户端环境中,表示为window对象)的一个属性。而第二个例子则为obj对象,由于我们很少直接的引用全局对象,就分开来描述。

作为参数传递:

//高级打印函数的第二个版本
function adPrint2(str, handler){
print(handler(str));
}

//将字符串转换为大写形式,并返回
function up(str){
return str.toUpperCase();
}

//将字符串转换为小写形式,并返回
function low(str){
return str.toLowerCase();
}

adPrint2("Hello, world", up);
adPrint2("Hello, world", low);
运行此片段,可以得到这样的结果:

HELLO, WORLD
hello, world

应该注意到,函数adPrint2的第二个参数,事实上是一个函数,将这个处理函数作为参数传入,在adPrint2的内部,仍然可以调用这个函数,这个特点在很多地方都是有用的,特别是,当我们想要处理一些对象,但是又不确定以何种形式来处理,则完全可以将“处理方式”作为一个抽象的粒度来进行包装(即函数)。

作为函数的返回值:

先来看一个最简单的例子:

function currying(){ 
return function(){ 
print("curring"); 
} 
}

函数currying返回一个匿名函数,这个匿名函数会打印”curring”,简单的调用currying()会得到下面的结果:
function (){ 
print("curring"); 
}

如果要调用currying返回的这个匿名函数,需要这样:

currying()();
第一个括号操作,表示调用currying本身,此时返回值为函数,第二个括号操作符调用这个返回值,则会得到这样的结果:

currying

Javascript 相关文章推荐
javascript HTMLEncode HTMLDecode的完整实例(兼容ie和火狐)
Jun 02 Javascript
jquery 屏蔽一个区域内的所有元素,禁止输入
Oct 22 Javascript
获取dom元素那些讨厌的位置封装代码
Jun 23 Javascript
基于node.js的快速开发透明代理
Dec 25 Javascript
JQuery中阻止事件冒泡几种方式及其区别介绍
Jan 15 Javascript
js实现鼠标感应向下滑动隐藏菜单的方法
Feb 20 Javascript
使用JS实现图片展示瀑布流效果(简单实例)
Sep 06 Javascript
Node.js读写文件之批量替换图片的实现方法
Sep 07 Javascript
老生常谈javascript中逻辑运算符&amp;&amp;和||的返回值问题
Apr 13 Javascript
带你快速理解javascript中的事件模型
Aug 14 Javascript
小程序实现投票进度条
Nov 20 Javascript
javascript实现前端成语点击验证优化
Jun 24 Javascript
JavaScript入门之对象与JSON详解
Oct 21 #Javascript
JavaScript内核之基本概念
Oct 21 #Javascript
输入框的字数时时统计—关于 onpropertychange 和 oninput 使用
Oct 21 #Javascript
学习JavaScript的最佳方法分享
Oct 21 #Javascript
修复IE9&amp;safari 的sort方法
Oct 21 #Javascript
修复ie8&amp;chrome下window的resize事件多次执行
Oct 20 #Javascript
jquery ajax return没有返回值的解决方法
Oct 20 #Javascript
You might like
杏林同学录(六)
2006/10/09 PHP
说明的比较细的php 正则学习实例
2008/07/30 PHP
laravel如何开启跨域功能示例详解
2017/08/31 PHP
12款经典的白富美型—jquery图片轮播插件—前端开发必备
2013/01/08 Javascript
JavaScript中的比较操作符&gt;、=、
2014/12/31 Javascript
JS实现简易图片轮播效果的方法
2015/03/25 Javascript
文本框只能输入数字的实现方法(兼容IE火狐)
2016/06/25 Javascript
JS获取及验证开始结束日期的方法
2016/08/20 Javascript
利用Jquery队列实现根据输入数量显示的动画
2016/09/01 Javascript
轻量级JS Cookie插件js-cookie的使用方法
2018/03/22 Javascript
vue组件与复用详解
2018/04/08 Javascript
原生JS实现的雪花飘落动画效果
2018/05/03 Javascript
webpack4 SplitChunks实现代码分隔详解
2019/05/23 Javascript
微信小程序 自定义弹窗实现过程(附代码)
2019/12/05 Javascript
使用python编写android截屏脚本双击运行即可
2014/07/21 Python
Python数据结构之栈、队列的实现代码分享
2017/12/04 Python
Python多继承顺序实例分析
2018/05/26 Python
Python socket模块方法实现详解
2019/11/05 Python
python中元组的用法整理
2020/06/15 Python
为什么称python为胶水语言
2020/06/16 Python
Python 忽略文件名编码的方法
2020/08/01 Python
python 爬虫爬取京东ps4售卖情况
2020/12/18 Python
德国著名廉价网上药店:Shop-Apotheke
2017/07/23 全球购物
英国可持续奢侈品包包品牌:Elvis & Kresse
2018/08/05 全球购物
亚洲航空公司官方网站:AirAsia
2019/11/25 全球购物
华为慧通笔试题
2016/04/22 面试题
英文自荐信
2013/12/19 职场文书
导师推荐信范文
2014/05/09 职场文书
国际贸易毕业生自荐书
2014/06/22 职场文书
2014大学校园光棍节活动策划书
2014/09/29 职场文书
党员群众路线个人整改措施思想汇报
2014/10/12 职场文书
仰望星空观后感
2015/06/10 职场文书
寒假致家长的一封信
2015/10/10 职场文书
python pygame入门教程
2021/06/01 Python
Centos7中MySQL数据库使用mysqldump进行每日自动备份的编写
2021/08/02 MySQL
通过Python把学姐照片做成拼图游戏
2022/02/15 Python