JavaScript:The Good Parts (四)
第4章 函数(Functions)
4.1函数对象(Function Objects)
4.2函数字面量(Function Literal)
4.3调用(Invocation)
4.4参数(Arguments)
4.5返回(Return)
4.6异常(Exceptions)
4.7给类型增加方法(Augmenting Types)
4.8递归(Recursion)
4.9作用域(Scope)
4.10闭包(Closure)
4.11回调(Callbacks)
4.12模块(module)
4.13
4.14
4.15
JS中最好的特性就是它对函数的实现,它几乎无所不能。
函数包含一组语句,它们是JS的基础模块单元,用于代码复用、信息隐藏和组合调用。
函数用于指定对象的行为。一般来说,所谓编程就是将一组需求分解成一组函数与数据结构的技能。
4.1函数对象(Function Objects)
(1)在JS中函数就是对象。对象是“名/值”对的集合并拥有一个连到原型对象的隐藏连接。对象字面量产生的对象连接到Object.prototype,函数对象连接到Function.prototype(该原型对象本身连接到Object.prototype)。每个函数在创建时附有两个附加的属性:函数的上下文和实现函数行为的代码
(1.函数的上下文:指的是函数所在的 scope。一个嵌套函数的 scope 是它的外层函数,顶层函数的 scope 是全局。这个 scope 是静态的,不可改变的。一个函数的 scope 一层层向上。http://www.douban.com/group/topic/22693981 2.实现函数行为的代码:JS在创建一个函数对象时,会给该对象设置一个调用属性;当JS调用一个函数时,可以理解为调用此函数的“调用”属性。)。
(2)每个函数对象在创建时也随带有一个prototype属性。它的值是一个拥有constructor属性且值即为该函数的对象
(http://blog.csdn.net/adwu73/article/details/7219887)。这和隐藏连接到Function.prototype完全不同。
(3)因为函数是对象,所以它们可以像任何其他的值一样被使用。函数可以存放在变量、对象和数组中。函数可以被当作参数传递给其他函数,函数可以再返回函数。而且因为函数是对象,所以函数可以拥有方法。
(4)函数的与众不同之处在于它们可以被调用。
4.2函数字面量(Function Literal)
函数对象可以通过函数字面量来创建:
var add= function(){ //创建一个名为add的变量,并用来把两个数字相加的函数赋值给它。
return a + b;
};
函数字面量包含四个部分:
(1)第一个部分是保留字function。
(2)第二个部分是函数名,它可以被省略。函数可以用它的名字来递归地调用自己。此名字也能被调试器和开发工具用来识别函数。如果没有给函数命名,比如上面这个例子,它会被认为是匿名函数。
(3)函数第三部分是包围在圆括号中的一组参数。其中每个参数用逗号分隔,这些名称将被定义为函数中的变量。它们不像普通的变量那样将被初始化为undefined,而是在该函数被调用时初始化为实际提供的参数的值。
(4)第四部分是包围在花括号中的一组语句。这些预计是函数的主体。它们在函数被调用时执行。
函数字面量可以出现在任何允许表达式出现的地方。函数也可以被定义在其他函数中。一个内部函数自然可以访问自己的参数和变量,同时它也能方便地访问它被嵌套在其中的那个函数
(该函数对应的外部函数)的参数与变量。通过函数字面量创建的函数对象包含一个连到外部上下文的连接。这称为闭包
(http://www.nowamagic.net/program/program_HowToUnderstandClosure.php)。
4.3调用(Invocation)
调用一个函数将暂停当前函数的执行,传递控制权和参数给新函数。除了声明时定义的形式参数,每个函数接收两个附加的参数
:[color=red]this和
arguments。参数this在面向对象编程中非常重要,它的值取决于调用模式。[/color]在JS中一共有四种调用模式:
方法调用模式、函数调用模式、构造器调用模式和apply调用模式。这些模式在初始化关键参数this上存在差异。
调用运算符是跟在任何产生一个函数值的表达式之后的一对圆括号。圆括号内可包含零个或者多个用都后隔开的表达式,每个表达式产生一个参数值。每个参数值被赋予函数声明时定义的形式参数名。当实际参数值过多了,超出的参数值将被忽略。如果实际参数值过少,缺少的值将会被替换为undefined。对参数值不会进行类型检查:任何类型的值都可以被传递给参数。
(1)方法调用模式(The Method Invocation Pattern)
当一个函数被保存为对象的一个属性时,我们称他为一个方法。
当一个方法被调用时,this被绑定到该对象。如果一个调用表达式包含一个属性存取表达式(即一个.点表达式或[subscript]下标表达式),那么它被当作一个方法来调用。
var myObject= { //创建myObject。它有一个value属性和一个increment方法
value:0;
increment: function(inc){ //increment方法接收一个可选参数,如果参数不是数字那么默认使用数字1。
this.value += typeof inc=='number' ? inc : 1;
}
};
myObject.increment();
document.writeln(myObject.value); //1
myObject.increment(2);
document.writeln(myObject.value); //3
方法可以使用this去访问对象,所以它能从对象中取值或修改该对象。this到对象的绑定发生在调用的时候。这个延迟绑定(very late binding)使得函数可以对this高度复用。通过this可取得它们所属对象的上下文的方法称为公共方法。
(2)函数调用模式(The Function Invocation Pattern)
当一个函数并非一个对象的属性时,那么它被当作一个函数来调用:
var sum= add(3,4); //sum 的值为7
当函数以此模式被调用时,this被绑定到全局变量。这是语言设计上的一个错误。倘若语言设计正确,当内部函数被调用时,this应该仍然绑定到外部函数的this变量。这个设计错误的后果是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权。解决方案:如果该方法定义一个变量并给他赋值this,那么内部函数就可以通过那个变量访问到this。
myObject.double= function(){
var that= this; //按照约定,作者给这个变量命名为that
var helper= function(){
that.value= add(that.value, that.value);
};
helper(); //以函数的形式调用helper
};
myObject.double(); //以方法的形式调用double
document.writeln(myObject.getValue()); //6
(3)构造器调用模式(The Constructor Invocation Pattern)
JS是一门基于原型继承的语言。这意味着对象可以直接从其他对象继承属性。该语言是无类别的。<