Статические анализаторы JavaScript и ошибки, от которых они помогут отучиться (Часть 1)

в 21:18, , рубрики: javascript, анализаторы, Блог компании Paysto, метки:

Далеко не каждая строка моего кода получается идеальной с первого же раза. Ну, в некоторых случаях… Иногда… Ладно – практически никогда. Правда заключается в том, что я трачу значительно больше времени на исправление своих собственных глупых ошибок, чем мне хотелось бы. Именно поэтому я использую статические анализаторы практически в каждом написанном мной файле JavaScript.

Статические анализаторы просматривают код и находят в нем ошибки, прежде чем вы его запустите. Они выполняют простые проверки, например, проверку синтаксиса принудительного исполнения (например, наличия табуляции вместо пробелов) и более глобальные проверки, такие как проверка того, чтобы функции не были слишком сложными. Статические анализаторы также ищут ошибки, которые невозможно найти в процессе тестирования, например, == вместо ===.

В больших проектах и при работе в больших командах вам не помешает небольшая помощь в поиске таких «простых» багов, которые на самом деле оказываются не такими простыми, как кажутся.

JSLint, JSHint и Closure Compiler

Есть три основных варианта статических анализаторов для JavaScript: JSLint, JSHint и Closure Compiler.

JSLINT

JSLint был первым статическим анализатором для JavaScript. Его можно запустить на официальном сайте или использовать одну из надстроек, которые можно запускать в локальных файлах. JSLint находит много важных ошибок, но он очень жесткий. Вот яркий пример:

var s = 'mystring';
for (var i = 0; i < s.length; i++) {
 console.log(s.charAt(i));
}

JSLint показывает в этом коде две ошибки:

Unexpected '++'.
Move 'var' declarations to the top of the function.

Первая проблема – это определение переменной i в условиях цикла. JSLint также не принимает оператор ++ в конце определения цикла. Он хочет, чтобы код выглядел следующим образом:

var s = 'mystring';
var i;
for (i = 0; i < s.length; i = i + 1) {
 console.log(s.charAt(i));
}

Я ценю создателей JSLint, но как по мне – это перебор. Он оказался жестким и для Антона Ковалева, поэтому он создал JSHint.

JSHINT

JSHint работает так же, как и JSLint, но он написан в дополнение к Node.js, а потому он более гибкий. JSHint включает большое количество опций, что позволяет выполнять пользовательские проверки путем написания своего собственного генератора отчетов.
Запустить JSHint можно с сайта, но в большинстве случаев лучше установить JSHint в качестве локального инструмента командной строки с помощью Node.js. Как только вы установите JSHint, его можно запустить в ваших файлах с помощью такой команды:

jshint test.js

JSHint также включает плагины для популярных текстовых редакторов, поэтому его можно запускать в процессе написания кода.

CLOSURE COMPILER

Closure Compiler от Google – это совсем другая разновидность программы. Как предполагает его название, он представляет собой не только программу для проверки, но и компилятор. Он написан на Java и основан на анализаторе Rhino от Mozilla. Closure Compiler включает простой режим для выполнения базовой проверки кода, и более сложные режимы, позволяющие выполнять дополнительную проверку и обеспечивать соблюдение определений отдельных видов.

Closure Compiler сообщает об ошибках в коде JavaScript, но также создает минимизированные версии JavaScript. Компилятор удаляет пустое пространство, комментарии и неиспользуемые переменные и упрощает длинные выражения, делая скрипт максимально компактным.

Google сделал очень простую версию компилятора, доступную в сети, но скорее всего, вы захотите скачать Closure Compiler и запустить его локально.

Closure Compiler после проверки кода выводит список файлов в один минимизированный файл. Таким образом, вы можете запустить его, скачав файл compiler.jar.

java -jar compiler.jar --js_output_file compress.js --js test1.js --js test2.js

Выбираем правильную программу проверки

В своих проектах я комбинирую Closure Compiler и JSHint. Closure Compiler выполняет минимизацию и базовую проверку, в то время как JSHint проводит более сложный анализ кода. Эти две программы отлично работают вместе, и каждая из них охватывает те области, которые не может охватить другая. Кроме того, я могу использовать возможности расширения JSHint, чтобы писать пользовательские программы проверки. Одна написанная мной общая программа проверяет определенные функции, которые мне не нужны, например вызов функций, которых не должно быть в моем проекте.

Теперь, когда мы рассмотрели несколько программ для проверки, давайте разберем немного плохого кода. Каждый из этих шести примеров представляет собой код, который не стоит писать, и ситуации, в которых программы проверки кода могут спасти вас.

В этой статье для большинства примеров используется JSHint, но Closure Compiler обычно выдает похожие предупреждения.

== или ===?

JavaScript – это язык с динамической типизацией. Вам не нужно определять типы в процессе написания кода, при этом они существуют при запуске.

JavaScript предлагает два оператора сравнения для управления такими динамическими типами: == и ===. Давайте рассмотрим это на примере.

var n = 123;
var s = '123';

if (n == s) {
 alert('Переменные равны');
}

if (n === s) {
 alert('Переменные идентичны');
}

Оператор сравнения == — это остатки языка С, в который JavaScript уходит корнями. Его использование практически всегда является ошибкой: сравнивание значений отдельно от типов редко является тем, что разработчик на самом деле хочет сделать. На самом деле, число «сто двадцать три» отличается от строки «один два три». Эти операторы легко неправильно написать и еще легче неправильно прочесть. Проверьте этот код с помощью JSHint и вы получите следующее:

test.js: line 9, col 12, Expected '===' and instead saw '=='.

Неопределенные переменные и поздние определения

Давайте начнем с простого кода:

function test() {
 var myVar = 'Hello, World';
 console.log(myvar);
}

Видите баг? Я совершаю эту ошибку каждый раз. Запустите этот код, и вы получите ошибку:

ReferenceError: myvar is not defined

Давайте сделаем проблему немного более сложной:

function test() {
 myVar = 'Hello, World';
 console.log(myVar);
}

Запустите этот код, и вы получите следующее:

Hello, World

Этот второй пример работает, но у него есть очень неожиданные побочные действия. Правила для определения переменных JavaScript и области видимости оказываются, в лучшем случае, запутанными. В первом случае JSHint сообщит следующее:

test.js: line 3, col 17, 'myvar' is not defined.

Во втором случае он сообщит такое:

test.js: line 2, col 5, 'myVar' is not defined.
test.js: line 3, col 17, 'myVar' is not defined.

Первый пример поможет вам избежать ошибки времени выполнения программы. Вам не нужно тестировать свое приложение — JSHint найдет ошибку за вас. Второй пример хуже, так как в результате тестирования вы не найдете баг.

Проблема второго примера коварно незаметная и сложная. Переменная myVar теперь исчезла из своей области видимости и поднялась в глобальную область. Это означает, что она будет существовать и иметь значение Hello, World даже после запуска функции test. Это называется «загрязнение глобальной области видимости».

Переменная myVar будет существовать для каждой другой функции, которая будет запущена после функции test. Запустите следующий код после того, как выполните функцию test:

console.log('myVar: ' + myVar);

Вы все равно получите Hello, World. Переменная myVar будет висеть по всему вашему коду как шаблон, который приводит к сложным багам, которые вы будете искать всю ночь перед релизом, а все потому, что вы забыли вписать var.

Автор: Irina_Ua

Источник

Поделиться