JavaScript 不同维度对函数参数进行分类与使用技巧
·
0. 函数参数概述
函数参数是函数定义中用于函数调用时传递的值的变量。参数允许接收外部输入,使函数变得更加灵活。
以下是两个重要的概念:
- 形参:用于接收实际传递给函数的变量
- 实参:函数调用时传递给函数的具体值
1. 参数分类概述
JavaScript函数参数可以按照不同的维度进行分类:
按参数数量分类
- 无参数函数
- 固定参数函数
- 可变参数函数
按参数传递方式分类
- 按值传递(基本类型)
- 按引用传递(对象类型)
按参数定义方式分类
- 位置参数
- 默认参数
- 剩余参数
- 解构参数
2. 按参数数量分类
2.1 无参数函数
// 无参数函数
function sayHello() {
return "Hello, World!";
}
function getCurrentTime() {
return new Date().toLocaleString();
}
console.log(sayHello()); // "Hello, World!"
console.log(getCurrentTime()); // 当前时间字符串
2.2 固定参数函数
// 单个参数
function square(x) {
return x * x;
}
// 多个参数
function calculateArea(length, width) {
return length * width;
}
function createUser(name, age, email) {
return {
name: name,
age: age,
email: email,
createdAt: new Date()
};
}
console.log(square(5)); // 25
console.log(calculateArea(10, 5)); // 50
console.log(createUser("张三", 25, "zhang@example.com"));
2.3 可变参数函数
// 使用arguments对象(ES5方式)
function sum() {
var total = 0;
for (var i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
// 使用剩余参数(ES6方式)
function multiply(...numbers) {
return numbers.reduce((product, num) => product * num, 1);
}
// 混合参数 剩余参数要放在参数列表的最后
function formatString(template, ...values) {
return template.replace(/{(\d+)}/g, function(match, index) {
return values[index] || match;
});
}
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(multiply(2, 3, 4)); // 24
console.log(formatString("Hello {0}, you are {1} years old", "张三", 25));
3. 按参数传递方式分类
3.1 按值传递(基本类型)
// 基本类型参数按值传递
function modifyPrimitive(value) {
value = value * 2;
console.log("函数内部:", value); // 20
return value;
}
var num = 10;
console.log("修改前:", num); // 10
var result = modifyPrimitive(num);
console.log("修改后:", num); // 10 (未改变)
console.log("返回值:", result); // 20
3.2 按引用传递(对象类型)
// 对象类型参数按引用传递
function modifyObject(obj) {
obj.name = "修改后的名字";
obj.age = 30;
obj.newProperty = "新增属性";
console.log("函数内部:", obj);
// { name: "修改后的名字", age: 30, city: "北京", newProperty: "新增属性" }
}
function modifyArray(arr) {
arr.push(4);
arr[0] = 100;
console.log("函数内部数组:", arr); // [100, 2, 3, 4]
}
var person = {
name: "原始名字",
age: 25,
city: "北京"
};
var numbers = [1, 2, 3];
console.log("修改前person:", person);
modifyObject(person);
console.log("修改后person:", person); // 被修改了
console.log("修改前numbers:", numbers);
modifyArray(numbers);
console.log("修改后numbers:", numbers); // 被修改了
3.3 避免副作用的函数
// 不修改原始对象的函数
function createUserCopy(user, updates) {
// 创建新对象,不修改原始对象
return Object.assign({}, user, updates);
}
function filterArray(arr, condition) {
// 返回新数组,不修改原始数组
return arr.filter(condition);
}
var originalUser = { name: "张三", age: 25 };
var updatedUser = createUserCopy(originalUser, { age: 26 });
console.log("原始用户:", originalUser); // { name: "张三", age: 25 }
console.log("更新用户:", updatedUser); // { name: "张三", age: 26 }
var originalNumbers = [1, 2, 3, 4, 5];
var evenNumbers = filterArray(originalNumbers, (n) => n % 2 === 0);
var oddNumbers = filterArray(originalNumbers, (n) => n % 2 !== 0);
console.log("原始数组:", originalNumbers); // [1, 2, 3, 4, 5]
console.log("偶数数组:", evenNumbers); // [2, 4]
console.log("奇数数组:", oddNumbers); // [1, 3, 5]
4. 按参数定义方式分类
4.1 位置参数(Positional Parameters)
// 传统的位置参数
function createUser(name, age, email) {
return {
name: name,
age: age,
email: email
};
}
// 调用时必须按顺序传参
var user1 = createUser("张三", 25, "zhang@example.com");
var user2 = createUser("李四", 30, "li@example.com");
// 参数顺序错误会导致问题
var user3 = createUser(25, "王五", "wang@example.com"); // 错误的数据
console.log(user3); // { name: 25, age: "王五", email: "wang@example.com" }
4.2 默认参数(Default Parameters)
// ES6默认参数
function greet(name = "陌生人", greeting = "你好") {
return `${greeting},${name}!`;
}
function createConfig(host = "localhost", port = 3000, ssl = false) {
return {
host: host,
port: port,
ssl: ssl
};
}
// 建议把默认参数放在最后
function calculatePrice(basePrice, taxRate = 0.1, discount = 0) {
var taxedPrice = basePrice * (1 + taxRate);
return taxedPrice * (1 - discount);
}
console.log(greet()); // "你好,陌生人!"
console.log(greet("张三")); // "你好,张三!"
console.log(greet("李四", "欢迎")); // "欢迎,李四!"
console.log(createConfig()); // { host: "localhost", port: 3000, ssl: false }
console.log(createConfig("192.168.1.1")); // { host: "192.168.1.1", port: 3000, ssl: false }
console.log(calculatePrice.length); // 1 参数的长度等于第一个默认参数之前非默认参数的个数
console.log(calculatePrice(100)); // 110 (含10%税)
console.log(calculatePrice(100, 0.05, 0.1)); // 94.5 (含5%税,10%折扣)
4.3 剩余参数(Rest Parameters)
// 剩余参数收集多个参数
function sumAll(...numbers) {
return numbers.reduce((sum, num) => sum + num, 0);
}
function createTeam(leader, ...members) {
return {
leader: leader,
members: members,
totalMembers: members.length + 1
};
}
function logMessages(level, ...messages) {
messages.forEach(msg => {
console.log(`[${level}] ${msg}`);
});
}
function flexibleFunction(required, defaultParam = "default", ...rest) {
return {
required: required,
default: defaultParam,
rest: rest
};
}
console.log(sumAll(1, 2, 3, 4, 5)); // 15
console.log(createTeam("张三", "李四", "王五", "赵六"));
// { leader: "张三", members: ["李四", "王五", "赵六"], totalMembers: 4 }
logMessages("INFO", "系统启动", "用户登录", "数据加载完成");
console.log(flexibleFunction("必需参数", "自定义默认值", "额外1", "额外2"));
// { required: "必需参数", default: "自定义默认值", rest: ["额外1", "额外2"] }
4.4 解构参数(Destructuring Parameters)
// 对象解构参数
function createUser({ name, age, email, city = "北京" }) {
return {
name: name,
age: age,
email: email,
city: city,
createdAt: new Date()
};
}
// 数组解构参数
function processCoordinates([x, y, z = 0]) {
return {
x: x,
y: y,
z: z,
distance: Math.sqrt(x*x + y*y + z*z)
};
}
// 混合解构参数
function createProduct({ id, name, price }, [category, subcategory], options = {}) {
return {
id: id,
name: name,
price: price,
category: category,
subcategory: subcategory,
...options
};
}
// 嵌套解构参数
function processUser({
personal: { firstName, lastName },
contact: { email, phone = "未提供" },
preferences: { theme = "light", language = "zh-CN" }
}) {
return {
fullName: `${firstName} ${lastName}`,
contact: { email, phone },
preferences: { theme, language }
};
}
// 使用示例
var userConfig = {
name: "张三",
age: 25,
email: "zhang@example.com"
};
console.log(createUser(userConfig));
console.log(processCoordinates([3, 4])); // { x: 3, y: 4, z: 0, distance: 5 }
console.log(processCoordinates([1, 2, 2])); // { x: 1, y: 2, z: 2, distance: 3 }
var productData = {
id: 1,
name: "笔记本电脑",
price: 5999
};
var categoryInfo = ["电子产品", "电脑"];
var options = { warranty: "1年", color: "银色" };
console.log(createProduct(productData, categoryInfo, options));
var userData = {
personal: { firstName: "张", lastName: "三" },
contact: { email: "zhang@example.com" },
preferences: { theme: "dark" }
};
console.log(processUser(userData));
5. 参数验证和处理
5.1 参数类型检查
// 参数类型验证
function divide(dividend, divisor) {
// 类型检查
if (typeof dividend !== 'number' || typeof divisor !== 'number') {
throw new TypeError('参数必须是数字类型');
}
if (divisor === 0) {
throw new Error('除数不能为零');
}
return dividend / divisor;
}
function processArray(arr, callback) {
// 参数验证
if (!Array.isArray(arr)) {
throw new TypeError('第一个参数必须是数组');
}
if (typeof callback !== 'function') {
throw new TypeError('第二个参数必须是函数');
}
return arr.map(callback);
}
// 使用示例
try {
console.log(divide(10, 2)); // 5
console.log(divide(10, 0)); // 抛出错误
} catch (error) {
console.error(error.message);
}
try {
console.log(processArray([1, 2, 3], x => x * 2)); // [2, 4, 6]
console.log(processArray("not array", x => x * 2)); // 抛出错误
} catch (error) {
console.error(error.message);
}
5.2 参数范围验证
// 参数范围验证
function createPerson(name, age) {
// 非空验证
if (!name || typeof name !== 'string') {
throw new Error('姓名必须是非空字符串');
}
// 范围验证
if (typeof age !== 'number' || age < 0 || age > 150) {
throw new Error('年龄必须是0-150之间的数字');
}
return {
name: name.trim(),
age: Math.floor(age)
};
}
function calculateDiscount(price, discountPercent) {
// 价格验证
if (typeof price !== 'number' || price <= 0) {
throw new Error('价格必须是正数');
}
// 折扣验证
if (typeof discountPercent !== 'number' || discountPercent < 0 || discountPercent > 100) {
throw new Error('折扣百分比必须是0-100之间的数字');
}
return price * (discountPercent / 100);
}
// 使用示例
try {
console.log(createPerson(" 张三 ", 25)); // { name: "张三", age: 25 }
console.log(createPerson("", 25)); // 抛出错误
console.log(createPerson("李四", -5)); // 抛出错误
} catch (error) {
console.error(error.message);
}
try {
console.log(calculateDiscount(100, 20)); // 20
console.log(calculateDiscount(100, 150)); // 抛出错误
} catch (error) {
console.error(error.message);
}
6. 高级参数模式
6.1 参数对象模式
// 参数对象模式 - 处理大量可选参数
function createHttpRequest({
url,
method = 'GET',
headers = {},
body = null,
timeout = 5000,
retries = 3,
cache = true
} = {}) {
// 必需参数验证
if (!url) {
throw new Error('URL是必需参数');
}
return {
url: url,
method: method.toUpperCase(),
headers: headers,
body: body,
timeout: timeout,
retries: retries,
cache: cache
};
}
// 链式调用模式
function createQueryBuilder(table) {
var query = {
table: table,
conditions: [],
sorts: [],
limit: null
};
return {
where: function(condition) {
query.conditions.push(condition);
return this;
},
orderBy: function(field, direction = 'ASC') {
query.sorts.push({ field, direction });
return this;
},
limit: function(count) {
query.limit = count;
return this;
},
build: function() {
return query;
}
};
}
// 使用示例
var request = createHttpRequest({
url: 'https://api.example.com/users',
method: 'POST',
headers: { 'Content-Type': 'application/json' },
timeout: 10000
});
console.log(request);
var query = createQueryBuilder('users')
.where('age > 18')
.where('status = "active"')
.orderBy('created_at', 'DESC')
.limit(10)
.build();
console.log(query);
6.2 可变参数处理模式
// 处理不同类型参数的函数
function addEventListeners(element, ...args) {
// 支持多种调用方式
if (args.length === 2 && typeof args[0] === 'string') {
// addEventListeners(element, 'click', handler)
element.addEventListener(args[0], args[1]);
} else if (args.length === 1 && typeof args[0] === 'object') {
// addEventListeners(element, { click: handler, mouseover: handler })
var events = args[0];
Object.keys(events).forEach(eventType => {
element.addEventListener(eventType, events[eventType]);
});
} else {
throw new Error('参数格式不正确');
}
}
// 混合参数类型处理
function formatData(...args) {
var result = [];
args.forEach(arg => {
switch (typeof arg) {
case 'string':
result.push(`字符串: ${arg}`);
break;
case 'number':
result.push(`数字: ${arg}`);
break;
case 'boolean':
result.push(`布尔值: ${arg}`);
break;
case 'object':
if (Array.isArray(arg)) {
result.push(`数组: [${arg.join(', ')}]`);
} else if (arg === null) {
result.push('空值: null');
} else {
result.push(`对象: ${JSON.stringify(arg)}`);
}
break;
default:
result.push(`未知类型: ${arg}`);
}
});
return result;
}
// 使用示例
console.log(formatData("hello", 42, true, [1, 2, 3], { name: "张三" }, null));
// [
// "字符串: hello",
// "数字: 42",
// "布尔值: true",
// "数组: [1, 2, 3]",
// "对象: {"name":"张三"}",
// "空值: null"
// ]
7. 参数最佳实践
7.1 参数设计原则
// 1. 优先使用默认参数而不是在函数内部处理
// 不好的做法
function badExample(name, options) {
options = options || {};
var timeout = options.timeout || 5000;
var retries = options.retries || 3;
// ...
}
// 好的做法
function goodExample(name, { timeout = 5000, retries = 3 } = {}) {
// ...
}
// 2. 使用参数验证
function validateAndProcess(data, {
required = true,
maxLength = 1000,
format = 'json'
} = {}) {
// 参数验证
if (required && (data === null || data === undefined)) {
throw new Error('数据是必需的');
}
if (typeof data === 'string' && data.length > maxLength) {
throw new Error(`数据长度不能超过${maxLength}个字符`);
}
// 处理逻辑
switch (format) {
case 'json':
return JSON.stringify(data);
case 'xml':
// XML处理逻辑
return `<data>${data}</data>`;
default:
return String(data);
}
}
// 3. 合理使用剩余参数
function logger(level, message, ...details) {
var timestamp = new Date().toISOString();
var logEntry = `[${timestamp}] ${level}: ${message}`;
if (details.length > 0) {
logEntry += ` (详情: ${details.join(', ')})`;
}
console.log(logEntry);
}
// 使用示例
logger('INFO', '用户登录', '用户ID: 123', 'IP: 192.168.1.1');
logger('ERROR', '数据库连接失败', '主机: localhost', '端口: 3306', '错误: 连接超时');
7.2 性能考虑
// 1. 避免在函数内部创建大对象
// 不好的做法
function badPerformance(data, options) {
// 每次调用都创建大对象
var defaultOptions = {
cache: new Map(), // 大对象
processors: Array(1000).fill().map(() => function(){}), // 大数组
config: { /* 复杂配置 */ }
};
var finalOptions = Object.assign(defaultOptions, options);
// ...
}
// 好的做法
function goodPerformance(data, options = {}) {
// 只创建必要的对象
var finalOptions = {
cache: options.cache || new Map(),
processors: options.processors || [],
config: options.config || {}
};
// ...
}
// 2. 参数传递优化
// 对于大对象,考虑传递引用而不是复制
function processLargeDataset(dataset, {
chunkSize = 1000,
callback = null
} = {}) {
// 不复制整个数据集,而是传递引用
for (let i = 0; i < dataset.length; i += chunkSize) {
var chunk = dataset.slice(i, i + chunkSize);
if (callback) callback(chunk);
}
}
这些参数分类和示例涵盖了JavaScript函数参数的主要使用场景,从基本的位置参数到高级的解构参数,以及参数验证和最佳实践,为编写健壮、灵活的JavaScript函数提供了全面的指导。
更多推荐


所有评论(0)