Потребовалось три недели и ночи, чтобы дочитать книгу «Написание поддерживаемого JavaScript». Резюме таково: Первая часть по стилю программирования и третья часть по автоматизированному тестированию находятся в конце книги. Автор обобщил и разобрал. можете посмотреть, что я организовал раньше (Как выработать хороший стиль программирования). Основное внимание в этой книге уделяется практике программирования второй части, которая также является наиболее питательным местом. К сожалению, автор специально не обобщил ее в книге. Я подытожу ее здесь, чтобы помочь вам вместе улучшить качество кода. Рекомендуется читать и нажимать, чувствуя, что более полезно, чем просто чтение статьи. Контента много, поэтому нужно запастись терпением.
1. Слабая связь слоя пользовательского интерфейса
Многие шаблоны проектирования предназначены для решения проблемы тесной связи. Как добиться слабой связи,При изменении компонента без изменения компонентов где-либо еще мы можем сказать, что это слабая связь и ключ к улучшению ремонтопригодности кода.
1-1. Извлеките JS из Css
образец кода
// 不好的写法
.box {
// Css表达式包裹在一个特殊的expression()函数中
width: expression(document.body.offsetWidth + 'px')
}
Рекомендуемая практика: избегайте использования выражений CSS (браузеры IE9 и выше больше не поддерживают выражения CSS).
1-2. Извлеките CSS из JS
Образец кода:
// 不好的写法
element.style.color = 'red'
element.style.cssText = 'color: red; left: 10px; top: 100px;'
Когда вам нужно изменить стиль элемента с помощью js, вы можете добавить соответствующее имя класса в js, манипулируя className CSS.
Образец кода:
/*定义CSS样式*/
.reveal {
color: red;
left: 10px;
top: 100px;
}
// 好的写法 - 原生写法
element.className += 'reveal'
// 好的写法 - HTML5
element.classList.add('reveal')
Рекомендуемая практика: js не должен напрямую манипулировать стилями, чтобы поддерживать слабую связь с CSS. В дополнение к изменению значения по умолчанию атрибута позиционирования, такого как style.top, style.left изменяет значение по умолчанию через js.
1-3.Извлечение JS из HTML
// 不好的写法
<button onclick="doSomething()">Click Me </button>
Первая проблема: строго говоря, когда на кнопке происходит событие клика, должна существовать функция doSomething(). Может случиться так, что функция не существует, когда пользователь нажимает кнопку, и в это время будет сообщено об ошибке JS; Вторая проблема: с точки зрения ремонтопригодности, если вы измените имя функции doSomething() в этом примере, вам нужно изменить код функции как HTML, так и JS, который является типичным тесно связанным кодом. пути улучшения: образец кода
function doSomething () {
// 一些代码
}
var btn = document.getElementById('action-btn')
btn.addEventListener('click', doSomething, false)
Совместимость IE8 и более ранние версии не поддерживают функцию addEventListener(), поэтому вам нужна стандартная функция для инкапсуляции этих различий.
образец кода
function addEventListernner(target, type, handler) {
if (target.addEventListener) {
target.addEventListener(type, handler, false)
} else if (target.addEventListener) {
target.addEventListener('on' + type, handler)
} else {
target['on' + type] = handler
}
}
Эта функция отлично работает во всех случаях, мы часто используем этот метод вот так
function doSomething () {
// 一些代码
}
var btn = document.getElementByid('action-btn')
addEventListener(btn, 'click', doSomething)
Рекомендуемая практика: для библиотек, управляемых узлами, таких как JQ, рекомендуется использовать мониторинг событий для привязки узлов в js-файлах и выдачи соответствующих функциональных событий. Не рекомендуется напрямую привязывать функциональные события к html-файлам.
1-4. Извлечение HTML из JS
// 不好的写法
var div = document.getElementById('mu-div')
div.innerHTML = '<h3>Hello World</h3>'
пути улучшения:
-
а. Для большого количества тегов можно использовать - загрузить с сервера
-
б. Для небольшого количества тегов можно использовать - простой клиентский шаблон
-
в) Для сложных шаблонов на стороне клиента рассмотрите такие решения, как Handlebars (http://handlebarsjs.com/), который представляет собой полную систему шаблонов на стороне клиента, разработанную для JS на стороне браузера.
1-5. Резюме одним предложением
HTML, CSS, JS, отношения между ними должны быть независимыми друг от друга. Если происходит пересечение и появляется сильно связанный код, принцип ремонтопригодности кода нарушается.
2. Обработка событий
Введение сценария
// 不好的用法
function handleClick (event) {
var popup = document.getElementById('popup')
popup.style.left = event.clientX + 'px'
popup.style.top = event.clientY + 'px'
popup.className = 'reveal'
}
// 上文中的addEventListener()
addEventListener(element, 'click', handleClick)
2-1. Изолируйте логику приложения
Проблема с приведенным выше примером кода заключается в том, что обработчик событий (связанный с поведением пользователя) содержит логику приложения (логика приложения — это функциональный код, связанный с приложением, а не с поведением пользователя). В приведенном выше примере кода логика приложения заключается в отображении всплывающего окна в определенном месте, но иногда вам нужно определить, следует ли отображать всплывающее окно, когда пользователь наводит указатель мыши на элемент или когда нажимается клавиша. на клавиатуре нажимается кадр. Такие несколько обработчиков событий выполняют одну и ту же логику приложения, и ваш код случайно дублируется.
Лучше всего отделить логику приложения от всех обработчиков событий, поэтому давайте перепишем приведенный выше код следующим образом:
// 好的写法 -事件处理程序抽离应用逻辑
var MyApplication = {
handleClick: function (event) {
this.showPopup(event)
},
// 应用逻辑:显示弹出框
showPopup: function (event) {
var popup = document.getElementById('popup')
popup.style.left = event.clientX + 'px'
popup.style.top = event.clientY + 'px'
popup.className = 'reveal'
}
}
addEventListener(element, 'click', function(event) {
MyApplication.handleClick(event)
})
Рекомендуемая практика: абстрагирование обработчиков событий от логики приложения.
2-2. Не отправлять объекты событий
После удаления логики приложения в приведенном выше коде все еще есть проблема, то есть объект события распространяется без проверки. Он передается в MyApplication.handleClick() из обработчика событий анонимной функции, который, в свою очередь, передается в MyApplication. Показать всплывающее окно(), Объект события содержит много дополнительной информации, связанной с событием, и этот код использует только две из них. Логика приложения не должна полагаться на объект события для правильной работы. Лучше всего, чтобы обработчик событий использовал объект события для обработки события, а затем получал данные, необходимые для передачи в логику приложения. Например: логика приложения MyApplication. Методу showPopup() нужны только эти два данных, координата X и координата Y. Мы перепишем метод следующим образом:
// 好的写法
var MyApplication = {
handleClick: function (event) {
this.showPopup(event.clientX, event.clientY)
},
// 应用逻辑:显示弹出框
showPopup: function (x, y) {
var popup = document.getElementById('popup')
popup.style.left = x + 'px'
popup.style.top = y + 'px'
popup.className = 'reveal'
}
}
addListener(element, 'click', function(event) {
MyApplication.handleClick(event) // 可以这样用
})
В этом переписанном коде MyApplication.handleClick() передает координаты x и y в MyApplication. showPopup() вместо ранее переданного объекта события. Это делает MyApplication очень понятным. ожидается от showPopup() Передаваемые параметры и эта логика приложения могут быть легко вызваны непосредственно из любого места в тесте или коде. Например:
// 这样调用非常棒
MyApplication.showPopup(10, 10)
Рекомендуемая практика: обработчики событий используют объекты событий для обработки событий, логика приложения не должна полагаться на объекты событий для правильной работы.
2-3. Сделайте обработчик события единственной функцией, которая касается объекта события
Обработчик событий должен выполнять любые необходимые действия над объектом события перед входом в логику приложения, включая блокировку события или предотвращение всплытия события, должен быть включен непосредственно в обработчик события. Давайте снова перепишем приведенный выше код следующим образом:
// 好的写法
var MyApplication = {
handleClick: function (event) {
// 假设事件支持DOM Level2
event.preventDefault()
event.stopPropagation()
// 传入应用逻辑
this.showPopup(event.clientX, event.clientY)
},
// 应用逻辑:显示弹出框
showPopup: function (x, y) {
var popup = document.getElementById('popup')
popup.style.left = x + 'px'
popup.style.top = y + 'px'
popup.className = 'reveal'
}
}
addEventListener(element, 'click', function(event) {
MyApplication.handleClick(event) // 可以这样用
})
В этом коде MyApplication.handleClick является обработчиком событий, поэтому он вызывает event.preventDefault() и event.stopPropagation() перед передачей данных в логику приложения. Это ясно показывает разделение труда между обработчиками событий и логикой приложения, потому что логика приложения не должна зависеть от событий и, таким образом, может легко использовать одну и ту же бизнес-логику во многих местах, включая написание тестового кода.
Рекомендуемая практика: сделайте обработчик событий единственной функцией, которая касается объекта события.
2-4. Резюме в одном предложении
Отношения между обработкой событий и логикой применения в обработке событий являются независимыми и разделяющимися. Обработчик событий несет ответственность за обработку объекта события (не ограничиваясь заблокировать событие или предотвращение мероприятия от пузырька), а логика приложений отвечает за прием необходимых данных и не нужно зависит от события.
3. Извлечь данные конфигурации из кода
Определение: Данные конфигурации — это значения, жестко запрограммированные в приложении и в будущем могут быть изменены.
Общие данные конфигурации: URL-адреса, строки, которые необходимо отображать пользователю, повторяющиеся значения, настройки (например, элементы конфигурации на странице) и любые значения, которые могут измениться.
образец кода
// 将配置数据抽离出来
var config = {
MSG_INVALID_VALUE: 'Invalid value',
URL_INVALID: '/errors/invalid.php',
CSS_SELECTED: 'selected'
}
function validate (value) {
if (!value) {
alert (config.MSG_INVALID_VALUE)
location.href = config.URL_INVALID
}
}
function toggleSelected (element) {
if (hasClass(element, config, CSS_SELECTED} {
removeClass(element, config.CSS_SELECTED)
} else {
addClass(element, config.CSS_SELECTED)
}
В этом коде мы сохраняем данные конфигурации в объекте конфигурации. Каждое свойство объекта конфигурации содержит часть данных, и каждое имя свойства имеет префикс, указывающий тип данных (MSG означает информацию, отображаемую для пользователя, URL означает сетевой адрес, CSS означает, что это имя calss). Конечно, соглашение об именах является личным предпочтением. Самое важное в этом коде то, что все данные конфигурации удаляются из функции и заменяются заполнителями свойств в объекте конфигурации.
4. Не двигайтесь, если это не ваша цель
Помните, что если ваш код не создает эти объекты, не изменяйте их.Включение местных объектов (объект, массив и т. Д.), Объекты DOM (такие как документ), объекты BOM (такие как окно) и объекты класса библиотеки
4-1 Принципы
Перед лицом объектов, которыми мы не владеем, мы должны следовать следующим трем принципам.
не переопределять методы
// 不好的写法
document.getElementById = function () {
return null // 引起混论
}
нет нового метода
Array.prototype.reverseSort = functino () {
return this.sort().reverse()
}
Рекомендуемая практика: большинство библиотек JavaScript имеют механизм плагинов, который позволяет добавлять некоторые функции в кодовую базу. Если вы хотите изменить его, лучший и наиболее удобный способ — создать плагин
не удалять метод
// 不好的写法 -删除了Dom方法
document.getElementById = null
4-2 Лучший способ — расширение объектов с помощью наследования
В JavaScript наследование все еще имеет некоторые большие ограничения. Прежде всего, пока невозможно наследовать от объектов DOM или BOM. Во-вторых, наследование от Array не работает должным образом из-за сложной взаимосвязи между индексом массива и свойством длины.
4-2-1 Наследование на основе объектов, также часто называемое прототипным наследованием, реализовано с помощью метода Object.create() в ES5.
образец кода
var person = {
name: 'Nicholas',
sayName: function () {
alert(this.name)
}
}
var myPerson = Object.create(person)
myPerson.sayName = function () {
alert('Anonymous')
}
myPerson.sayName() // 弹出 'Anonymous' 重新定义myPerson.sayName会自动切断对person.sayName的访问
person.sayName() // 弹出'Nicholas'
К новому объекту будут добавлены свойства и методы второго параметра метода Object.create().
var person = {
name: 'Nicholas',
sayName: function () {
alert(this.name)
}
}
var myPerson = Object.create(person, {
name: {value:'Greg'}
})
myPerson.sayName() // 弹出 'Greg'
person.sayName() // 弹出'Nicholas'
Как только новый объект создан таким образом, новый объект можно полностью изменить. В конце концов, вы являетесь владельцем объекта и можете добавлять новые методы, перезаписывать существующие методы или даже удалять методы в своем собственном проекте.
Портал знаний: для более глубокого и поверхностного копирования знаний об объектах нажмитездесьСаморасширение
4-2-2 Наследование на основе типов
Наследование зависит от прототипа и реализуется через конструкторы.
образец кода
function MyError (message) {
this.message = message
}
MyError.prototype = new Error ()
В приведенном выше примере класс MyError наследуется от Error (так называемый суперкласс). MyError.prototype назначается экземпляру Error. Затем каждый экземпляр MyError наследует свои свойства и методы от Error, и instanceof тоже отлично работает.
function MyError (message) {
this.message = message
}
MyError.prototype = new Error ()
var error = new MyError('Something bad happened.')
console.log(error instanceof Error) // true
console.log(error instanceof MyError) // true
4-2-3 Фасадный режим
Фасадный паттерн — это популярный паттерн проектирования, который создает новый интерфейс для существующего объекта. Вы не можете наследовать от объекта DOM, поэтому единственный способ безопасного добавления к нему функциональности — это создание фасада. Ниже приведен пример кода оболочки объекта DOM.
function (element) {
this.element = element
}
DOMWrapper.prototype.addClass = function (className) {
element.className += '' + className
}
DOMWrapper.prototype.remove = function () {
this.element.parentNode.removeChild(this.element)
}
// 用法
var wrapper = new DOMWrapper(document.getElementById('my-div'))
// 添加一个className
wrapper = addClass('selected')
// 删除元素
wrapper.remove()
Тип DOMWrapper ожидает, что элемент DOM будет передан его конструктору. Элемент сохраняется для дальнейшего использования и определяет некоторые методы управления элементом. Метод addClass() предназначен для тех, кто еще не Простой способ добавить ClassName к элементу, который реализует атрибут classList HTML5. Метод remove() инкапсулирует операцию удаления элемента из DOM, ограждая разработчика от необходимости доступа к родительскому узлу элемента.
4-2-4 Три типа сравнения
С точки зрения удобства сопровождения JavaScript, фасады — очень удобный способ самостоятельного полного контроля над этими интерфейсами. Вы можете разрешить доступ к свойствам или методам любого базового объекта и наоборот, эффективно фильтруя доступ к этому объекту. Вы также можете изменить существующие методы, чтобы упростить их использование (приведенный выше пример кода является примером). Независимо от того, как изменяются базовые объекты, пока изменяется фасад, приложение может продолжать нормально работать. Фасад, который реализует определенный интерфейс, делающий один объект похожим на другой, называется адаптером. Единственная разница между фасадом и адаптером заключается в том, что первый создает новый интерфейс, а второй реализует существующий интерфейс.
4-3 Модификация блока
ES5 представил несколько методов для предотвращения модификации объектов. Блокировка этих объектов гарантирует, что никто не сможет преднамеренно или непреднамеренно изменить функциональные возможности, которые им не нужны.
4-3-1 Три уровня модификации замка
предотвратить расширение Запрещено «добавлять» свойства и методы к объектам, но существующие свойства и методы можно изменять или удалять.
тюлень Аналогичен «предотвратить расширение», но запрещает «удаление» существующих свойств и методов объекта.
заморозить Аналогичен «запечатыванию», но запрещает «модификацию» существующих свойств и методов объекта (все поля доступны только для чтения)
Каждый тип блокировки имеет два метода: один используется для реализации операции, а другой используется для определения того, применяется ли соответствующая операция.
4-3-2 Пример кода приложения
предотвратить расширение
var person = {
name: 'Nicholas'
}
// 锁定对象
Object.preventExtensions(person) // 实施可扩展
console.log(Object.isExtensible(person)) // false 检测一个对象是否是可扩展的
person.age = 25 // 正常情况下悄悄地失败,除非在strict模式下则会特意抛出错误提示
тюлень
var person = {
name: 'Nicholas'
}
// 锁定对象
Object.seal(person)
console.log(Object.isExtensible(person)) // false 检测一个对象是否是可扩展的
console.log(Object.isSealed(person)) // true 检测一个对象是否是密封的
delete person.name // 正常情况下悄悄地失败,除非在strict模式下抛出错误
person.age = 25 // 同上
console.log(person)
заморозить
var person = {
name: 'Nicholas'
}
// 锁定对象
Object.freeze(person)
console.log(Object.isExtensible(person)) // false 检测一个对象是否是可扩展的
console.log(Object.isSealed(person)) // true 检测一个对象是否是密封的
console.log(Object.isFrozen(person)) // true 检测一个对象是否是冻结
person.name = 'Greg' // 正常情况下悄悄地失败,除非在strict模式下抛出错误
person.age = 25 // 同上
delete person.name // 同上
console.log(person)
4-3-3. Резюме одним предложением
Использование этих методов в ES5 — отличный способ убедиться, что ваш проект не заблокирован для внесения изменений с вашего согласия. Если вы являетесь автором кодовой базы, вы, вероятно, захотите заблокировать части основной библиотеки, чтобы предотвратить их случайное изменение, или хотите принудительно Места, которым разрешено расширяться, продолжают выживать. Если вы разработчик приложения, заблокируйте все части приложения, которые вы не хотите изменять. В обоих случаях описанный выше метод можно использовать только после определения всех функций этих объектов. Когда объект заблокирован, его нельзя разблокировать.
5. Резюме
«Написание поддерживаемого JavaScript», первая часть стиля программирования, вдохновила меня: независимо от того, используем ли мы Vue или React, мы должны уделять больше внимания стилям программирования, перечисленным в официальных документах, прежде чем использовать фреймворк, который поможет нам Стандартизируйте структуру кода, это маленькая деталь, которую мы часто упускаем из виду. Вторая часть практики программирования, HTML, JS, CSS разделены и независимы друг от друга, чтобы поддерживать слабую связь; связь между обработчиками событий и логикой приложения в обработке событий независима и разделена; данные конфигурации извлекаются из кода; не перемещайте свои объекты; улучшение этих деталей очень полезно для улучшения обслуживания кода. Что касается третьей части автоматизированного тестирования, то она больше касается использования и установки системных инструментов вроде Ant и Ci. Вся книга здесь, и в будущем она будет больше посвящена применению в работе. Если вы считаете, что это полезно для вашего развития, вы можете поставить лайк и собрать его.Если я написал что-то не так, я надеюсь, что я могу указать на это. Если у вас есть лучшие идеи или предложения, вы можете поделиться ими со мной в разделе комментариев ниже. Все развиваются и растут вместе. Спасибо [поклон].
6. Общайтесь вместе
-
личныйрепозиторий github, приветствую всех звезд
-
Личная официальная учетная запись WeChat, интерфейс оплаты, подписка на официальную учетную запись WeChat yhzg_gz (нажмите, чтобы скопировать, добавьте официальную учетную запись в WeChat и вставьте ее)
пс: Совершенствуйся и заводи дружбу с противоположным полом