判断一个数是奇数还是偶数
模运算符 % 做得很好。
const IsEven = num => num % 2 === 0;
console.log(IsEven(2));
// Result: true
console.log(IsEven(3));
// Result: false
判断日期是否为工作日
检查给定日期是否为工作日。
const isWorkday = (date) => date.getDay() % 6 !== 0;
console.log(isWorkday(new Date("2022/10/17")));
// Result: true (Monday)
console.log(isWorkday(new Date("2022/10/16")));
// Result: false (Sumday)
获取随机布尔值(真/假)
使用 Math.random() 会返回一个介于 0 和 1 之间的随机数,然后判断是否大于 0.5 会得到一个有 50% 概率为 True 或 False 的值。
const randomBool = () => Math.random() >= 0.5;
console.log(randomBool());
从日期对象获取时间
使用 Date 对象的 .toTimeString() 方法将其转换为时间字符串,然后截取该字符串。
const timeBeginDate = date => date.toTimeString().slice(0, 8);
console.log(timeBeginDate(new Date(2022, 8, 10, 15, 30, 21)));
// Result: "15:30:21"
console.log(timeBeginDate(new Date()));
// Result: return current time
滚动到页面顶部
window.scrollTo() 会滚动到指定坐标,如果坐标设置为(0, 0),会返回到页面顶部。
const toTop = () => window.scrollTo(0, 0);
toTop();
反转字符串
反转字符串的方法有很多,这里是最简单的一种,使用 split()、reverse() 和 join()
const reverse = str => str.split('').reverse().join('');
console.log(reverse('hello maxwell'));
//Result: llewxam olleh
确定当前选项卡是否可见
浏览器可以打开很多标签,下面的代码段是判断当前标签是否为活动标签。
const isBrowserTabInView = () => document.hidden;
isBrowserTabInView();
检查指定元素是否被聚焦
你可以使用 document.activeElement 来确定元素是否处于焦点中。
const elementIsFocus = (el) => (el === document.activeElement);
elementIsFocus(anyElement)
// Returns True if it is in focus, otherwise returns False
判断当前用户是否支持触摸事件
const touchSupported = () => {
('ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch);
}
console.log(touchSupported());
// Returns True if touch events are supported, otherwise returns False
判断当前用户是否为 Apple 设备
你可以使用 navigator.platform 来确定当前用户是否是 Apple 设备。
const isAppleDevice = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
console.log(isAppleDevice);
// If it is an Apple device it will return True otherwise it will return False
获取所有参数的平均值
reduce() 函数可用于计算所有参数的平均值。
const average = (...args) => args.reduce((a, b) => a + b) / args.length;
var avg = average(6,10, 8, 12);
console.log(avg);
// Result: 9
转换华氏/摄氏度
不要再害怕处理温度单位了,下面两个函数就是两个温度单位的相互转换。
const celsiusToFahrenheit = (celsius) => celsius * 9 / 5 + 32;
const fahrenheitToCelsius = (fahrenheit) => (fahrenheit - 32) * 5 / 9;
// Examples
console.log(celsiusToFahrenheit(20)); // 68
console.log(celsiusToFahrenheit(0)); // 32
console.log(celsiusToFahrenheit(-15)); // 5
console.log(celsiusToFahrenheit(35)); // 95
滚动到页面顶部
我们可以使用 window.scrollTo() 平滑滚动到页面顶部。
const scrollToTop = () => {
window.scrollTo({ top: 0, left: 0, behavior: "smooth" });
};
滚动到页面底部
当然,如果知道文档的高度,也可以平滑滚动到页面底部。
const scrollToBottom = () => {
window.scrollTo({
top: document.documentElement.offsetHeight,
left: 0,
behavior: "smooth",
});
};
滚动元素到可见区域
有时我们需要将元素滚动到可见区域,该怎么办? 使用scrollIntoView就足够了。
const smoothScroll = (element) => {
element.scrollIntoView({
behavior: "smooth",
});
};
全屏显示元素
你一定遇到过这样的场景,你需要全屏播放视频并在浏览器中全屏打开页面。
const goToFullScreen = (element) => {
element = element || document.body;
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullScreen();
}
};
退出浏览器全屏状态
是的,这个和第四点一起使用,你也会出现退出浏览器全屏状态的场景。
const goExitFullscreen = () => {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
};
获取数据类型
如何通过函数获取变量的数据类型?
const getType = (value) => {
const match = Object.prototype.toString.call(value).match(/ (\w+)]/)
return match[1].toLocaleLowerCase()
}
getType() // undefined
getType({}}) // object
getType([]) // array
getType(1) // number
getType('fatfish') // string
getType(true) // boolean
getType(/fatfish/) // regexp
停止冒泡事件
需要一种适用于所有平台的防止事件冒泡的方法。
const stopPropagation = (event) => {
event = event || window.event;
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
};
深拷贝对象
如何复制深度嵌套的对象?
const deepCopy = (obj, hash = new WeakMap()) => {
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
if (hash.has(obj)) {
return hash.get(obj);
}
let allDesc = Object.getOwnPropertyDescriptors(obj);
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);
hash.set(obj, cloneObj);
for (let key of Reflect.ownKeys(obj)) {
if (obj[key] && typeof obj[key] === "object") {
cloneObj[key] = deepCopy(obj[key], hash);
} else {
cloneObj[key] = obj[key];
}
}
return cloneObj;
};
问题:克隆嵌套对象可能很棘手且容易出错。
解决方案:使用结构化克隆或 Lodash 等库来深度克隆对象。
const obj = { a: 1, b: { c: 2 } };
const deepClone = JSON.parse(JSON.stringify(obj));
console.log(deepClone); // { a: 1, b: { c: 2 } }
确定设备类型
我们经常需要这样做,以便在手机上显示 A 逻辑,在 PC 上显示 B 逻辑。 基本上,设备类型是通过识别浏览器的 userAgent 来确定的。
const isMobile = () => {
return !!navigator.userAgent.match(
/(iPhone|iPod|Android|ios|iOS|iPad|Backerry|WebOS|Symbian|Windows Phone|Phone)/i
);
};
判断设备是Android还是IOS
除了区分是手机还是PC之外,很多时候我们还需要区分当前设备是Android还是IOS。
const getExplorerInfo = () => {
let t = navigator.userAgent.toLowerCase();
return 0 <= t.indexOf("msie")
? {
//ie < 11
type: "IE",
version: Number(t.match(/msie ([\d]+)/)[1]),
}
: !!t.match(/trident\/.+?rv:(([\d.]+))/)
? {
// ie 11
type: "IE",
version: 11,
}
: 0 <= t.indexOf("edge")
? {
type: "Edge",
version: Number(t.match(/edge\/([\d]+)/)[1]),
}
: 0 <= t.indexOf("firefox")
? {
type: "Firefox",
version: Number(t.match(/firefox\/([\d]+)/)[1]),
}
: 0 <= t.indexOf("chrome")
? {
type: "Chrome",
version: Number(t.match(/chrome\/([\d]+)/)[1]),
}
: 0 <= t.indexOf("opera")
? {
type: "Opera",
version: Number(t.match(/opera.([\d]+)/)[1]),
}
: 0 <= t.indexOf("Safari")
? {
type: "Safari",
version: Number(t.match(/version\/([\d]+)/)[1]),
}
: {
type: t,
version: -1,
};
};
设置cookie
cookie可能是我见过的最糟糕的API,它太难用了,我们不得不重新封装它以最大限度地提高我们的开发效率。
const setCookie = (key, value, expire) => {
const d = new Date();
d.setDate(d.getDate() + expire);
document.cookie = `${key}=${value};expires=${d.toUTCString()}`;
};
获取cookies
除了对cookie进行写入之外,我们还会涉及到它的读取操作。
const getCookie = (key) => {
const cookieStr = unescape(document.cookie);
const arr = cookieStr.split("; ");
let cookieValue = "";
for (let i = 0; i < arr.length; i++) {
const temp = arr[i].split("=");
if (temp[0] === key) {
cookieValue = temp[1];
break;
}
}
return cookieValue;
};
删除cookies
删除cookie的想法是什么? 其实只要将它的过期时间设置为此时,它就会立即过期。
const delCookie = (key) => {
document.cookie = `${encodeURIComponent(key)}=;expires=${new Date()}`;
};
生成随机字符串
不知道大家有没有遇到过需要生成随机字符串的场景。 我遇到过很多次了,每次都要重新google一遍,直到学会这个工具功能。
const randomString = (len) => {
let chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz123456789";
let strLen = chars.length;
let randomStr = "";
for (let i = 0; i < len; i++) {
randomStr += chars.charAt(Math.floor(Math.random() * strLen));
}
return randomStr;
};
randomString(10) // pfkMfjEJ6x
randomString(20) // ce6tEx1km4idRNMtym2S
将字符串的第一个字母大写
const fistLetterUpper = (str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
fistLetterUpper('fatfish') // Fatfish
生成指定范围内的随机数
const randomNum = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
randomNum(1, 10) // 6
randomNum(10, 20) // 11
打乱数组的顺序
const shuffleArray = (array) => {
return array.sort(() => 0.5 - Math.random())
}
let arr = [ 1, -1, 10, 5 ]
shuffleArray(arr) // [5, -1, 10, 1]
shuffleArray(arr) // [1, 10, -1, 5]
从数组中获取随机值
之前做过一个抽奖项目,需要让数组中的奖品随机出现。
const getRandomValue = array => array[Math.floor(Math.random() * array.length)];
const prizes = [ '$100', '🍫', '🍔' ]
getRandomValue(prizes) // 🍫
getRandomValue(prizes) // 🍔
getRandomValue(prizes) // 🍫
第一种格式化货币的方式
货币格式化的方式有很多种,比如这两种方式。
const formatMoney = (money) => {
return money.replace(new RegExp(`(?!^)(?=(\\d{3})+${money.includes('.') ? '\\.' : '$'})`, 'g'), ',')
}
formatMoney('123456789') // '123,456,789'
formatMoney('123456789.123') // '123,456,789.123'
formatMoney('123') // '123'
第二种格式化货币的方式
正则表达式让我们太头疼了不是吗? 因此,我们需要找到一种更简单的方式来格式化货币。
const formatMoney = (money) => {
return money.toLocaleString()
}
formatMoney(123456789) // '123,456,789'
formatMoney(123456789.123) // '123,456,789.123'
formatMoney(123) // '123'
使用 `let` 和 `const` 代替 `var`
问题:`var` 具有函数作用域,这可能导致错误和不可预测的行为。
解决方案:使用具有块作用域的 `let` 和 `const`。
let count = 0;
const PI = 3.14;
使用 `let` 和 `const` 有助于防止与作用域相关的错误,确保变量只能在定义的块内访问。
默认参数
问题:如果没有提供参数,函数可能会失败。
解决方案:使用默认参数设置后备值。
function greet(name = 'Guest') {
return `Hello, ${name}!`;
}
console.log(greet()); // "Hello, Guest!"
默认参数确保函数具有合理的默认值,从而防止错误并使代码更加健壮。
模板文字
问题:字符串连接可能很麻烦且容易出错。
解决方案:使用模板文字进行更清晰、更易读的字符串插值。
const name = 'John';
const greeting = `Hello, ${name}!`;
console.log(greeting); // "Hello, John!"
模板文字使创建带有嵌入表达式和多行字符串的字符串变得更加容易。
解构赋值
问题:从对象和数组中提取值可能非常冗长。
解决方案:使用解构赋值更简洁地提取值。
const user = { name: 'Jane', age: 25 };
const { name, age } = user;
console.log(name, age); // "Jane" 25
解构赋值允许您轻松地将对象中的属性和数组中的元素提取到不同的变量中。
扩展运算符
问题:组合数组或对象可能很麻烦。
解决方案:使用扩展运算符可以轻松组合数组和对象。
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = […arr1, …arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
扩展运算符允许您将一个数组或对象的元素扩展到另一个数组或对象中。
剩余参数
问题:处理可变数量的函数参数可能很棘手。
解决方案:使用剩余参数捕获数组中的所有参数。
function sum(…args) {
return args.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
剩余参数允许您将无限数量的参数作为数组处理,从而使您的函数更加灵活。
短路求值
问题:编写条件语句可能很冗长。
解决方案:使用短路求值编写简洁的条件。
const isLoggedIn = true;
const user = isLoggedIn && { name: 'Jane', age: 25 };
console.log(user); // { name: 'Jane', age: 25 }
短路求值使用逻辑运算符 `&&` 和 `||` 来简化条件表达式。
可选链
问题:如果链中的任何部分为 `null` 或 `undefined`,则访问深层嵌套的属性可能会导致错误。
解决方案:使用可选链安全地访问嵌套属性。
const user = { profile: { name: 'Jane' } };
const userName = user?.profile?.name;
console.log(userName); // "Jane"
可选链式连接允许您安全地访问嵌套属性,而无需明确检查链式连接的每一级是否为 `null` 或 `undefined`。
空值合并
问题:如果值为 `0` 或 `””`,则使用 `||` 提供默认值可能会产生意外结果。
解决方案:仅在 `null` 或 `undefined` 时使用空值合并 (`??`) 提供默认值。
const user = { name: '', age: 0 };
const userName = user.name ?? 'Anonymous';
const userAge = user.age ?? 18;
console.log(userName); // ""
console.log(userAge); // 0
空值合并仅允许在左侧为“null”或“undefined”时提供默认值。
对象属性简写
问题:将变量分配给对象属性可能会重复。
解决方案:使用属性简写来简化对象创建。
const name = 'Jane';
const age = 25;
const user = { name, age };
console.log(user); // { name: 'Jane', age: 25 }
属性简写允许您在属性名称与变量名称匹配时省略属性名称,从而使代码更简洁。
动态属性名称
问题:使用动态属性名称创建对象可能很冗长。
解决方案:使用计算属性名称动态创建对象属性。
const propName = 'age';
const user = { name: 'Jane', [propName]: 25 };
console.log(user); // { name: 'Jane', age: 25 }
计算属性名称允许您动态创建对象属性,使用表达式的值作为属性名称。
数组 `map()`、`filter()` 和 `reduce()`
问题:迭代数组以转换、过滤或累积值可能会重复。
解决方案:使用 `map()`、`filter()` 和 `reduce()` 进行常见的数组操作。
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]
const sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum); // 15
这些数组方法提供了一种转换、过滤和减少数组的函数式方法,使您的代码更具表现力和简洁性。
字符串 `includes()`、`startsWith()` 和 `endsWith()`
问题:检查字符串是否包含、以子字符串开头或以子字符串结尾可能很冗长。
解决方案:使用 `includes()`、`startsWith()` 和 `endsWith()` 进行更简单的字符串检查。
const str = 'Hello, world!';
console.log(str.includes('world')); // true
console.log(str.startsWith('Hello')); // true
console.log(str.endsWith('!')); // true
这些字符串方法提供了一种简单易读的方法来检查子字符串的存在、开始或结束。
函数参数中的数组和对象解构
问题:从作为函数参数传递的数组或对象中提取值可能很冗长。
解决方案:在函数参数中使用解构来直接提取值。
const user = { name: 'Jane', age: 25 };
function greet({ name, age }) {
return `Hello, ${name}! You are ${age} years old.`;
}
console.log(greet(user)); // "Hello, Jane! You are 25 years old."
函数参数中的解构允许您直接从传递给函数的对象或数组中提取值,从而使代码更简洁、更易读。
解构中的默认值
问题:解构对象时处理缺失的属性可能很麻烦。
解决方案:在解构中使用默认值来提供后备值。
const user = { name: 'Jane' };
const { name, age = 18 } = user;
console.log(name); // "Jane"
console.log(age); // 18
解构中的默认值允许您为可能缺失的属性提供后备值,从而使您的代码更加健壮。
对象 `assign()`
问题:克隆或合并对象可能很冗长且容易出错。
解决方案:使用 `Object.assign()` 克隆或合并对象。
const target = { a: 1 };
const source = { b: 2 };
const merged = Object.assign(target, source);
console.log(merged); // { a: 1, b: 2 }
`Object.assign()` 允许您高效地克隆或合并对象,从而减少手动复制的需要。
数组 `find()` 和 `findIndex()`
问题:使用循环在数组中查找元素或其索引可能很麻烦。
解决方案:使用 `find()` 和 `findIndex()` 使代码更易读。
const users = [
{ id: 1, name: 'Jane' },
{ id: 2, name: 'John' },
];
const user = users.find(u => u.id === 1);
console.log(user); // { id: 1, name: 'Jane' }
const index = users.findIndex(u => u.id === 1);
console.log(index); // 0
这些数组方法提供了一种根据条件查找元素或其索引的简单方法,从而提高了代码的可读性。
数组 `some()` 和 `every()`
问题:检查数组中的部分或全部元素是否满足条件可能会很冗长。
解决方案:使用 `some()` 和 `every()` 来获得更简洁的代码。
const numbers = [1, 2, 3, 4, 5];
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
const allEven = numbers.every(num => num % 2 === 0);
console.log(allEven); // false
这些数组方法允许您以简洁的方式检查数组中的部分或全部元素是否满足条件。
数组 `flat()` 和 `flatMap()`
问题:展平嵌套数组或映射和展平数组可能很麻烦。
解决方案:使用 `flat()` 和 `flatMap()` 使代码更易读。
const nested = [1, [2, [3, [4]]]];
const flat = nested.flat(2);
console.log(flat); // [1, 2, 3, [4]]
const mapped = [1, 2, 3].flatMap(x => [x, x * 2]);
console.log(mapped); // [1, 2, 2, 4, 3, 6]
这些数组方法提供了一种简单的方法来展平嵌套数组,并在一个步骤中映射和展平。
数组 `from()` 和 `of()`
问题:从可迭代对象或参数创建数组可能很冗长。
解决方案:使用 `Array.from()` 和 `Array.of()` 获得更简洁的代码。
const set = new Set([1, 2, 3]);
const arrFromSet = Array.from(set);
console.log(arrFromSet); // [1, 2, 3]
const arrOfNumbers = Array.of(1, 2, 3);
console.log(arrOfNumbers); // [1, 2, 3]
`Array.from()` 允许您从可迭代对象创建数组,而 `Array.of()` 允许您从参数列表创建数组。
回调中的参数解构
问题:处理可选回调函数可能很麻烦。
解决方案:使用短路求值来调用可选回调。
function fetchData(url, callback) {
fetch(url).then(
response => response.json()
).then(
data => {
callback && callback(data);
}
);
}
短路求值允许您仅在提供可选回调函数时才调用该函数,从而使代码更加健壮。
Promisify 回调
问题:将基于回调的函数转换为承诺可能很麻烦。
解决方案:使用实用函数来 promisify 回调。
function promisify(fn) {
return function (…args) {
return new Promise((resolve, reject) => {
fn(…args, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
};
}
const readFile = promisify(require('fs').readFile);
readFile('path/to/file.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err));
Promisifying 允许您将基于回调的函数转换为承诺,从而更轻松地使用 async/await 语法。
用于类似同步代码的 Async/Await
问题:使用承诺编写异步代码可能冗长且难以阅读。
解决方案:使用 async/await 以同步风格编写异步代码。
async function fetchData(url) {
try {
const response = await fetch(url);
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData('https://api.example.com/data');
Async/await 提供了一种编写外观和行为都像同步代码的异步代码的方法,从而提高了可读性和可维护性。
链接承诺
问题:按顺序处理多个异步操作可能很麻烦。
解决方案:链式承诺处理多个异步操作。
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log('Data:', data);
return fetch('https://api.example.com/more-data');
})
.then(response => response.json())
.then(moreData => {
console.log('More Data:', moreData);
})
.catch(error => {
console.error('Error:', error);
}
链接 Promise 可让您按顺序处理多个异步操作,从而提高可读性和可维护性。
Promise.all 用于并发执行
问题:同时处理多个异步操作可能具有挑战性。
解决方案:使用 `Promise.all` 来处理并发异步操作。
const fetchData1 = fetch('https://api.example.com/data1').then(response => response.json());
const fetchData2 = fetch('https://api.example.com/data2').then(response => response.json());
Promise.all([fetchData1, fetchData2])
.then(([data1, data2]) => {
console.log('Data 1:', data1);
console.log('Data 2:', data2);
})
.catch(error => {
console.error('Error:', error);
});
`Promise.all` 允许您同时处理多个异步操作,并在所有操作完成后继续执行。
防抖动函数
问题:频繁的函数调用(例如在窗口调整大小事件期间)会降低性能。
解决方案:使用防抖动函数来限制函数执行的速率。
function debounce(func, wait) {
let timeout;
return function (…args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
window.addEventListener('resize', debounce(() => {
console.log('Window resized');
}, 200));
防抖动函数可确保函数仅在一段时间不活动后才被调用,从而提高性能。
节流阀函数
问题:限制频繁触发的事件(如滚动或调整大小)的函数执行速率。
解决方案:使用节流阀函数来限制函数的执行速率。
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function (…args) {
if (!lastRan) {
func.apply(this, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if (Date.now() - lastRan >= limit) {
func.apply(this, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
window.addEventListener('scroll', throttle(() => {
console.log('Window scrolled');
}, 200));
节流函数可确保在指定时间段内最多只调用一次函数,从而提高频繁触发事件的性能。
记忆化
问题:反复调用昂贵的函数会降低性能。
解决方案:使用记忆化来缓存昂贵的函数调用的结果。
function memoize(func) {
const cache = new Map();
return function (…args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = func.apply(this, args);
cache.set(key, result);
return result;
};
}
const expensiveFunction = memoize((num) => {
console.log('Computing…');
return num * 2;
});
console.log(expensiveFunction(2)); // "Comput
ing…" 4
console.log(expensiveFunction(2)); // 4
记忆化通过缓存昂贵的函数调用结果并返回缓存的结果以供后续具有相同参数的调用来提高性能。
柯里化函数
问题:创建具有多个参数的函数可能很麻烦。
解决方案:使用柯里化创建具有部分应用参数的函数。
function curry(func) {
return function curried(…args) {
if (args.length >= func.length) {
return func.apply(this, args);
}
return function (…nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
};
}
const sum = (a, b, c) => a + b + c;
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
通过柯里化,您可以创建可以用较少参数调用的函数,并返回接受其余参数的新函数。
部分应用
问题:调用带有重复参数的函数可能很繁琐。
解决方案:使用部分应用将一些参数预先应用于函数。
function partial(func, …presetArgs) {
return function (…laterArgs) {
return func(…presetArgs, …laterArgs);
};
}
const multiply = (a, b, c) => a * b * c;
const double = partial(multiply, 2);
console.log(double(3, 4)); // 24
部分应用允许您通过预先应用一些参数来创建新函数,从而使您的代码更加灵活和可重用。
函数组合
问题:将多个函数组合成一个操作可能很麻烦。
解决方案:使用函数组合来组合多个函数。
const compose = (…funcs) => (arg) =>
funcs.reduceRight((prev, fn) => fn(prev), arg);
const add = (x) => x + 1;
const multiply = (x) => x * 2;
const addThenMultiply = compose(multiply, add);
console.log(addThenMultiply(5)); // 12
函数组合允许您通过组合多个函数来创建新函数,从而使您的代码更加模块化和可重用。
函数流水线
问题:将一系列函数应用于一个值可能会很冗长。
解决方案:使用函数流水线按顺序应用一系列函数。
const pipe = (…funcs) => (arg) =>
funcs.reduce((prev, fn) => fn(prev), arg);
const add = (x) => x + 1;
const multiply = (x) => x * 2;
const addThenMultiply = pipe(add, multiply);
console.log(addThenMultiply(5)); // 12
函数流水线允许您按顺序将一系列函数应用于一个值,从而提高代码的可读性和可维护性。
避免使用全局变量
问题:全局变量可能导致冲突和意外的副作用。
解决方案:使用局部变量和模块来避免污染全局命名空间。
// Using local variables
function doSomething() {
let localVariable = 'This is local';
console.log(localVariable);
}
// Using modules
const myModule = (function () {
let privateVariable = 'This is private';
return {
publicMethod() {
console.log(privateVariable);
},
};
})();
myModule.publicMethod(); // "This is private"
避免使用全局变量有助于防止冲突和意外副作用,从而使您的代码更加模块化和易于维护。
使用闭包进行封装
问题:暴露函数的内部细节可能会导致误用。
解决方案:使用闭包封装内部细节。
function createCounter() {
let count = 0;
return {
increment() {
count++;
return count;
},
decrement() {
count - ;
return count;
},
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
闭包允许您封装内部细节并仅公开必要的功能,从而提高代码安全性和可维护性。
模块模式
问题:将代码组织成可重用的模块可能具有挑战性。
解决方案:使用模块模式创建可重用和封装的代码。
const myModule = (function () {
let privateVariable = 'This is private';
function privateMethod() {
console.log(privateVariable);
}
return {
publicMethod() {
privateMethod();
},
};
})();
myModule.publicMethod(); // "This is private"
模块模式允许您创建可重用和封装的代码,从而改善代码组织和可维护性。
单例模式
问题:确保只创建一个类的实例可能具有挑战性。
解决方案:使用单例模式创建单个实例。
const singleton = (function () {
let instance;
function createInstance() {
return {
name: 'Singleton Instance',
};
}
return {
getInstance() {
if (!instance) {
instance = createInstance();
}
return instance;
},
};
})();
const instance1 = singleton.getInstance();
const instance2 = singleton.getInstance();
console.log(instance1 === instance2); // true
单例模式确保只创建一个类的实例,这对于管理共享资源或配置很有用。
工厂模式
问题:创建具有复杂初始化的对象可能很麻烦。
解决方案:使用工厂模式创建对象。
function createUser(name, role) {
return {
name,
role,
sayHello() {
console.log(`Hello, my name is ${this.name} and I am a ${this.role}`);
},
};
}
const admin = createUser('Alice', 'admin');
const user = createUser('Bob', 'user');
admin.sayHello(); // "Hello, my name is Alice and I am an admin"
user.sayHello(); // "Hello, my name is Bob and I am a user"
工厂模式允许您以灵活且可重用的方式创建具有复杂初始化的对象。
观察者模式
问题:管理状态变化和通知多个组件可能具有挑战性。
解决方案:使用观察者模式来管理状态变化并通知观察者。
function Subject() {
this.observers = [];
}
Subject.prototype = {
subscribe(observer) {
this.observers.push(observer);
},
unsubscribe(observer) {
this.observers = this.observers.filter((obs) => obs !== observer);
},
notify(data) {
this.observers.forEach((observer) => observer.update(data));
},
};
function Observer(name) {
this.name = name;
}
Observer.prototype.update = function (data) {
console.log(`${this.name} received data: ${data}`);
};
const subject = new Subject();
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');
subject.subscribe(observer1);
subject.subscribe(observer2);
subject.notify('New data available'); // "Observer 1 received data: New data available" "Observer 2 received data: New data available"
观察者模式允许您管理状态变化并通知多个观察者,从而改善代码组织和可维护性。
避免使用 `eval()`
问题:使用 `eval()` 可能导致安全漏洞和性能问题。
解决方案:避免使用 `eval()` 并使用更安全的替代方案。
// Avoid
const code = 'console.log("Hello, world!")';
eval(code); // "Hello, world!"
// Use safer alternatives
const func = new Function('console.log("Hello, world!")');
func(); // "Hello, world!"
避免使用 `eval()` 有助于防止安全漏洞和性能问题,从而使您的代码更安全、更高效。