JavaScript入门之基本函数详解

总的来说,函数在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

(0)

相关推荐

  • JavaScript基础函数整理汇总

    这里给大家整理汇总了一些javascript的基础函数,都是比较常用和实用的.整理出来也有助于大家更好的理解javascript. 复制代码 代码如下: <script type="text/javascript">     /*创建函数和字面量函数*/     /*         function add(){             alert("函数创建成功")         };         var testfunction=functi

  • javascript最基本的函数汇总

    我记得早期的 JavaScript ,要完成任何事情几乎都绕不开一些简单的函数,因为浏览器提供商实现功能有所差异,而且不只是边缘功能,基础功能也一样,如 addEventListener 和 attachEvent.虽然时代变了,但仍有一些函数是每个开发者都应该掌握的,以便于完成某些功能和提高性能. debounce 对于高耗能事件,debounce 函数是一种不错解决方案.如果你不对 scroll.resize.和 key* 事件使用 debounce  函数,那么你几乎等同于犯了错误.下面的

  • JavaScript入门之基本函数详解

    总的来说,函数在JavaScript中可以: ◆ 被赋值给一个变量 ◆ 被赋值为对象的属性 ◆ 作为参数被传入别的函数 ◆ 作为函数的结果被返回 ◆ 用字面量来创建 函数对象 1.1 创建函数 创建JavaScript函数的一种不长用的方式(几乎没有人用)是通过new操作符来作用于Function"构造器": 复制代码 代码如下: var funcName = new Function( [argname1, [... argnameN,]] body ); 参数列表中可以有任意多的参

  • JVM上高性能数据格式库包Apache Arrow入门和架构详解(Gkatziouras)

    Apache Arrow是是各种大数据工具(包括BigQuery)使用的一种流行格式,它是平面和分层数据的存储格式.它是一种加快应用程序内存密集型. 数据处理和数据科学领域中的常用库: Apache Arrow.诸如Apache Parquet,Apache Spark,pandas之类的开放源代码项目以及许多商业或封闭源代码服务都使用Arrow.它提供以下功能: 内存计算 标准化的柱状存储格式 一个IPC和RPC框架,分别用于进程和节点之间的数据交换 让我们看一看在Arrow出现之前事物是如何

  • 基于JavaScript表单脚本(详解)

    什么是表单? 一个表单有三个基本组成部分: 表单标签:这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务器的方法. 表单域:包含了文本框.密码框.隐藏域.多行文本框.复选框.单选框.下拉选择框和文件上传框等. 表单按钮:包括提交按钮.复位按钮和一般按钮:用于将数据传送到服务器上的CGI脚本或者取消输入,还可以用表单按钮来控制其他定义了处理脚本的处理工作. JavaScript与表单间的关系:JS最初的应用就是用于分担服务器处理表单的责任,打破依赖服务器的局面,尽管目前web和jav

  • 基于Python数据可视化利器Matplotlib,绘图入门篇,Pyplot详解

    Pyplot matplotlib.pyplot是一个命令型函数集合,它可以让我们像使用MATLAB一样使用matplotlib.pyplot中的每一个函数都会对画布图像作出相应的改变,如创建画布.在画布中创建一个绘图区.在绘图区上画几条线.给图像添加文字说明等.下面我们就通过实例代码来领略一下他的魅力. import matplotlib.pyplot as plt plt.plot([1,2,3,4]) plt.ylabel('some numbers') plt.show() 上图是我们通

  • JavaScript数据结构链表知识详解

    最近在看<javascript数据结构和算法>这本书,补一下数据结构和算法部分的知识,觉得自己这块是短板. 链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中不是连续放置的.每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成. 好处:可以添加或移除任意项,它会按需扩容,且不需要移动其他元素. 与数组的区别: 数组:可以直接访问任何位置的任何元素: 链表:想要访问链表中的一个元素,需要从起点(表头)开始迭代列表直到找到所需的元素. 做点小笔记. funct

  • JavaScript里 ==与===区别详解

    1.对于string,number等基础类型,==和===是有区别的 1)不同类型间比较,==之比较"转化成同一类型后的值"看"值"是否相等,===如果类型不同,其结果就是不等 2)同类型比较,直接进行"值"比较,两者结果一样 2.对于Array,Object等高级类型,==和===是没有区别的 进行"指针地址"比较 3.基础类型与高级类型,==和===是有区别的 1)对于==,将高级转化为基础类型,进行"值&quo

  • JavaScript String 对象常用方法详解

    字符串是不可变的.字符串方法,不会改变原有字符串的内容,而是返回一个新字符串. charAt():返回在指定位置的字符. concat():连接两个字符串文本,并返回一个新的字符串. indexOf(): indexOf() 方法返回指定值在字符串对象中首次出现的位置. match(): 使用正则表达式与字符串相比较. replace(): 被用来在正则表达式和字符串直接比较,然后用新的子串来替换被匹配的子串. slice(): 摘取一个字符串区域,返回一个新的字符串. split(): 通过分

  • JavaScript hasOwnProperty() 函数实例详解

    hasOwnProperty()函数用于指示一个对象自身(不包括原型链)是否具有指定名称的属性.如果有,返回true,否则返回false. 该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法. IE 5.5+.FireFox.Chrome.Safari.Opera等主流浏览器均支持该函数. 语法 object.hasOwnProperty( propertyName ) 参数 参数 描述 propertyName Str

  • JavaScript This指向问题详解

    这篇文章主要介绍了JavaScript This指向问题详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 各位小伙伴在面试中被面试官问道this指向问题一定不少吧,同时还被问道apply,call和bind的用法区别,现在,就来简单的聊一聊this到底指向何方. 1.基本概念 MDN的官方解释:与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别.在绝大多数情况下,

  • javascript异常处理实现原理详解

    这篇文章主要介绍了javascript异常处理实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.什么是例外处理 当 JavaScript程序在运行中发生了诸如数组索引越界.类型不匹配或者语法错误时,JavaScript解释器就会引发例外处理. ECMAScript定义了六种类型的错误,除此之外,我们可以使用Error对象和throw语句来创建并引发自定义的例外处理信息. 通过运用例外处理技术,我们可以实现用结构化的方式来响应错误事

随机推荐