Статьи

Основные принципы поддерживаемого JavaScript

JavaScript это любопытный язык. Это легко написать, но трудно освоить. К концу этой статьи, мы надеемся, вы превратите свой код спагетти в обед из пяти блюд, полный читабельного, понятного аппетитного аппетита!


При написании JS-кода следует помнить, что это динамический язык. Это означает, что есть много способов сделать что-то. Вам не нужно иметь дело со строго типизированными классами или некоторыми более сложными функциями языков, такими как C # и Java. Это и благословение, и проклятие.

«Твердость» JavaScript отчетливо видна при рассмотрении следующего изображения:

Маленькая крошечная книга слева — это книга Дугласа Крокфорда, которую ДОЛЖЕН ПРОЧИТАТЬ, JavaScript: Хорошие части . Рядом с ним, справа, находится JavaScript «Полное руководство » Дэвида Фланагана.

Хотя обе эти книги отлично читаются, «Хорошие части» иллюстрируют, что, хотя в JavaScript много МОДА, хорошие части можно суммировать в значительно более коротком чтении. Так что, если вы ищете хорошее, быстрое чтение, прочитайте «Хорошие части» — и прочитайте его несколько раз!

Это, естественно, привело к большому количеству бессонных ночей для веб-разработчиков.

Вы можете прочитать статью об истории JavaScript здесь , но суть в том, что Брэндон Айх в 1995 году был нанят Netscape для разработки языка. Он придумал слабо типизированный язык, который мы знаем как JavaScript. С годами он стал «стандартизированным» как ECMAscript, но на протяжении всех войн браузеров различные браузеры реализовывали эти функции по-разному. Это, естественно, приведет к большому количеству бессонных ночей для веб-разработчиков. Эта проблема, в сочетании с тем фактом, что JavaScript считался наиболее применимым для манипулирования изображениями и выполнения быстрых проверок, привела к тому, что JavaScript неправильно стал рассматриваться как ужасный язык.

Пришло время это исправить! Хотя да, в JavaScript есть много плохих вещей, но при правильном использовании он может быть фантастическим языком — и его динамичная природа будет расти на вас!


Одним из недостатков реализации JavaScript является то, что он работает поверх глобального объекта. В случае браузеров это будет window объект. Таким образом, каждый раз, когда такой код присутствует на странице …

01
02
03
04
05
06
07
08
09
10
function doStuff(){
    alert(‘I am doing stuff’);
}
 
function doMoreStuff(){
   var images = document.images.length;
   console.log(«There are » + images + «on this page»);
}
doStuff();
doMoreStuff();

Функции doStuff и doMoreStuff сразу доступны для объекта глобального window .

Это означает, что если кто-нибудь придет и попытается написать функцию, которая также называется doStuff , возникнет конфликт! Все теги script в основном берут код внутри себя и запускают его в window в порядке, на который они ссылаются в HTML. В результате второй человек, реализующий doStuff , перезапишет первый doStuff .

Распространенная техника устранения этой проблемы — использование либо самозаполняющихся анонимных функций, либо пространств имен . Объектно-ориентированные люди, читающие это, вероятно, уже знакомы с концепцией пространства имен, но основная идея состоит в том, чтобы сгруппировать функции в разные области для повторного использования.

1
2
3
4
var NS = NS ||
NS.Utils = NS.Utils ||
NS.Models = NS.Models ||
NS.Views = NS.Views ||

Это предотвратит загрязнение глобального пространства имен и улучшит удобочитаемость вашего приложения. Теперь вы просто определяете функции в соответствующем пространстве имен. Обычно определяемое пространство имен — это app , которое управляет остальной частью приложения.

На каждом языке существует набор шаблонов дизайна. Адди Османи говорит …

Шаблоны проектирования — это повторно используемые решения часто возникающих проблем в разработке программного обеспечения.

Их много, и при правильном использовании они могут значительно повлиять на работоспособность вашего приложения. Адди написала замечательную книгу по шаблонам дизайна JavaScript, которая называется Essential Design Patterns . Абсолютно прочитай!

Другим часто используемым шаблоном является Шаблон модуля выявления .

01
02
03
04
05
06
07
08
09
10
11
12
13
NS.App = (function () {
    // Initialize the application
    var init = function () {
        NS.Utils.log(‘Application initialized…’);
    };
     
    // Return the public facing methods for the App
    return {
        init: init
    };
}());
 
NS.App.init();

