参数及返回值
形参和实参
// 函数内部是一个封闭的环境,可以通过参数的方式,把外部的值传递给函数内部
// 带参数的函数声明
function 函数名(形参1, 形参2, 形参...){
// 函数体
}
// 带参数的函数调用
函数名(实参1, 实参2, 实参3);
解释:
- 形式参数:在声明一个函数的时候,为了函数的功能更加灵活,有些值是固定不了的,对于这些固定不了的值。我们可以给函数设置参数。这个参数没有具体的值,仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。
- 实际参数:如果函数在声明时,设置了形参,那么在函数调用的时候就需要传入对应的参数,我们把传入的参数叫做实际参数,也叫实参。
function fn(a, b) {
console.log(a + b);
}
var x = 5, y = 6;
fn(x,y);
// x,y 实参,有具体的值。函数执行的时候会把x,y复制一份给函数内部的a和b。
// 函数内部的值是复制的新值,无法修改外部的x,y
var f = function (one) {
console.log(one);
}
f(1, 2, 3)
由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。
arguments对象 — 函数的实参参数集合
var f = function (one) {
console.log(arguments);
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
f(1, 2, 3)
rest参数
由于JavaScript函数允许接收任意个参数,于是我们就不得不用arguments
来获取所有参数:
// 只获取出a,b意外的参数
function foo(a, b) {
var i, datas = [];
if (arguments.length > 2) {
for (i = 2; i<arguments.length; i++) {
datas.push(arguments[i]);
}
}
console.log('a = ' + a);
console.log('b = ' + b);
console.log(datas);
}
foo(1,2,3,4,5);
为了获取 除了 已定义参数a
、b
之外的参数,我们不得不循环arguments
,并且循环要从索引2
开始以便排除前两个参数,这种写法很别扭,只是为了获得额外的datas
参数,有没有更好的方法?
ES6标准引入了rest参数,上面的函数可以改写为:
function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}
foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]
foo(1);
// 结果:
// a = 1
// b = undefined
// Array []
rest参数只能写在最后,前面用...
标识,从运行结果可知,传入的参数先绑定a
、b
,多余的参数以数组形式交给变量rest
,所以,不再需要arguments
我们就获取了全部参数。
如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined
)。
注意因为rest参数是ES6新标准,所以,请使用新型浏览器;
返回值
当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西。也就是返回值。函数通过return返回一个返回值
//声明一个带返回值的函数
function 函数名(形参1, 形参2, 形参...){
//函数体
return 返回值;
}
//可以通过变量来接收这个返回值
var 变量 = 函数名(实参1, 实参2, 实参3);
函数的调用结果就是返回值,因此我们可以直接对函数调用结果进行操作。
返回值详解:
如果函数没有显示的使用 return语句 ,那么这个函数就没有任何返回值,仅仅是执行了而已;
如果函数使用 return语句,那么跟在return后面的值,就成了函数的返回值;
如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined ,
函数使用 return 语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说 return 后面的所有其他代码都不会再执行。
- return 后没有任何内容,可以当做调试来使用;
递归
执行代码,查看执行结果:
function fn1 () {
console.log(111);
fn2();
console.log('fn1');
}
function fn2 () {
console.log(222);
fn3();
console.log('fn2');
}
function fn3 () {
console.log(333);
fn4();
console.log('fn3');
}
function fn4 () {
console.log(444);
console.log('fn4');
}
fn1()
/*
** 执行结果为:
111
222
333
444
fn4
fn3
fn2
fn1
*/
最简单的一句话介绍递归:函数内部自己调用自己
递归案例:
计算 1+2+3+……+100 的结果
function sum(n){
if(n>1){
return n + sum(n-1);
}else{
return 1;
}
}
var jieguo = sum(5);
console.log(jieguo);
递归必须要有判断条件,不加判断会死循环;
作用域
作用域(scope)指的是变量存在的范围。在 ES5 的规范中,Javascript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域(局部作用域),变量只在函数内部。
函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。
var v = 1;
function f() {
console.log(v);
}
f();
// 1
在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)。
function f(){
var v = 1;
}
v // ReferenceError: v is not defined
注意,对于var
命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。
作用域链
只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。
将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。
function f1() {
var num = 123;
function f2() {
console.log( num );
}
f2();
}
var num = 456;
f1();
函数内部的变量声明的提升
与全局作用域一样,函数作用域内部也会产生“变量提升”现象。
var
命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
function foo() {
console.log(y);//undefined
var y = 'Bob';
}
foo();
注意: JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。
对于上述foo()函数,JavaScript引擎看到的代码相当于:
function foo() {
var y; // 提升变量y的申明
var x = 'Hello, ' + y;
alert(x);
y = 'Bob';
}
函数本身的作用域
var a = 1;
var x = function () {
console.log(a);
};
function f() {
var a = 2;
x();
}
f(); // 1
// 讨论结果为什么是 1 ?
函数本身也是一个值,也有自己的作用域。
它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
总之,函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。
var x = function () {
console.log(a);
};
function y(f) {
var a = 2;
f();
}
y(x);// ReferenceError: a is not defined
function foo() {
var x = 1;
function bar() {
console.log(x);
}
return bar;
}
var x = 2;
var f = foo();
f(); // 1