锁定老帖子 主题:JavaScript内核系列 第4章 函数
精华帖 (4) :: 良好帖 (17) :: 新手帖 (0) :: 隐藏帖 (6)
|
|
---|---|
作者 | 正文 |
发表时间:2010-04-18
最后修改:2010-04-18
第四章 函数函数,在C语言之类的过程式语言中,是顶级的实体,而在Java/C++之类的面向对象的语言中,则被对象包装起来,一般称为对象的方法。而在JavaScript中,函数本身与其他任何的内置对象在低位上是没有任何区别的,也就是说,函数本身也是对象。 总的来说,函数在JavaScript中可以:
4.1函数对象4.1.1 创建函数创建JavaScript函数的一种不长用的方式(几乎没有人用)是通过new操作符来作用于Function“构造器”:
参数列表中可以有任意多的参数,然后紧跟着是函数体,比如:
将会打印结果:
6
但是,谁会用如此难用的方式来创建一个函数呢?如果函数体比较复杂,那拼接这个String要花费很大的力气,所以JavaScript提供了一种语法糖,即通过字面量来创建函数:
或:
事实上,这样的语法糖更容易使传统领域的程序员产生误解,function关键字会调用Function来new一个对象,并将参数表和函数体准确的传递给Function的构造器。 通常来说,在全局作用域(作用域将在下一节详细介绍)内声明一个对象,只不过是对一个属性赋值而已,比如上例中的add函数,事实上只是为全局对象添加了一个属性,属性名为add,而属性的值是一个对象,即function(x, y){return x+y;},理解这一点很重要,这条语句在语法上跟:
并无二致。都是给全局对象动态的增加一个新的属性,如此而已。 为了说明函数跟其他的对象一样,都是作为一个独立的对象而存在于JavaScript的运行系统,我们不妨看这样一个例子:
没有错,p虽然引用了一个匿名函数(对象),但是同时又可以拥有属性,完全跟其他对象一样,运行结果如下:
4.1.2 函数的参数在JavaScript中,函数的参数是比较有意思的,比如,你可以将任意多的参数传递给一个函数,即使这个函数声明时并未制定形式参数,比如:
函数adPrint在声明时接受三个形式参数:要打印的串,要打印的长度,是否转换为大小写的标记。但是在调用的时候,我们可以按顺序传递给adPrint一个参数,两个参数,或者三个参数(甚至可以传递给它多于3个,没有关系),运行结果如下:
Hello, world
Hello hello HELLO 事实上,JavaScript在处理函数的参数时,与其他编译型的语言不一样,解释器传递给函数的是一个类似于数组的内部值,叫arguments,这个在函数对象生成的时候就被初始化了。比如我们传递给adPrint一个参数的情况下,其他两个参数分别为undefined.这样,我们可以才adPrint函数内部处理那些undefined参数,从而可以向外部公开:我们可以处理任意参数。
我们通过另一个例子来讨论这个神奇的arguments:
函数sum没有显式的形参,而我们又可以动态的传递给其任意多的参数,那么,如何在sum函数中如何引用这些参数呢?这里就需要用到arguments这个伪数组了,运行结果如下:
150
108 Error: not a number exception 4.2函数作用域作用域的概念在几乎所有的主流语言中都有体现,在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中:
JavaScript的函数是在局部作用域内运行的,在局部作用域内运行的函数体可以访问其外层的(可能是全局作用域)的变量和函数。JavaScript的作用域为词法作用域,所谓词法作用域是说,其作用域为在定义时(词法分析时)就确定下来的,而并非在执行时确定,如下例:
运行结果是什么呢?初学者很可能得出这样的答案:
global
local 而正确的结果应该是:
undefined
local
因为在函数scopeTest的定义中,预先访问了未声明的变量str,然后才对str变量进行初始化,所以第一个print(str)会返回undifined错误。那为什么函数这个时候不去访问外部的str变量呢?这是因为,在词法分析结束后,构造作用域链的时候,会将函数内定义的var变量放入该链,因此str在整个函数scopeTest内都是可见的(从函数体的第一行到最后一行),由于str变量本身是未定义的,程序顺序执行,到第一行就会返回未定义,第二行为str赋值,所以第三行的print(str)将返回”local”。
4.3函数上下文在Java或者C/C++等语言中,方法(函数)只能依附于对象而存在,不是独立的。而在JavaScript中,函数也是一种对象,并非其他任何对象的一部分,理解这一点尤为重要,特别是对理解函数式的JavaScript非常有用,在函数式编程语言中,函数被认为是一等的。 函数的上下文是可以变化的,因此,函数内的this也是可以变化的,函数可以作为一个对象的方法,也可以同时作为另一个对象的方法,总之,函数本身是独立的。可以通过Function对象上的call或者apply函数来修改函数的上下文: 4.4 call和applycall和apply通常用来修改函数的上下文,函数中的this指针将被替换为call或者apply的第一个参数,我们不妨来看看2.1.3小节的例子:
//定义一个人,名字为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则需要跟若干个参数,参数之间以逗号(,)隔开即可。
4.5使用函数前面已经提到,在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)这样的语句来进行求和操作。
赋值为对象的属性:
事实上,这个例子与上个例子的本质上是一样的,第一个例子中的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的内部,仍然可以调用这个函数,这个特点在很多地方都是有用的,特别是,当我们想要处理一些对象,但是又不确定以何种形式来处理,则完全可以将“处理方式”作为一个抽象的粒度来进行包装(即函数)。
作为函数的返回值: 先来看一个最简单的例子:
函数currying返回一个匿名函数,这个匿名函数会打印”curring”,简单的调用currying()会得到下面的结果:
如果要调用currying返回的这个匿名函数,需要这样:
currying()(); 第一个括号操作,表示调用currying本身,此时返回值为函数,第二个括号操作符调用这个返回值,则会得到这样的结果:
currying
附:由于作者本身水平有限,文中难免有纰漏错误等,或者语言本身有不妥当之处,欢迎及时指正,提出建议,参与讨论,谢谢大家! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-04-18
函数也是对象这点很有意思,跟其他的语言差别很大。看来对JavaScript的理解还很浅,好好学习!
|
|
返回顶楼 | |
发表时间:2010-04-18
不错,前三篇都已经看过了,内容很好,例子也都很有代表性。4
|
|
返回顶楼 | |
发表时间:2010-04-18
修改了一部分之后发现:
第五章[数组]和第六章[正则表达式]内容都比较短,但是又不能合并在一章,而把闭包单独列出来作为第七章来讲似乎又有点不太合理,应该归并到第九章[函数式的JavaScript]中,不知道大家的意见如何? 第八章[面向对象的JavaScript]倒是已经差不多完成了,但是考虑到前面的内容还没有整理完,就先等等。 |
|
返回顶楼 | |
发表时间:2010-04-19
楼主辛苦了。 我觉得数组和前面的String Math Date等可以写到一章。 正则表达式写1到2章。 闭包写3章。 第1章讲闭包的概念,以及各种常见的形式和简单的经典用例,比如用在事件绑定和setTimeout中。 第二章2闭包在项目中的具体应用,怎样用闭包来改善代码, 用闭包实现封装,aop,柯里化等等。 第三章讲讲闭包和函数式编程的结合使用。
这两章可以多结合一下项目中使用过的代码,或者jquery等框架的源码来详细讲一下,我觉得要写就要写的最好,至少是最细。很多人都会有一段时间,似乎对闭包有所了解了,但不知道到底有什么用,具体可以用到什么地方,甚至觉得只是个卖弄的把戏。 现在泛讲闭包的文章很多,但基本都是大搞术语,一点都不通俗,隔靴搔痒,看过之后远到不了高潮。 另外,还是觉得标题有点过了。 |
|
返回顶楼 | |
发表时间:2010-04-19
02221021 写道 楼主辛苦了。 我觉得数组和前面的String Math Date等可以写到一章。 正则表达式写1到2章。 闭包写3章。 第1章讲闭包的概念,以及各种常见的形式和简单的经典用例,比如用在事件绑定和setTimeout中。 第二章2闭包在项目中的具体应用,怎样用闭包来改善代码, 用闭包实现封装,aop,柯里化等等。 第三章讲讲闭包和函数式编程的结合使用。
这两章可以多结合一下项目中使用过的代码,或者jquery等框架的源码来详细讲一下,我觉得要写就要写的最好,至少是最细。很多人都会有一段时间,似乎对闭包有所了解了,但不知道到底有什么用,具体可以用到什么地方,甚至觉得只是个卖弄的把戏。 现在泛讲闭包的文章很多,但基本都是大搞术语,一点都不通俗,隔靴搔痒,看过之后远到不了高潮。 另外,还是觉得标题有点过了。 这位朋友关于章节的划分,以及各章节的内容大纲的意见都很好,我会多加考虑,非常感谢。 理论和实践相结合的这种方式是解释/学习任何知识点都应该采用的方法,例子完全来源于实践的一个隐患是跟具体应用结合的太过紧密,可能影响到读者的发挥,演绎。这是一个比较难把握的地方,我会尽量注意。 另外,关于标题,怎么说呢?我主要是想要将JavaScript的核心概念,或者说相异于传统的编程语言的概念专门列出来讲,英文名称应该是 JavaScript Core,大家觉得译为什么名字比较妥帖呢? |
|
返回顶楼 | |
发表时间:2010-04-19
在John大的JavaScript忍者秘密中,闭包和原型链是分别独立成章的,还有一个with语法比较有意思,不过没怎么看懂.
还有一本Good part of JavaScript里面把各个关键字都拿出来并且还用JS实现了一遍,感觉不错.里面将function 和 new关键字, constructor函数, prototype 等联系起来将整个闭包,继承之类的实现了一遍,受益匪浅. 楼主也可以以另类视角来剖析一番.期待.. |
|
返回顶楼 | |
发表时间:2010-04-19
dreampuf01 写道 在John大的JavaScript忍者秘密中,闭包和原型链是分别独立成章的,还有一个with语法比较有意思,不过没怎么看懂.
还有一本Good part of JavaScript里面把各个关键字都拿出来并且还用JS实现了一遍,感觉不错.里面将function 和 new关键字, constructor函数, prototype 等联系起来将整个闭包,继承之类的实现了一遍,受益匪浅. 楼主也可以以另类视角来剖析一番.期待.. 嗯,你的建议确实都很有意思。我可以参考下这些牛人的资料,在消化之后再做整理。这个系列后边的章节中,面向对象的JavaScript会对原型链,继承等概念进行实例化的讨论。而函数式的JavaScript会涉及闭包,柯里化,map等的讨论,尽量写的详细些,并结合实例来讲. 谢谢你的建议! |
|
返回顶楼 | |
发表时间:2010-04-19
JavaScript Core
JavaScript 扣肉 JavaScript 核心技术 ??? |
|
返回顶楼 | |
发表时间:2010-04-19
iq527 写道 JavaScript Core
JavaScript 扣肉 JavaScript 核心技术 ??? JavaScript核心技术貌似已经出版过一本书了,用别人的书名的话肯定不妥,呵呵。 Core或者Kernel直译过来就是内核了,或者该叫JavaScript Core Concepts. |
|
返回顶楼 | |