函数
一、函数的定义
只定义一次,但可以被执行或调用任意次。JavaScript中函数是参数化的:函数的定义包括一个称为形参的标示符列表,这些参数在函数体中像局部变量一样工作。如果函数挂在在一个对象上,作为对象的一个属性,就称它为对象的一个方法。当通过这个对象来调用函数时,该对象就是此次调用的上下文,也就是该函数的this的值。用于初始化一个新创建的对象的函数称为构造函数。
二、函数的三种定义方式
1、函数声明:
函数声明语句实际上声明了一个变量,并把一个函数对象复制给它。
function sum (num1,num2) { return num1 + num2 ; }
2、函数表达式
使用函数表达式可以无须对函数命名,从而实现动态编程。匿名函数,也成为拉姆达函数,是一种使用JavaScript函数的强大方式 。
特点:
(1)函数表达式不同于函数声明。函数声明要求有名字,但函数表达式不需要。没有名字的表达式也叫做匿名函数
(2)在无法确定如何引用函数的情况下,递归函数就会变得比较复杂
(3)递归函数始终调用arguments.callee来递归地调用自身,不要使用函数名-函数名可随时发生变化
var sum = function(num1,num2) return num1 + num2 ; };
如果一个函数声明表达式包含名称,函数的局部作用域将会包含一个绑定到函数对象的名称。这时函数的名称将成为函数内部的一个局部变量:如:递归调用
var f = function fact(x) { if (x<=1) return 1; else return x*fact(x-1); }
函数表达式特别适合定义那些只用一次的函数:
var tensquared = (function(x) { return x*x; }(10));
两种调用方式的区别:
尽管函数声明语句和函数定义表达式包含相同的函数名。但是两者仍然不同。。两种方式都创建新的函数对象,但函数声明语句中国的函数名是一个变量名,变量指向函数对象。和通过var声明变量一样,函数定义语句被显示地“提前”到了脚本或函数体的顶部。因此它们呢在真个脚本和函数体内都是可见的。使用var的话,只有变量声明提前了—变量初始化代码仍然留在原来的位置。然而使用函数声明语句的话,函数名称和函数体都提前。也就是说,可以声明一个JavaScript函数之前调用它。
3、构造函数
不管通过函数定义语句还是函数直接量表达式,函数的定义都要使用function关键字,单函数还可以使用Function()构造函数来定义。Function()构造函数可以传入任意数量的字符串实参,最后一个实参所表示的文本就是函数体;它可以包含任意的JavaScirpt语句,每两条语句之间用分号分开。
注意: Function()构造函数并不需要传入实参以指定函数名。就像函数直接量一样,Function()构造函数创建一个匿名函数。
var f = new Function("x" ,"y", "return x*y;" ); var f = funciton(x,y) { return x*y; }
三、构造函数和普通函数
构造函数与其他函数的唯一区别,就在于调用它们的方式不同。不过,构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过new操作符来调用,那它就可以当做构造函数;任何函数,只要不通过new操作符来调用,那它和普通函数没有任何区别。
function Person(name,age,job) { this.age = age; this.name = name; this.job = job; }
1、普通函数
Person("Greg", 27 ,"Doctor"); //添加到windows
2、构造函数
var person = new Person("Nicholas", 29, "Software Engineer");
new操作符具体做了什么事情:
(1)创建一个新的对象
(2)将构造函数的作用域赋给新对象(因此this就指向这个新对象);
(3)执行构造函数的代码(为这个新对象添加属性);
(4)返回新对象
四、return语句
return语句导致函数停止执行,并返回它的表达式给调用者,如果没有与之相关的表达式,则返回undefined
五、函数特性
特性:声明提前
Javascript函数里的声明的所有变量都被"提前"至函数体的顶部
六、函数的调用
1、作为函数
printprop({x:1});
2、作为方法
作为方法调用和函数调用有一个重要的区别,即(调用上下文this),函数体里可以用this引用该对象。
注意:
方法和this关键字是面向对象编程范例的核心。任何函数只要作为方法调用实际上都会传入一个隐式的实参(this)—这个实参是一个对象。需要注意的是,this是一个关键字,不是变量,也不是属性名。关键字this没有作用域限制,嵌套的函数不会从调用它的函数中继承this。如果嵌套函数想要访问外部函数的this,需要将this的值保存在一个变量里,这个变量和内部函数都同在一个作用域内。通常用变量self来保存this .
3、作为构造函数
构造函数可以使用this关键字来引用这个新创建的对象
4、通过他们的call()和apply()方法调用
七、闭包
1、定义:闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的方式,就是在一个函数内部创建另一个函数。函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包。”从技术角度讲, 所有
JavaScript函数都是闭包。
2、原理:在后台环境中,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域。通常,函数的作用域及其所有变量都会在函数执行结束被销毁。但是,当函数返回一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止。
3、应用:在匿名函数和闭包一文中有写到
4、缺点
(1)于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。
(2)同一个作用域链中定义的多个闭包,共享同样的私有变量或变量。当书写循环代码时,往往会出错。由于作域链的机制导致一个问题,在循环中里的匿名函数取得的任何变量都是最后一个值
八、常用的属性和方法
1、属性
length: 函数形参的长度
arguments.length: 函数实参的长度
prototype:这个属性是指向一个对象的引用 ,这个对象称为"原型对象"。每一个函数都包含不同的原型对象。当将函数用作构造函数的时候,新创建的对象将会从原型对象上继承属性。
2、方法
call()
apply()
bind()
toString()
九、自定义函数属性
JavaScript中的函数并不是原始值,而是一种特殊对象。也就是说,函数可以拥有属性。
当函数需要一个"静态"变量来保持某个值不变,最方便的方式就是给函数定义属性,而不是定义一个全局变量。比如,假设你想写一个返回一个唯一整数的函数,不管到哪里调用函数都会返回这个整数,而且这些值得信息需要在不同的函数调用过程持久化。可以将这些信息放到全局变量中,但这并不是必须的,因为这个信息仅仅是函数本身用到。最好将这个信息保存到函数对象的一个属性中。
十、函数式编程
JavaScript并非函数式编程语言,但在JavaScript中可以像操控对象一样操控函数,也就是说可以在JavaScript中应用函数式编程技术。
例如:求数字的平均值和标准差
var sum = function(x,y) { return x+y; }; var square = function(x) { return x*x; }; var data = [1,1,3,5,5]; var mean = data.map(function(x) { return x-mean; }); var deviations = data.map(function(x) {return x-mean;}); var stddev = Math.sqrt(deviations.map(square).reduce(sum)/(data.length-1));