Выше функция App определена внутри объекта NS . Внутри определяется переменная функции для init , которая возвращается как литерал анонимного объекта . Обратите внимание, что в конце есть дополнительный набор скобок: }()); , Это заставляет функцию NS.App автоматически выполняться и возвращаться. Теперь вы можете вызвать NS.App.init() для инициализации вашего приложения.

Приведенная выше анонимная функция является наилучшей практикой в ​​JavaScript и называется самоисполняющейся анонимной функцией . Поскольку функции в JavaScript имеют собственную область видимости — т.е. переменные, определенные внутри функций, не доступны вне их — это делает анонимные функции полезными несколькими способами.

01
02
03
04
05
06
07
08
09
10
11
12
// Wrap your code in a SEAF
(function (global) {
 
    // Now any variables you declare in here are unavailable outside.
    var somethingPrivate = ‘you cant get to me!’;
     
    global.somethingPublic = ‘but you can however get to me!’;
     
}(window));
 
console.log(window.somethingPublic);
console.log(somethingPrivate);

В этом примере, поскольку эта функция выполняется автоматически, вы можете передать window в исполняющую часть }(window)); и он будет доступен как global внутри анонимной функции. Эта практика ограничивает глобальные переменные на объекте window и поможет предотвратить конфликты имен.

Теперь вы можете начать использовать SEAF в других областях вашего приложения, чтобы сделать код более модульным. Это позволяет повторно использовать ваш код и способствует разделению задач.

