Анонимные функции впервые появились в языке LISP, а позже развились не только в функциональные языки, но и во все более и более анонимные функции в интерпретируемых и компилируемых языках.Возможно, есть более модное название — лямбда-выражение.
Замыкания в основном реализуются с помощью анонимных функций.Когда внешние переменные ссылаются в анонимных функциях, анонимные функции образуют замыкания. Из-за неразрывной связи между замыканиями и анонимными функциями их часто путают. На самом деле анонимные функции, замыкания, самовыполняющиеся функции, функции обратного вызова и стрелочные функции в Js кажутся одними и теми же, но они разные.Пожалуйста, прочтите и поймите сами.
Анонимные функции имеют динамически программируемый процесс выполнения. Умное использование может сделать ваш код простым, но элегантным, гибким, но ограниченным. Что ж, официально переходим к теме этой статьи и используем анонимные функции для рефакторинга кода. По соглашению рефакторинга сначала указать на неприятный запах в коде (Bad Smell):
- Определение длинных повторяющихся конфигураций
- Условная фильтрация коллекций
- Уникальные вызовы методов
Определение длинных повторяющихся конфигураций
При написании кода конфигурации вы часто сталкиваетесь с большим количеством повторяющихся конфигураций.Если вы хотите изменить какой-то контент, вы должны изменить их все, что очень утомительно и легко пропустить. Например:
{
html: `
<label><input type="checkbox" name="apple" /> Apple</label>
<label><input type="checkbox" name="banana" /> Banana</label>
<label><input type="checkbox" name="orange" /> Orange</label>
`
}
Здесь есть три варианта. Структура трех вариантов одинакова. Если вы хотите добавить один ко всем параметрамtitle
, то вы должны сделать это три раза.
{
html: `
<label><input type="checkbox" name="apple" title="Apple" /> Apple</label>
<label><input type="checkbox" name="banana" title="Banana" /> Banana</label>
<label><input type="checkbox" name="orange" title="Orange" /> Orange</label>
`
}
Программисты — странная группа людей, которые скорее сделают пять разных вещей, чем сделают одно и то же три раза. Итак, как это можно терпеть, извлекать разное содержимое в конфигурации:
{
html: (function(fruits) {
return fruits.map(it => `<label><input type="checkbox" name="${it.name}" title="${it.title}" /> ${it.title}</label>`).join('');
}([
{name: 'apple', title: 'Apple'},
{name: 'banana', title: 'Banana'},
{name: 'orange', title: 'Orange'}
]))
}
Такая модификация полностью прозрачна для вызывающей стороны конфигурации, но для мейнтейнера конфигурации структура и данные разделены.Параметры метода могут значительно снизить риск совершения ошибок и избежать бездумного копирования и вставки.
Хотя написание логики кода в конфигурации не является особенно рекомендуемой практикой, это ничто по сравнению с удобством сопровождения кода.
Условная фильтрация коллекций
Фильтрация коллекций — очень распространенное требование Предположим, есть студенческая коллекция:
let students = [
{name: 'Lucy', age: 20, sex: 'female'},
{name: 'LiLei', age: 21, sex: 'male'},
{name: 'Jim', age: 18, sex: 'male'}
]
Теперь, чтобы отфильтровать одноклассников, которым 20 лет:
function filterByAge(list, age) {
let filtered = [];
for (let i = 0; i < list.length; i++) {
if (list[i].age === age) {
filtered.push(list[i]);
}
}
return filtered;
}
И отфильтровать одноклассницу по имени ЛиЛей:
function filterByName(list, name) {
let filtered = [];
for (let i = 0; i < list.length; i++) {
if (list[i].name === name) {
filtered.push(list[i]);
}
}
return filtered;
}
Также отфильтруйте одноклассников мужского пола:
function filterBySex(list, sex) {
let filtered = [];
for (let i = 0; i < list.length; i++) {
if (list[i].sex === sex) {
filtered.push(list[i]);
}
}
return filtered;
}
Просто, когда вы думаете, что закончили и можете встатьсгибатьКогда я вытягивал талию, меня внезапно с силой оттолкнуло назад на сиденье, что помогло мне найти детскую обувь, названия которой начинались на «Л». Хоть ты и ммп~ в душе, что еще ты можешь сделать?Пиши, поэтому я добавил следующий метод:
function filterByNameStart(list, nameStart) {
let filtered = [];
for (let i = 0; i < list.length; i++) {
if (list[i].name.indexOf(nameStart) === 0) {
filtered.push(list[i]);
}
}
return filtered;
}
Итак, этот метод вызова:
filterByName(filterBySex(filterByAge(students, 21), 'male'), 'LiLei');
Это можно понимать как нахождение ЛиЛей, 21-летнего мужчины. Что ж, читабельность не так уж и плоха.
Однако нетрудно заметить, что вышеизложенноеfilterByAge
,filterByNameStart
В этой серии методов, за исключением разных условий фильтрации, остальная логика точно такая же, что приводит к большому дублированию кода и вообще никакой гибкости.Если вызывающая сторона меняет требования, вы должны добавлять методы.
Теперь мы используем анонимные функции для извлечения различных частей и позволяем вызывающей стороне фильтровать по своему усмотрению.filter
Основная логика метода заботится только о том, должен ли текущий элемент быть помещен в набор результатов, а другая логика оценки передается анонимной функции.fn
сделать:
function filter(list, fn) {
let filtered = [];
for (let i = 0; i < list.length; i++) {
if (fn(list[i], i) === true) {
filtered.push(list[i]);
}
}
return filtered;
}
Приведенный выше пример будет записан так:
filter(students, function(member) {
return member.name === 'LiLei' && member.age === 21 && member.sex === 'male';
});
Использование метода стрелки может быть более кратким:
filter(students, member => member.name === 'LiLei' &&
member.age === 21 &&
member.sex === 'male');
Теперь звонящий упоминает какие-то ненормальные методы фильтрации, можете оглянуться и написать сами.
Уникальные вызовы методов
Предположим, что есть API-интерфейс, который удваивает входящее число и возвращает его, этот интерфейс поддерживает одиночные и пакетные методы.
входящий5
- Выполнить успешно и вернуться
{status: 'success', data: 10}
- Ошибка выполнения возвращается
{status: 'failed', error: 'xxx'}
входящий[2, 3]
- Выполнить успешно и вернуться
{status: 'success', data: [{status: 'success', data: 4}, {status: 'success', data: 6}]}
- Ошибка выполнения возвращается
{status: 'success', data: [{status: 'failed', error: 'xxx'}, {status: 'failed', error: 'xxx'}]}
То есть один ввод будет выводиться в одном формате, а пакетный ввод будет выводиться в пакетном формате. Реализована следующая версия:
function multiple(inNum) {
if (Array.isArray(inNum)) {
// 处理批量情况
return {
status: 'success',
data: inNum.map(it => {
if (isNaN(parseFloat(it))) {
return {
status: 'failed',
error: 'The input is not a number'
};
}
return {
status: 'success',
data: it * 2
}
})
};
} else {
// 处理单个情况
if (isNaN(parseFloat(inNum))) {
return {
status: 'failed',
error: 'The input is not a number'
};
}
return {
status: 'success',
data: inNum * 2
};
}
}
Есть два метода, одиночный и пакетный, за исключением того, что форматы ввода и вывода разные, а остальная логика точно такая же. Если вы хотите заменить умножение на 2 на умножение на 3, вы должны поменять оба места. Давайте посмотрим, как использовать анонимные функции, чтобы избежать двух изменений:
function execute(data, fn) {
// 最小执行单元
let single = it => {
try {
return {
status: 'success',
data: fn(it)
};
} catch (e) {
return {
status: 'failed',
error: e.toString()
}
}
};
if (Array.isArray(data)) {
return {
status: 'success',
data: data.map(single)
}
} else {
return single(data);
}
}
function multiple(inNum) {
return execute(inNum, it => {
if (isNaN(parseFloat(it))) {
throw new Error('The input is not a number');
}
return it * 2;
});
}
Сейчасexecute
Этот метод заботится только о формате ввода и вывода и обработке ошибок и выполняет всю грязную работу;multiple
Метод заботится только о конкретной реализации бизнеса и не заботится о том, является ли ввод одним элементом или массивом. Если вы хотите умножить на 3, просто изменитеmultiple
метод последнийreturn
. В качестве таких,execute
Его также можно повторно использовать другими методами API, которые, можно сказать, убивают двух зайцев одним выстрелом.
Суммировать
Цель этой статьи — просто познакомить с некоторыми идеями.В коде много неприятных запахов, которые можно рефакторить с помощью анонимных функций.Этот метод рефакторинга применим не только к Js. Пока вы больше думаете и делаете больше, качество кода определенно будет на высоте~~
О рефакторинге хотелось бы сказать еще немного.Процесс рефакторинга должен быть постепенным.Когда меняешь в первый раз,можешь подумать,что это нормально,но во второй раз надо подумать,а нет ли лучшего способа чтобы достичь этого. Лучше всего, если модификация прозрачна для вызывающего абонента. Стоит позволить вызывающему абоненту сотрудничать с модификацией. Конечно, необходимо взвесить временные затраты. Твой босс наблюдает за тобой 😡
Счастливый код 😁