Function definition specification
Function Naming Conventions
Function names should clearly express their purpose, using camelCase. Avoid single letters or vague terms, and prefer names that start with verbs. For example:
// Good naming
function calculateTotalPrice() {}
function getUserInfo() {}
// Poor naming
function calc() {} // Too brief
function data() {} // Unclear noun
function process() {} // Too generic verb
For functions that return boolean values, use prefixes like is
, has
, or can
:
function isValidUser() {}
function hasPermission() {}
function canEditContent() {}
Parameter Design Principles
Limit the number of function parameters to 3 or fewer. For more than 3, consider using an object parameter:
// Not recommended
function createUser(name, age, gender, address, phone) {}
// Recommended
function createUser({ name, age, gender, address, phone }) {}
Set default values for parameters to avoid existence checks inside the function:
// Not recommended
function connect(host, port) {
host = host || 'localhost';
port = port || 8080;
}
// Recommended
function connect(host = 'localhost', port = 8080) {}
Function Length Control
A single function should ideally not exceed 20 lines (excluding blank lines and comments). Long functions should be split into smaller ones:
// Not recommended
function processOrder(order) {
// Validation logic...15 lines
// Calculation logic...20 lines
// Database operations...15 lines
// Notification logic...10 lines
}
// Recommended
function processOrder(order) {
validateOrder(order);
const total = calculateTotal(order);
saveOrder(order, total);
notifyUser(order);
}
Single Responsibility Principle
Each function should do only one thing and avoid side effects:
// Not recommended: Modifies data and sends notifications
function updateUserProfile(user) {
user.updatedAt = new Date();
database.save(user);
emailService.sendUpdateNotification(user.email);
}
// Recommended: Split responsibilities
function updateUserProfile(user) {
markAsUpdated(user);
persistUser(user);
}
function markAsUpdated(user) {
user.updatedAt = new Date();
}
function persistUser(user) {
database.save(user);
notifyProfileUpdate(user);
}
Return Value Consistency
Functions should maintain consistent return types, avoiding cases where they sometimes return an object and sometimes return null:
// Not recommended
function findUser(id) {
const user = database.query(id);
return user || null; // Sometimes User object, sometimes null
}
// Recommended: Always return a User object or throw an exception
function findUser(id) {
const user = database.query(id);
if (!user) throw new Error('User not found');
return user;
}
For operations that may fail, consider returning an object with a status:
function divide(a, b) {
if (b === 0) {
return { success: false, error: 'Cannot divide by zero' };
}
return { success: true, value: a / b };
}
Error Handling Standards
Error handling should be explicit, avoiding silent failures:
// Not recommended
function parseJSON(json) {
try {
return JSON.parse(json);
} catch {
return null;
}
}
// Recommended: Explicitly throw errors
function parseJSON(json) {
try {
return JSON.parse(json);
} catch (error) {
throw new Error(`Invalid JSON: ${error.message}`);
}
}
For expected errors (e.g., validation failures), return error objects instead of throwing exceptions:
function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!regex.test(email)) {
return { valid: false, reason: 'Invalid email format' };
}
return { valid: true };
}
Pure Functions Preferred
Write pure functions whenever possible, where the same input always produces the same output:
// Impure: Depends on external state
let discount = 0.1;
function applyDiscount(price) {
return price * (1 - discount);
}
// Pure function
function applyDiscount(price, discount) {
return price * (1 - discount);
}
Higher-Order Function Usage
Use higher-order functions to improve code reusability:
// Create function factories
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
// Use function composition
function pipe(...fns) {
return function(initialValue) {
return fns.reduce((val, fn) => fn(val), initialValue);
};
}
const processValue = pipe(
x => x * 2,
x => x + 3,
x => x / 2
);
Arrow Function Use Cases
Arrow functions are suitable for short callbacks, while regular functions are better for scenarios requiring this
binding:
// Suitable for arrow functions
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);
// Scenarios requiring `this` binding
const calculator = {
value: 1,
add: function(amount) {
this.value += amount;
return this;
}
};
Commenting Standards
Add JSDoc comments for complex functions:
/**
* Calculate the distance between two points
* @param {Object} point1 - First point
* @param {number} point1.x - X coordinate
* @param {number} point1.y - Y coordinate
* @param {Object} point2 - Second point
* @param {number} point2.x - X coordinate
* @param {number} point2.y - Y coordinate
* @returns {number} Straight-line distance between the points
*/
function calculateDistance(point1, point2) {
const dx = point1.x - point2.x;
const dy = point1.y - point2.y;
return Math.sqrt(dx * dx + dy * dy);
}
Performance Considerations
Avoid creating unnecessary functions in hot paths:
// Not recommended: Creates a new function on every render
function Component() {
const handleClick = () => {
console.log('Clicked');
};
return <button onClick={handleClick}>Click</button>;
}
// Recommended: Cache with useCallback
function Component() {
const handleClick = useCallback(() => {
console.log('Clicked');
}, []);
return <button onClick={handleClick}>Click</button>;
}
For frequently called simple functions, consider performance optimizations:
// Simple operations can be inlined
array.forEach(item => processItem(item));
// Complex operations should be extracted as separate functions
function processItem(item) {
// Complex processing logic
}
array.forEach(processItem);
Asynchronous Function Standards
Asynchronous functions should be explicitly marked with async
and handle errors properly:
// Not recommended: Ignores error handling
async function fetchData() {
const response = await fetch('/api/data');
return response.json();
}
// Recommended: Complete error handling
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Failed to fetch data:', error);
throw error; // Or return a default value
}
}
Use Promise.all
to optimize multiple asynchronous operations:
async function fetchMultipleResources() {
const [user, posts] = await Promise.all([
fetch('/api/user'),
fetch('/api/posts')
]);
return {
user: await user.json(),
posts: await posts.json()
};
}
Functional Programming Practices
Use functional programming concepts appropriately:
// Use currying
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
};
}
};
}
const add = curry((a, b) => a + b);
const add5 = add(5);
console.log(add5(3)); // 8
Type Safety Considerations
Define function types explicitly in TypeScript:
// Explicitly define parameter and return types
function formatName(firstName: string, lastName: string): string {
return `${lastName}, ${firstName}`;
}
// Use interfaces for complex parameters
interface User {
id: number;
name: string;
}
function getUserInfo(user: User): string {
return `ID: ${user.id}, Name: ${user.name}`;
}
Test-Friendly Design
Write functions that are easy to test:
// Not recommended: Directly depends on external services
async function getWeather() {
const response = await fetch('https://api.weather.com');
return response.json();
}
// Recommended: Inject dependencies
async function getWeather(fetchService = fetch) {
const response = await fetchService('https://api.weather.com');
return response.json();
}
// Can inject mocks during testing
test('getWeather', async () => {
const mockFetch = jest.fn().mockResolvedValue({ json: () => ({ temp: 25 }) });
const weather = await getWeather(mockFetch);
expect(weather.temp).toBe(25);
});
Code Organization Suggestions
Group related functions together:
// userUtils.js
export function validateUser(user) {
// Validation logic
}
export function formatUserName(user) {
// Formatting logic
}
export function saveUser(user) {
// Saving logic
}
// Import as needed
import { validateUser, formatUserName } from './userUtils';
Parameter Validation Best Practices
Validate parameters at the function entry point:
function createAccount(username, password) {
if (typeof username !== 'string' || username.length < 4) {
throw new Error('Username must be at least 4 characters');
}
if (typeof password !== 'string' || password.length < 8) {
throw new Error('Password must be at least 8 characters');
}
// Normal logic
}
Recursive Function Notes
Recursive functions must have a termination condition:
// Calculate factorial
function factorial(n) {
if (n < 0) throw new Error('Negative numbers not allowed');
if (n <= 1) return 1; // Termination condition
return n * factorial(n - 1);
}
// Tail recursion optimization (JavaScript engines may not optimize this)
function factorial(n, acc = 1) {
if (n < 0) throw new Error('Negative numbers not allowed');
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
Function Overloading Patterns
Simulate function overloading in JavaScript:
function createElement(tag, options) {
if (typeof tag === 'string') {
// Handle string tag names
const element = document.createElement(tag);
if (options) applyOptions(element, options);
return element;
} else if (tag instanceof Function) {
// Handle component functions
return tag(options);
}
throw new Error('Invalid tag parameter');
}
function applyOptions(element, options) {
// Apply various options
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn