文章内容是读“JavaScript高级程序设计”的笔记。
函数其实也是一个对象,每个函数都是Function类型的一个实例,与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名称实际上是一个指向函数的指针。
一、定义函数的几种方式:例如我们以返回两个数值的和为例。
- 函数声明:这种方式定义的函数的作用域是全局的。
function sum (num1, num2) {
return num1 + num2;
}
- 函数表达式:这种方式声明的函数与函数声明的区别主要是作用域的差别,用这种方式定义,在调用函数时,必须要在定义函数所在的作用域内,而且必须要在函数被声明之后才能调用,否则就会报错,而函数声明就可以在函数声明之前调用。
var sum = function(num1, num2){
return num1 + num2;
};
- 创建Function类型实例:这种声明方式可以很好的理解“函数即是对象,函数名即是指针”。这种声明方式效率不高,因为js引擎需要解析两次代码,一次是常规的解析,第二次是解析传入构造函数中的字符串。
var sum = new Function("num1", "num2", "return num1 + num2");
无论是哪一种定义函数的方式,在调用函数时,都是函数名后面加上括号
()
,如果有参数则在括号内加上参数。
二、函数的其他特点
- 没有重载方法:如果定义了两个同名函数,前面的函数会被后面的覆盖。
- 函数可以被当做一个变量来传递:函数本身也是一个变量,函数可以当做函数的参数,函数也可以当做函数的返回值。例如下面的例子:
function callSomeFunction(someFunction, someArgument){
return someFunction(someArgument);
}
function add10(num){
return num + 10;
}
var result1 = callSomeFunction(add10, 10);
alert(result1);//20
function getGreeting(name){
return "Hello, " + name;
}
var result2 = callSomeFunction(getGreeting, "Nicholas");
alert(result2);//"Hello, Nicholas"
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;
}
};
}
三、函数的属性
3.1 在函数内部,有两个特殊的对象: arguments
和this
.
- 第一个属性arguments 的主要用途是保存函数参数,它还有一个属性叫做
callee
,该属性是一个指针,指向拥有这个arguments对象的函数。这个属性在递归函数中可以很好的解开函数的执行与函数名之间耦合。例如这个阶乘函数:
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * factorial(num-1)
}
}
上面的例子中函数的执行与函数名 factorial 紧紧耦合在了一起。为了解决这个问题,可以将函数修改成下面这样。
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * arguments.callee(num-1)//将这一步原来使用函数名改为callee属性。
}
}
修改之后无论引用函数时使用的是什么名字,都可以保证正常完成递归调用。
var trueFactorial = factorial;
factorial = function(){
return 0;
};
alert(trueFactorial(5));//120
alert(factorial(5));//0
3.2 函数的另外两个属性length 和 prototype
length
属性表示函数希望接收的命名参数的个数。- prototype 是保存它们所有实例方法的真正所在。换句话说,诸如toString() 和 valueOf() 等方法实际上都保存在 prototype 名下,只不过是通过各自对象的实例访问罢了。
- prototype 属性是不可枚举的,因此使用 for-in 无法发现。
3.3 函数的两个方法apply()
和call()
这两个方法的用途都是在特定的作用域中调用函数,实际上等于
设置函数体内this对象的值
。
apply()
方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。第二个参数可以是 Array 的实例,也可以是arguments 对象。
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));//20
alert(callSum2(10,10));//20
call()
方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。call()方法第一个参数是 this 值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call() 方法时,传递给函数的参数必须逐个列举出来。例如:
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
alert(callSum(10,10));//20
3. apply()
和call()
正强大的地方是能够扩充函数赖以运行的作用域。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this);//red
sayColor.call(window);//red
sayColor.call(o);//blue
sayColor.call(o)
的意思是将sayColor函数在对象实例o的作用域中调用。
备注:本文内容是学习“JavaScript高级程序设计”这本书整理的笔记。