JavaScript 函数与变量作用域详解
本文详细解析JavaScript中函数的定义方式、参数传递机制及返回值处理,重点讲解arguments与rest参数的使用。同时深入剖析变量作用域概念,包括函数作用域、全局作用域、变量提升现象,以及ES6引入的块级作用域关键字let和const,帮助开发者避免命名冲突并编写更规范的代码。
函数定义与调用基础
函数定义和调用
在JavaScript中,定义函数主要有以下几种方式。
function abs(x){
if(x >=0){
return x;
}else{
return -x;
}
}
以abs()函数为例,其定义结构如下:
function 关键字用于标识这是一个函数定义;
abs 是赋予该函数的名称;
(x) 括号内列出了函数的参数,若存在多个参数,则使用逗号进行分隔;
{...} 之间的代码块构成了函数体,内部可以包含多条语句,甚至可以为空。
需要注意的是,函数体内部的语句在执行过程中,一旦遇到 return 语句,函数执行即告结束,并将结果返回。因此,通过条件判断和循环语句,可以在函数内部实现复杂的逻辑处理。
如果函数中没有 return 语句,函数执行完毕后依然会返回结果,但此时返回值为 undefined。
由于JavaScript中的函数本质上也是一个对象,上述定义的 abs() 函数实际上是一个函数对象,而函数名 abs 可以被视为指向该函数的变量引用。
var abs = function(x){
if(x >= 0){
return x;
} else {
return -x;
}
}
在这种定义方式下,function (x) { ... } 是一个匿名函数,它没有具体的函数名。但是,这个匿名函数被赋值给了变量 abs,因此可以通过变量 abs 来调用该函数。
这两种定义方式在功能上完全等价。需要注意的是,第二种方式遵循完整的语法规范,需要在函数体末尾添加一个分号,以表示赋值语句的结束。
调用函数时,只需按顺序传入相应的参数即可:
abs(10); // 返回10 abs(-9); // 返回9
JavaScript允许传入任意数量的参数而不影响函数的调用。因此,即使传入的参数数量多于定义时的参数数量,也不会报错,尽管函数内部可能并不需要这些额外的参数。
abs(10,'blablabla'); //返回10 abs(-9,'haha','hehe',null) // 返回9
同样地,传入的参数数量少于定义时的参数数量也是允许的。
abs(); 返回NaN
此时,abs(x) 函数中的参数 x 将接收到 undefined 值,导致计算结果为 NaN。
function abs(x){
if(typeof x !=='number'){
throw 'Not a number':
}
if(x >=0){
return x;
}else{
return -x;
}
}
arguments对象与rest参数
arguments
JavaScript 提供了一个免费的关键字 arguments,它仅在函数内部生效,并且始终指向当前函数调用者传入的所有参数。
function foo(x){
alert(x); // 10
for(var i=0; i < arguments.length;++){
alert(arguments[i]); // 10,20,30
}
}
foo(10.20,30)
利用 arguments,你可以获取调用者传入的所有参数。这意味着,即使函数定义时没有声明任何参数,依然可以通过 arguments 获取到传入的值:
function abs(){
if(arguments.length ===0){
return 0;
}
var x = arguments[0]
return x >=0 ? x : -x;
}
abs(); //0
abs(10); // 10
abs(-9) //9
实际上,arguments 最常见的用途是判断传入参数的个数。你可能会看到如下形式的写法:
// foo(a[,b],c)
// 接受2~3 个参数,b 是可选参数,如果只传入两个参数,b默认为null
function foo(a,b,c){
if(arguments.length ===2){
// 实际拿到的参数是a 和b c 为undefined
c = b;
b = null; // b 变为默认值
要将中间的参数 b 变为“可选”参数,只能通过检查 arguments 来判断,然后重新调整参数并赋值。
rest 参数
由于JavaScript 函数允许接收任意数量的参数,有时我们不得不使用 arguments 来获取所有参数:
function foo(a,b){
var i, rest = [];
if(arguments.length > 2){
for(i = 2; i < arguments.length; i++){
rest.push(arguments[i]);
}
}
console.log('a =' + a);
console.log('b = ' + b);
console.log(rest);
}
为了获取除了已定义参数 a、b 之外的其他参数,我们不得不使用 arguments,并且循环需要从索引 2 开始以排除前两个参数。这种写法较为别扭,仅仅是为了获取额外的 rest 参数,是否有更好的方法?
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)。
return语句与自动分号机制
return 语句
前文提到,JavaScript引擎有一个在行末自动添加分号的机制,这可能导致你在 return 语句上遇到一个大坑:
function foo(){
return {name:'foo'};
}
foo(); // {name:'foo'}
需要注意:
function foo(){
return: //自动添加了分号,相当于return undefined
{name:'foo'}; // 这行语句已经没法执行到了。
}
因此,正确的多行写法应该是:
function foo(){
return { // 这里不会自动加分号,因为表示语句尚未结束。
name:'foo'
}
}
变量作用域与提升机制
变量作用域
在JavaScript中,使用 var 声明的变量实际上具有作用域限制。
如果一个变量在函数体内部声明,则该变量的作用域为整个函数体,在函数体外不应引用该变量。
‘use strict':
function foo(){
var x = 1;
x = x +1;
}
x = x +2; // RefrenceError 无法在函数体外引用该该变量x
如果两个不同的函数各自声明了同一个变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量互相独立,互不影响:
'use struct':
function foo(){
var x = 1;
x = x +1;
}
function bar (){
var x= 'A';
x = x + 'B';
}
由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
'use strict';
function foo(){
var x =1;
function bar(){
var x = 1;
function bar(){
var y= x +1; //bar 可以访问foo 的变量x
}
var z = y + 1; //RefernceError! foo 不可以访问bar 的变量y!
}
}
如果内部函数和外部函数的变量名重名怎么办?
'use strict':
function foo(){
var x = 1;
function bar (){
var x = 'A';
alert('x in bar() =' + x); // 'A'
}
alert('x in foo()=' +x) //1
bar();
}
变量提升
JavaScript的函数定义有一个特点,它会先扫描整个函数体的语句,把所有声明的变量“提升”到函数顶部:
'use strict';
function foo(){
var x='Hello,'+y;
alert(x);
var y = 'Bob';
}
foo();
对于上述 foo() 函数,JavaScript引擎看到的代码相当于:
function foo(){
var y; // 提升变量y的
var x = 'Hello' + y;
alert(x);
y = 'Bob';
}
由于JavaScript的这一特性,我们在函数内部定义变量时,请严格遵守“在函数内部首先声明所有变量”这一规则。最常见的做法是使用一个 var 声明函数内部用到的所有变量:
function foo(){
var x =1, // x 初始化为1
y = x +1, // y 初始化为2
z,i; // z和i 为undefined
// 其他语句
for(i =0; i<100; i++){
...
}
}
全局作用域与名字空间管理
全局作用域
不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript 默认有一个全局作用域的变量,它被绑定到 window 对象的一个属性上。
‘use strict';
var sourse = 'Learn JavaScript';
alert(course); // 'Learn JavaScript';
alert(window.course); // 'Learn JavaScript'
名字空间
全局变量会绑定到 window 上,不同的JavaScript文件如果使用相同的全局变量,或者定义了相同名字的顶层函数,都会造成
命名冲突,并且很难被发现,
减少冲突的一个方法是把自己的所有的变量和函数全部绑定到一个全局变量中。
// 唯一的曲剧变量MYAPP
var MYAPP = {};
//其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数
MYAPP.foo = function (){
return 'foo';
};
把自己的代码全部放入唯一的名字空间 MYAPP 中,会大大减少全局变量冲突的可能。
块级作用域与常量定义
局部作用域
由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的。
function foo(){
for(var i = 0; i<100; i++){
//
}
i+=100; // 仍然可以引用变量;
}
为了解决块级作用域问题,ES6引入了新的关键字 let,用 let 替代 var 可以声明一个块级作用域的变量:
function foo(){
var sum = 0;
for(let i=0; i<100;i++){
sum +=i;
}
i +=1;
}
常量
由于 var 和 let 声明的变量,如果要声明一个常量,在ES6之前是不行的,我们通常用全部大写的变量来表示这是一个常量
不要修改它的值。
var PI = 3.14; ES6标准引入了新的关键字 const 来定义常量,const 与 let都具有块级作用域; const PI = 3.14; PI = 3; // 某些浏览器不报错,但是无效果。 PI; // 3.14
以上这篇JavaScript基础函数_深入剖析变量和作用域就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
