目录
在JavaScript中,函数和方法是执行特定任务的代码块,尽管它们在很多方面相似,但存在一些关键差异,主要体现在它们如何被定义和调用上。
一、函数的定义
在JavaScript中,函数可以通过多种方式定义,包括函数声明、函数表达式和箭头函数等。
1. 函数声明
函数声明使用function
关键字定义,具有函数名和参数列表,并以分号结束。
function add(a, b) {
return a + b;
}
console.log(add(3, 5)); // 输出:8
函数声明的特点是无论在何处调用,都会从头开始执行。
2. 函数表达式
函数表达式将函数赋值给一个变量,可以是匿名函数或命名函数。
const add = function (a, b) {
return a + b;
};
console.log(add(3, 5)); // 输出:8
函数表达式可以在任何地方定义和调用。
3. 箭头函数
箭头函数提供了更简洁的语法,减少了代码量。
const add = (a, b) => a + b;
console.log(add(3, 5)); // 输出:8
箭头函数不仅提供了更简洁的语法,还解决了传统函数中 this
指向问题,使代码更加清晰和易于维护。
二、函数的调用
1. 调用方式
调用函数很简单,只需使用函数名并传递必要的参数。
console.log(add(3, 3)); // 输出: 6
2. 参数数量的灵活性
JavaScript对函数的参数数量使用非常灵活。如果传递的参数过多,多余的部分会被忽略;如果传递的参数过少,缺失的参数将被赋值为undefined
。
function add(a, b) {
return a + b;
}
console.log(add(3, 3, 3)); // 输出:6
console.log(add(3)); // 输出:NaN
三、arguments 对象
1. 基本概念
arguments
对象是 JavaScript 中所有非箭头函数的局部变量,用于存储传递给函数的参数。它是一个类数组对象,具有 length
属性,索引从 0 开始,但没有 Array 的内置方法如 forEach()
和 map()
。arguments
对象可以通过 Array.prototype.slice.call(arguments)
或 Array.from(arguments)
转换为真正的 Array,但不支持 pop()
等方法。当调用的参数多于函数正式声明的参数时,arguments
对象可以用来处理可变数量的参数。
2. 属性
-
arguments.callee
:指向当前执行的函数。 -
arguments.length
:表示传递给函数的参数数量。 -
arguments[Symbol.iterator]
:返回一个数组迭代器对象,包含参数中每个索引的值。
3. 应用场景
arguments
对象常用于处理可变数量的参数、递归操作等场景。例如,在递归函数中,可以通过 arguments.callee
获取当前函数引用,从而实现递归计算。
以下是使用 arguments
对象的示例代码:
// 打印元素
function printArgs() {
for (let i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
printArgs(1, 'hello', true); // 输出:1 hello true
// 求和
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(sum(1, 2, 3)); // 输出:6
// 递归计算阶乘
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
console.log(factorial(5)) // 输出:120
4. 转换为真数组
虽然 arguments
对象不直接支持数组方法,但可以通过 Function.call
间接调用,将 arguments
转换为数组:
function convertToArray() {
return Array.prototype.slice.call(arguments);
}
console.log(convertToArray(1, 2, 3)); // 输出:[1, 2, 3]
5. 总结
arguments
对象是 JavaScript 中一个强大的工具,用于处理函数调用时的参数信息。尽管它不是真正的数组,但通过一些技巧可以将其转换为数组,并利用其特性来编写灵活的函数。然而,在现代 JavaScript 中,推荐使用剩余参数(rest parameters)来替代 arguments
对象,因为剩余参数提供了更好的可读性和功能。
四、Rest参数
1. 基本概念
JavaScript中的Rest参数(Rest Parameters)是ES6(ECMAScript 2015)引入的一个重要特性,用于处理函数中不定数量的参数。Rest参数允许开发者定义一个函数,使其能够接收任意数量的参数,并将这些参数收集到一个数组中。这种语法通过在参数列表末尾添加三个点(...
)来实现。例如:
function myFunction(a, b, ...args) {
console.log(a); // 输出第一个参数
console.log(b); // 输出第二个参数
console.log(args); // 输出剩余的所有参数,组成一个数组
}
myFunction(1, 2, 3, 4, 5); // 输出:1, 2, [3, 4, 5]
在本例中,...args
会收集所有未命名的额外参数,并将其存储为一个数组。
2. 特点
-
只能出现在参数列表的最后:Rest参数必须是最后一个参数,不能与其他命名参数混合使用。
function f(x, ...y, z) { } // 报错:Uncaught SyntaxError: Rest parameter must be last formal parameter.
-
返回值是一个数组:Rest参数总是返回一个数组,即使没有传递额外的参数,它也会返回一个空数组。
function f(x, ...y) {
console.log(y);
}
f(1); // 输出:[]
-
与
arguments
对象的区别:Rest参数是一个真正的数组,而arguments
对象是一个类数组对象,不支持数组方法如sort()
、map()
等。 -
只能使用一次:一个函数只能有一个Rest参数,且必须是最后一个参数。
3. 应用场景
Rest参数在处理不定数量的参数时非常有用,例如计算总和、合并数组、解构赋值等。
// 计算总和
function sum(...numbers) {
return numbers.reduce((acc, curr) => acc + curr, 0);
}
console.log(sum(1, 2, 3, 4)); // 输出:10
// 合并数组
function mergeArrays(...arrays) {
return arrays.reduce((acc, curr) => acc.concat(curr), []);
}
console.log(mergeArrays([1, 2], [3, 4], [5])); // 输出:[1, 2, 3, 4, 5]
// 解构赋值
const [a, b, ...rest] = [1, 2, 3, 4];
console.log(rest); // 输出:[3, 4]
4. 总结
Rest参数是JavaScript中处理不定数量参数的强大工具,它简化了函数定义和调用的复杂性。通过将多余的参数收集到一个数组中,开发者可以更方便地操作这些参数。Rest参数与扩展运算符的结合使用,进一步增强了其灵活性和实用性。
五、变量的作用域
JavaScript中的变量作用域分为全局作用域和局部作用域。
1. 全局作用域
全局作用域是指在所有函数之外定义的变量,其作用域范围是同一个页面文件中的所有脚本,可以在任何地方使用,直到页面关闭。
const globalVar = 'I am global!';
console.log(globalVar); // 输出:"I am global!"
2. 局部作用域
局部作用域是指在函数内部声明的变量,仅在该函数及其嵌套函数中有效,不能在函数外部访问。
function test() {
const localVar = 'I am local!';
console.log(localVar);
}
test(); // 输出:"I am local!"
console.log(localVar); // 报错:Uncaught ReferenceError: localVar is not defined.
3. 块级作用域
ES6 引入了 let
和 const
关键字,它们提供了块级作用域(如if
语句、for
循环等)。使用 let
或 const
声明的变量只在块级中可用。
if (true) {
let blockScopeVar = 1;
const blockScopeConst = 2;
}
console.log(blockScopeVar); // 报错:Uncaught ReferenceError: blockScopeVar is not defined.
console.log(blockScopeConst); // 报错:Uncaught ReferenceError: blockScopeConst is not defined.
4. let
与const
-
let
:声明一个块作用域的局部变量,可以重新赋值。
-
const
:声明一个块作用域的常量,一旦赋值就不能重新赋值(但如果是对象或数组,其内容仍然可以修改)。
const PI = 3.14;
PI = 3.14159; // 报错:Uncaught TypeError: Assignment to constant variable.
六、方法
JavaScript 方法(methods)是定义在对象中的函数,可以通过对象调用,用于执行特定任务或操作。这些方法可以是对象自带的,也可以是我们自定义的。
1. 方法定义与调用
const person = {
name: 'Tom',
// 定义方法
greet: function () {
console.log(`Hello, my name is ${this.name}!`);
}
};
// 调用方法
person.greet(); // 输出:"Hello, my name is Tom!"
2. apply
方法
apply
方法是 JavaScript 中 Function.prototype
的一个方法,允许我们在指定的上下文(即 this
值)中调用一个函数,并且可以传递一个参数数组作为函数的参数列表。apply
方法的第一个参数是 this
的值,第二个参数是一个数组或类数组对象,包含了将要传递给函数的参数。
function greet(greeting, name) {
console.log(greeting + ", " + name + "!");
}
const person = {name: "Tom"};
// 使用apply调用greet函数,设置this为person对象,并以数组形式提供参数。
greet.apply(person, ["Hello", person.name]); // 输出: Hello, Tom!
与apply
类似的call
方法允许我们调用一个函数,设置this
的值,并直接提供参数列表。
function greet(greeting, name) {
console.log(greeting + ", " + name + "!");
}
const person = {name: "Tom"};
greet.call(person, "Hi", person.name); // 输出: Hi, Tom!