如果实参过多,那么多余的实参会被忽略,如果实参过少,那么未被赋值的形参的值为 undefined。函数一定有一个返回值,如果没有通过 return 语句指定返回值,那么函数返回值为 undefined。
一个函数和其访问的外部变量组成一个闭包。这是 JavaScript 的关键魅力所在。
函数调用
每个函数被调用时,会接收到两个额外的参数:
1.this
2.arguments
this 的值和具体调用的模式有关,在 JavaScript 中有四种调用模式:
1.方法调用模式。对象的属性如果是函数,则称其为方法。如果一个方法通过 o.m(args) 被调用,this 为对象 o(由此可见,在调用时,this 和 o 才进行绑定),例如:
复制代码 代码如下:
var obj = {
value: 0,
increment: function(v) {
this.value += (typeof v === 'number' ? v : 1);
}
};
obj.increment(); // this === obj
2.函数调用模式。如果一个函数不是一个对象的属性,那么它将作为一个函数被调用,这时候 this 被绑定到全局对象上,例如:
复制代码 代码如下:
message = 'Hello World';
var p = function() {
console.log(this.message);
}
p(); // 输出 'Hello World'
这种行为有时候让人疑惑,看一个例子:
复制代码 代码如下:
obj = {
value: 0,
increment: function() {
var helper = function() {
// 对全局对象中的 value 加 1
this.value += 1;
}
// helper 被作为一个函数来调用
// 因此 this 为全局对象
helper();
}
};
obj.increment(); // obj.value === 0
我们期望的结果应该是:
复制代码 代码如下:
obj = {
value: 0,
increment: function() {
var that = this;
var helper = function() {
that.value += 1;
}
helper();
}
};
obj.increment(); // obj.value === 1
3.构造函数调用模式。意图使用 new 前缀的函数被叫做构造函数,例如:
复制代码 代码如下:
// Test 被叫做构造函数
var Test = function(string) {
this.message = string;
}
var myTest = new Test("Hello World");
一个函数前面可以加上 new 来调用(这样的函数通常大写开头),加上 new 之后将创建一个链接到此函数的 prototype 属性的对象,且构造函数中 this 为此对象。
4.apply 调用模式。函数的 apply 方法被用于调用函数,其有两个参数,第一个为 this,第二个为参数数组,例如:
复制代码 代码如下:
var add = function(a, b) {
return a + b;
}
var ret = add.apply(null, [3, 4]); // ret === 7
函数调用时,我们能够访问一个名为 arguments 的类数组(非真正的 JavaScript 数组),其包含了所有的实参,这样我们就能实现变长参数:
复制代码 代码如下:
var add = function() {
var sum = 0;
for (var i=0; i<arguments.length; ++i) {
sum += arguments[i];
}
return sum;
}
add(1, 2, 3, 4);
异常
现在来说说 JavaScript 的异常处理机制。我们使用 throw 语句来抛出异常,try-cache 语句来捕获并处理异常:
复制代码 代码如下: