Function
函数实际上是对象,每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数实际对象,因为函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。函数通常是使用函数声明语法定义的。
1 2 3 4 5 6 7 8 9 10
| function sum(num1, num2){ return num1 + num2; }
var sum = function(num1, num2){ return num1 + num2; }
var sum = new Function('num1','num2','return num1 + num2');
|
由Function构造函数可以直观看出:函数是对象,函数名是指针。
没有重载
在javascript中,如果声明了两个同名函数,后面的函数会覆盖前面的函数。
tips:
重载,简单说,就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。
函数声明与函数表达式
解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);函数表达式,则必须等到解析器执行到它所在的代码行,才会被真正解释执行。
1 2 3 4
| alert(sum(10,10)); function sum(num1, num2){ return sum1 + sum2; }
|
以上代码可正常执行,代码开始前,解析器已经通过一个函数声明提升的过程,读取并将函数声明添加到执行环境。对代码求值时,Javascript引擎在第一遍会声明函数并将它们放到源代码树的顶部。所以即使声明函数的代码在调用它的代码后面,Javascrit引擎也能把函数声明提升到顶部。
下面的代码将函数声明改为函数表达式,执行期间就会导致错误。
1 2 3 4
| alert(sum(10,10)); var sum = function(num1, num2){ return sum1 + sum2; }
|
上述代码产生错误的原因在于函数位于一个初始化语句中,而不是一个函数声明。在执行到函数所在语句之前,变量sum中不会保存有对函数的引用。而且由于第一行代码就会导致”unexpected identifier”错误,实际上也不会去执行到下一行。
除了通过变量访问函数的时间不同这一点除外,函数声明与函数表达式的语法其实是等价的。
作为值的函数
ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。不仅可以像传递参数一样把一个函数传递给另一个函数,而且也可以将一个函数作为另一个函数的结果返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function callSomeFun(somFun, somArgs){ return somFun(somArgs); }
function add10(num){ return num + 10; } var result1 = callSomeFunction(add10, 10); alert(result1); function getGreeting(name){ return "Hello, " + name; } var result2 = callSomeFunction(getGreeting, "Nicholas"); alert(result2);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function createComparisonFunction(propertyName) { return function(object1, object2){ var value1 = object1[propertyName]; var value2 = object2[propertyName]; if (value1 < value2){ return -1; }else if (value1 > value2){ return 1; }else{ return 0; } }; } var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}]; data.sort(createComparisonFunction("name")); alert(data[0].name); data.sort(createComparisonFunction("age")); alert(data[0].name);
|
函数内部属性
函数内部有两个特殊对象:arguments和this。argements的主要用途是保存函数参数,此外这个对象还有一个callee属性。该属性是一个指针,指向拥有这个arguments对象的函数。
callee经典实现–阶乘函数:
1 2 3 4 5 6 7
| function factorial(num){ if(num < 1){ return 1; }else{ return num * factorial(num -1); } }
|
定义阶乘函数一般用到递归算法。上面的代码所示,在函数有名字,且名字不会变的情况下,没有问题。但这个函数的执行与函数名factorial紧紧耦合了。为了消除这种耦合,使用arguments.callee。
1 2 3 4 5 6 7
| functino factorial(num){ if(num < 1){ return 1; }else{ return num * arguments.callee(num - 1); } }
|
函数内部特殊对象:this。this引用的是函数据以执行的环境对象-或者可以说是this值(当网页在全局作用域中调用函数时,this对象的引用就是window)。
1 2 3 4 5 6 7 8
| window.color = 'red'; var o = { color: 'blue'}; function sayColor(){ alert(this.color); } sayColor(); o.sayColor = sayColor; o.sayColor();
|
上面函数sayColor()是在全局作用域中定义的,它引用了this对象。由于在调用函数之前,this的值并不确定,因此this可能会在代码执行过程中引用不同的对象。当在全局作用中调用sayColor()时,this引用的是全局对象window。当把函数赋给对象o并调用o.sayColor()时,this引用的对象是o,因此对this.color求值会转换成对o.color求值,结果返回blue。
ECMAScript5规范化了另一个函数属性 caller。这个属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值是null。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| function outer(){ inner(); } function inner(){ alert(inner.caller); } outer();
function inner(){ alert(arguments.callee.caller); }
>当函数在严格模式下运行时,访问arguments.callee会导致错误。ECMAScript5还定义了arguments.caller属性。但在严格模式下,访问也会导致错误,在非严格模式下,这个属性始终是undefined。定义这个属性是为了区分arguments.caller和函数caller属性。 严格模式还有一个限制,不能为函数的caller属性赋值,否则会导致错误。
### 函数的属性和方法 ECMAScript中函数也是对象,因此函数也具有属性和方法。每个函数都包含两个属性: length和prototype。其中,length表示函数希望接收的命名参数个数。 对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。换言之,诸如toString()/valueOf()等方法实际上都保存在prototype名下。只不过是通过各自对象的实例访问。在创建自定义引用类型以及实现继承时,prototype属性的作用极为重要。**在ECMAScript中,prototype属性是不可枚举的,因此使用for-in无法发现。 >每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
首先,apply接受两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中第二个参数可以是Array实例,也可以是arguments对象。
```javascript function sum(num1, num2){ return num1 + num2; } function callSum1(num1, num2){ return sum.apply(this, arguments);//传入arguments对象 } function callSum2(num1, num2){ return sum.apply(this, [num1, num2]) } alert(callSum1(10,10)); alert(callSum2(10,10));
|
call()方法与apply()方法的作用相同,它们的区别在于仅在于接收参数的方式不同。对于call方法而言,第一个参数是this值没有变化,变化的
是其余参数都直接传递给函数。
1 2 3 4 5 6
| function sum(num1, num2){ return num1 + num2; } function sum1(num1, num2){ return sum.apply(this, num1, num2); }
|
apply()和call()最强大的地方在于能够扩充函数赖以运行的作用域。
1 2 3 4 5 6 7 8 9
| window.color = 'red'; var o = {color: 'blue'}; function sayColor(){ alert(this.color); }
sayColor.call(this); sayColor.call(window); sayColor.call(o);
|
ECMAScript5还定义了一个方法: bind()。该方法会创建一个函数实例,其this值会绑定到传给bind函数的值。
1 2 3 4 5 6 7
| window.color = 'red'; var o = {color: 'blue'}; function sayColor(){ alert(this.color); } var objectSayColor = sayColor.bind(o); objectSayColor();
|
sayColor()调用bind()并传入对象o,创建了objectSayColor()函数。objectSayColor()函数的this值等于o。因此即使在全局作用于中调用这个函数,也是输出blue。
每个函数继承toLocaleString()和toString()方法始终返回函数的代码。另外继承的valueOf()方法同样也只返回函数代码。