Вот пример потенциального использования этих идей.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(function ($) {
    var welcomeMessage = ‘Welcome to this application!’
     
    NS.Views.WelcomeScreen = function () {
        this.welcome = $(‘#welcome’);
    };
     
    NS.Views.WelcomeScreen.prototype = {
        showWelcome: function () {
            this.welcome.html(welcomeMessage)
                .show();
        }
    };
}(jQuery));
 
$(function () {
    NS.App.init();
});
 
// Modify the App.init above
var init = function () {
    NS.Utils.log(‘Application initialized…’);
    this.welcome = new NS.Views.WelcomeScreen();
    this.welcome.showWelcome();
};

Итак, выше, происходит несколько разных вещей. Во-первых, jQuery передается в качестве аргумента анонимной функции. Это гарантирует, что $ на самом деле является jQuery внутри анонимной функции.

Затем есть приватная переменная, называемая welcomeMessage , и функция назначается NS.Views.WelcomeScreen . Внутри этой функции this.welcome назначается селектору jQuery DOM. Это кэширует селектор внутри welcomeScreen , так что jQuery не должен запрашивать DOM для него более одного раза.

DOM-запросы могут занимать много памяти, поэтому убедитесь, что вы кэшируете их как можно больше.

Далее мы оборачиваем init приложения в $(function(){}); , что то же самое, что и $(document).ready() .

Наконец, мы добавляем код в инициализатор приложения. Это сделает ваш код красивым и отделенным, и к нему будет легко вернуться и изменить его позже. Больше ремонтопригодности!

Другим отличным шаблоном является Шаблон наблюдателя, который иногда называют «Pubsub». По сути, Pubsub позволяет нам подписываться на события DOM, такие как click и mouseover . С одной стороны, мы слушаем эти события, а с другой, что-то публикует эти события — например, когда браузер публикует (или объявляет), что кто-то нажал на конкретный элемент. Для pubsub есть много библиотек, так как это небольшой кусочек кода. Выполните быстрый поиск в Google, и тысячи вариантов сделают себя доступными. Одним из надежных решений является реализация AmplifyJS .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
// A data model for retrieving news.
NS.Models.News = (function () {
    var newsUrl = ‘/news/’
     
    // Retrieve the news
    var getNews = function () {
        $.ajax({
            url: newsUrl
            type: ‘get’,
            success: newsRetrieved
        });
    };
     
    var newsRetrieved = function (news) {
        // Publish the retrieval of the news
        amplify.publish(‘news-retrieved’, news);
    };
     
    return {
        getNews: getNews
    };
}());

Этот код определяет модель для получения новостей от какого-либо сервиса. Как только новости были получены с помощью AJAX, метод newsRetrieved срабатывает, передавая полученные новости в Amplify, и публикуется в теме новостей.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
(function () {
    // Create a news views.
    NS.Views.News = function () {
        this.news = $(‘#news’);
         
        // Subscribe to the news retrieval event.
        amplify.subscribe(‘news-retrieved’, $.proxy(this.showNews));
    };
     
    // Show the news when it arrives
    NS.Views.News.prototype.showNews = function (news) {
        var self = this;
        $.each(news, function (article) {
            self.append(article);
        });
    };
}());

Этот код выше представляет собой представление для отображения полученных новостей. В конструкторе News Amplify подписывается на тему получения новостей. Когда эта тема публикуется, функция showNews запускается, соответственно. Затем новость добавляется в DOM.

01
02
03
04
05
06
07
08
09
10
11
// Modify this the App.init above
var init = function () {
    NS.Utils.log(‘Application initialized…’);
    this.welcome = new NS.Views.WelcomeScreen();
    this.welcome.showWelcome();
     
    this.news = new NS.Views.News();
     
    // Go get the news!
    NS.Models.News.getNews();
};

Опять же, измените функцию init из приложения, чтобы добавить поиск новостей … и все готово! Теперь есть отдельные части приложения, каждый из которых отвечает за одно действие. Это известно как принцип единой ответственности .


Одним из ключей к поддерживаемому коду любого вида — не только JS — является документирование и комментирование . Комментарии могут быть неоценимыми для новых разработчиков, приходящих в проект — нуждающихся в понимании того, что происходит в коде. «Почему я снова написал эту строку?». Отличный инструмент для создания документации называется Docco . Это тот же инструмент, который генерирует документацию для веб-сайта Backbone.js . По сути, он берет ваши комментарии и размещает их рядом с вашим кодом.

Есть также инструменты, такие как JSDoc , которые генерируют документацию стиля API, описывающую каждый класс в вашем коде.

Другая вещь, которая может оказаться сложной при запуске нового проекта, — это попытаться определить, как лучше организовать ваш код. Одним из способов является разделение частей функциональности на отдельные папки. Например:

  • /app.js
  • /libs/jquery.js
  • /libs/jquery-ui.js
  • /users/user.js
  • /views/home.js

Эта структура помогает отделить части функциональности друг от друга. Есть, конечно, несколько способов организовать код, но все, что действительно имеет значение, — это выбрать структуру … и затем переходить с ней. Далее вы можете использовать инструмент сборки и минификации. Есть много вариантов:

Эти инструменты удаляют пробелы, удаляют комментарии и объединяют все указанные файлы в один. Это уменьшает размеры файлов и HTTP-запросы для приложения. Более того, это означает, что вы можете хранить все файлы отдельно во время разработки, но объединять их для производства.


Определение асинхронного модуля — это другой способ написания кода JavaScript.

Определение асинхронного модуля — это другой способ написания кода JavaScript; он делит весь код на отдельные модули. AMD создает стандартный шаблон для написания этих модулей для асинхронной загрузки кода.

Использование тегов script блокирует страницу, так как она загружается, пока DOM не будет готов. Следовательно, использование чего-то вроде AMD позволит DOM продолжить загрузку, пока скрипты все еще загружаются. По сути, каждый модуль делится на свой собственный файл, а затем есть один файл, который запускает процесс. Самая популярная реализация AMD — это RequireJS .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// main.js
require([‘libs/jquery’,’app.js’], function ($, app) {
    $(function () {
        app.init();
    });
});
 
// app.js
define([‘libs/jquery’, ‘views/home’], function ($, home) {
    home.showWelcome();
});
 
// home.js
define([‘libs/jquery’], function ($) {
    var home = function () {
        this.home = $(‘#home’);
    };
     
    home.prototype.showWelcome = function () {
        this.home.html(‘Welcome!’);
    };
     
    return new home();
});

В приведенном выше фрагменте кода есть файл main.js , с которого начинается процесс. Первый аргумент функции require — это массив зависимостей. Эти зависимости представляют собой список файлов, необходимых для app.js Когда они заканчивают загрузку, все, что возвращает модуль, передается в качестве аргумента обратному вызову функции справа.

Затем есть app.js , который требует jQuery, а также представление. Далее для представления home.js требуется только jQuery. Он имеет функцию home и возвращает экземпляр самого себя. В вашем приложении все эти модули хранятся в отдельных файлах, что делает ваше приложение легко обслуживаемым.


Обеспечение поддержки ваших приложений чрезвычайно важно для разработки. Это уменьшает количество ошибок и облегчает процесс исправления найденных вами ошибок.

«Друзья не позволяют друзьям писать код для спагетти!»