Статьи

20 слишком часто встречающихся ошибок для начинающих

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

Учитесь на наших ошибках; не делай этих вещей!


DOM медленный. Ограничение вашего взаимодействия с ним значительно повысит производительность вашего кода. Рассмотрим следующий (плохой) код:

Этот код фактически модифицирует DOM 100 раз и без необходимости создает 100 объектов jQuery. 100! Более правильный подход — использовать фрагмент документа или создать строку, содержащую 100 элементов <li/> , а затем добавить этот HTML-код к содержащему элементу. Таким образом, вы прыгаете в DOM всего один раз. Вот пример:

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

При создании больших строк хранение каждого фрагмента строки как элемента в элементе массива и вызов метода join() , возможно, более элегантен, чем конкатенация строк. Это один из самых быстрых и простых способов создания повторяющегося HTML в JavaScript без использования библиотеки шаблонов или фреймворка.

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

Не имеет смысла добавлять другую переменную с именем Something . Это вносит несоответствие в ваш шаблон именования переменных, заставляя ваш мозг когнитивно пометить эту переменную как отличную или особую. Вот почему константы в большинстве языков традиционно определяются всеми заглавными буквами.

Вы можете сделать еще один шаг вперед, сохранив аналогичную длину, грамматическую структуру и пояснительный характер при именовании функций. Например, рассмотрим следующую надуманную функцию:

Присвоение имени функции, которая добавляет пять к данному числу, должно следовать той же схеме, показанной здесь:

Иногда вы можете назвать функцию, чтобы указать ее возвращаемое значение. Например, вы можете назвать функцию, которая возвращает HTML-строку getTweetHTML() . Вы также можете добавить имя функции с помощью do , если функция просто выполняет операцию и не возвращает значение, например: doFetchTweets() .

Функции конструктора обычно следуют традиции классов на других языках, с заглавной буквы:

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

Массивы JavaScript не являются ассоциативными; попытки использовать их как таковые осуждаются сообществом. С другой стороны, объекты можно рассматривать как хеш-таблицы, и вы можете перебирать свойства объекта, используя цикл for...in , например:

Проблема, однако, заключается в том, что цикл for...in выполняет итерации по каждому перечисляемому свойству в цепочке прототипов объекта. Это может быть проблематично, если вы хотите использовать только те свойства, которые существуют на реальном объекте.

Вы можете решить эту проблему с помощью hasOwnProperty() . Вот пример:

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

Сравнение логических значений в условии является пустой тратой времени вычислений. Для примера рассмотрим следующее:

Обратите внимание на условие: foo == true . Сравнение foo и true не нужно, потому что foo уже является логическим значением (или является истинным или ложным). Вместо того, чтобы сравнивать foo , просто используйте его как условие, например так:

Чтобы проверить на false , используйте логический оператор NOT, как показано ниже:

События — сложная тема в JavaScript. Прошли времена встроенных обработчиков событий onclick (за исключением некоторых очень редких случаев «страницы-заставки»). Вместо этого используйте всплытие событий и делегирование.

Давайте представим, что у вас есть сетка картинок, которая должна запустить модальное окно лайтбокса. Вот что ты не должен делать. Примечание: мы используем jQuery здесь, при условии, что вы используете подобную библиотеку. Если нет, то те же принципы всплытия применимы и к ванильному JavaScript.

Соответствующий HTML:

1
2
3
4
5
6
<div id=»grid-container»>
    <a href=»someimage.jpg»><img src=»someimage-thumb.jpg»></a>
    <a href=»someimage.jpg»><img src=»someimage-thumb.jpg»></a>
    <a href=»someimage.jpg»><img src=»someimage-thumb.jpg»></a>
    …
</div>

(Плохой) JavaScript:

В этом коде предполагается, что вызов лайтбокса включает передачу элемента привязки, который ссылается на полноразмерное изображение. Вместо привязки к каждому элементу привязки, вместо этого привязывайте к элементу #grid-container .

В этом коде и this и event.target ссылаются на элемент привязки. Вы можете использовать эту же технику с любым родительским элементом. Просто убедитесь, что вы определили элемент, который должен быть целью события.

Чрезмерное использование троичных операторов довольно распространено как в JavaScript, так и в PHP.

1
2
// php
return (something()) ?

Выражение условия всегда возвращает значение true или false , что означает, что вам не нужно явно добавлять true / false качестве троичных значений. Вместо этого вы можете просто вернуть условие:

1
2
// php
return something();

операторы if...else являются центральной частью большинства языков. Но если вы делаете что-то простое, например, присваиваете значение переменной на основе условия — они могут испортить ваш код. Рассмотрим следующий код:

1
2
3
4
5
6
7
8
if ($greeting)
{
    $post->message = ‘Hello’;
}
else
{
    $post->message = ‘Goodbye’;
}

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

1
$post->message = $greeting ?

Это ясно, кратко и дает вам необходимую функциональность.

Как бы ни был полезен троичный оператор, самое важное правило — не злоупотреблять им! Цель кодирования — не свести вашу логику к как можно меньшему количеству строк.

Посмотрим правде в глаза: многие уровни вложенности уродливы и их трудно поддерживать / читать. Следующий код является относительно упрощенным примером, но со временем они становятся намного хуже:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// anti-pattern
$error_message = null;
 
if ($this->form_validation->run())
{
    if ($this->upload->do_upload())
    {
        $image = $this->upload->get_info();
 
        if ( ! $this->image->create_thumbnail($image[‘file_name’], 300, 150))
        {
            $error_message = ‘There was an error creating the thumbnail.’;
        }
    }
    else
    {
        $error_message = ‘There was an error uploading the image.’;
    }
}
else
{
    $error_message = $this->form_validation->error_string();
}
 
// Show error messages
if ($error_message !== null)
{
    $this->load->view(‘form’, array(
        ‘error’ => $error_message,
    ));
}
 
// Save the page
else
{
    $some_data[‘image’] = $image[‘file_name’];
 
    $this->some_model->save($some_data);
}

Это какой-то неприятный код, но вы можете сделать его значительно чище, используя исключения, например:

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
26
27
28
29
30
31
32
33
34
35
try
{
    if ( ! $this->form_validation->run())
    {
        throw new Exception($this->form_validation->error_string());
    }
 
    if ( ! $this->upload->do_upload())
    {
        throw new Exception(‘There was an error uploading the image.’);
    }
 
    $image = $this->upload->get_info();
 
    if ( ! $this->image->create_thumbnail($image[‘file_name’], 300, 150))
    {
        throw new Exception(‘There was an error creating the thumbnail.’);
    }
}
 
// Show error messages
catch (Exception $e)
{
    $this->load->view(‘form’, array(
        ‘error’ => $e->getMessage(),
    ));
 
    // Stop method execution with return, or use exit
    return;
}
 
// Got this far, must not have any trouble
$some_data[‘image’] = $image[‘file_name’];
 
$this->some_model->save($some_data);

Это может быть то же количество строк, но оно позволяет значительно более читаемый и поддерживаемый код. Это также позволяет избежать тех сложных сессий отладки, когда вы пропустили возможный путь через оператор if . Будь проще!

Второе мнение: будьте очень, очень осторожны при использовании исключений для управления потоком. Обратитесь сюда для получения дополнительной информации.

Быть счастливым исключением гораздо выгоднее, чем быть ложным.

Разработчики Ruby или Python привыкли наблюдать за тривиальными исключениями. Хотя это звучит скучно, на самом деле это очень хорошая вещь. Если что-то идет не так, выдается исключение, и вы сразу же знаете, в чем проблема.

В PHP — и особенно при использовании более старых фреймворков, таких как CodeIgniter, — вы получаете то, что я называю «ложно-счастливым кодом» (в отличие от «радостных исключений»). Вместо того, чтобы у вас возникло исключение, он просто возвращает false значение и назначает строку ошибки какому-либо другому свойству. Это вынуждает вас get_error(); это из класса, используя get_error(); метод.

Быть счастливым исключением гораздо выгоднее, чем быть ложным. Если в вашем коде произошла ошибка (например: не удалось подключиться к S3 для загрузки изображения, или значение пустое и т. Д.), То выведите исключение. Вы также можете выдавать определенные типы исключений, расширяя класс Exception , например так:

1
class CustomException extends Exception {}

Создание пользовательского исключения значительно облегчает отладку.

Обычно операторы if используются для контроля пути выполнения функции или метода. Заманчиво проверить условие и выполнить много кода, когда условие приводит к true , только для того, чтобы просто вернуться в оператор else . Например:

1
2
3
4
5
6
7
8
function someFunction($param) {
    if ($param == ‘OK’) {
       $this->doSomething();
       return true;
    } else {
       return false;
    }
}

Такое решение, однако, представляет потенциал для спагетти-кода. Вы можете сделать этот код проще для чтения, изменив условие. Вот лучшая версия:

1
2
3
4
5
6
function someFunction($param) {
    if ($param != ‘OK’) return false;
 
    $this->doSomething();
    return true;
}

Разве это не легче читать? Это простое изменение, которое существенно меняет читаемость вашего кода.

Цикл for обычно используется, когда вам нужен, например, счетчик. Вот простой цикл:

1
2
3
for (var i = 0; i < x; i++) {
    …
}

Есть несколько очень веских причин использовать цикл for , но цикл while может быть лучше, если вам просто нужно что-то простое, например:

1
2
3
4
5
var i = x;
 
while (i—) {
    …
}

Это не работает в любой ситуации, но это альтернатива.

Это легко одна из самых частых ошибок новичков.

Метод — это единица работы объекта, и ограничение ваших методов до поддерживаемого размера облегчает чтение и обслуживание вашего кода. Взгляните на следующий метод монстров:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class SomeClass {
 
    function monsterMethod() {
        if($weArePilots) {
            $this->goAndDressUp();
            $this->washYourTeeth();
            $this->cleanYourWeapon();
            $this->takeYourHelmet();
            if($this->helmetDoesNotFit())
                $this->takeAHat();
            else
                $this->installHelmet();
            $this->chekcYourKnife();
            if($this->myAirplain() == «F22»)
                $this->goToArmyAirport();
            else
                $this->goToCivilianAirport();
            $this->aim();
            $this->prepare();
            $this->fire();
        }
    }
 
}

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

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class SomeClass {
 
    function monsterMethod() {
        if($weArePilots) {
            $this->prepareYourself();
            $this->tryHelmet();
            $this->findYourAirport();
            $this->fightEnemy();
        }
    }
 
    private function prepareYourself() {
        $this->goAndDressUp();
        $this->washYourTeeth();
        $this->cleanYourWeapon();
        $this->chekcYourKnife();
    }
 
    private function tryHelmet() {
        $this->takeYourHelmet();
        if($this->helmetDoesNotFit())
            $this->takeAHat();
        else
            $this->installHelmet();
    }
 
    private function findYourAirport() {
        if($this->myAirplain() == «F22»)
            $this->goToArmyAirport();
        else
            $this->goToCivilianAirport();
    }
 
    private function fightEnemy() {
        $this->aim();
        $this->prepare();
        $this->fire();
    }
 
}

Там мы идем: чище и проще для отладки!

Слишком много уровней вложенности делает код трудным для чтения и обслуживания. Учтите следующее:

01
02
03
04
05
06
07
08
09
10
11
function doSomething() {
    if ($someCondition) {
        if ($someOtherCondition) {
            if ($yetSomeOtherCondition) {
                doSomethingSpecial();
            }
 
            doSomethingElse();
        }
    }
}

Вы можете обратиться к Совету № 10, чтобы упростить чтение этого кода, изменив некоторые условия.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
function doSomething() {
    if (!$someCondition) {
        return false;
    }
 
    if (!$someOtherCondition) {
        return false;
    }
 
    if ($yetSomeOtherCondition) {
        doSomethingSpecial();
    }
 
    doSomethingElse();
}

Этот код значительно чище и дает те же результаты, что и раньше.

Когда вы обнаружите себя с вложенными операторами if , внимательно изучите ваш код; Ваш метод может выполнять более одной задачи. Вот пример:

1
2
3
4
5
6
7
function someFunc() {
    if($oneThing) {
        $this->doSomething();
        if($anotherThing)
            $this->doSomethingElse();
    }
}

В этих случаях извлеките вложенные методы в их собственный метод:

01
02
03
04
05
06
07
08
09
10
11
function someFunc() {
    if($oneThing) {
        $this->doSomething();
        $this->doAnotherThing($anotherThing);
    }
}
 
private doAnotherThing($anotherThing) {
    if($anotherThing)
        $this->doSomethingElse();
}

Магические числа и строки злы. Определите переменные или константы со значениями, которые вы хотите использовать в своем коде.

Вместо этого:

1
2
3
4
5
function someFunct() {
    $this->order->set(23);
    $this->order->addProduct(‘superComputer’);
    $this->shoppingList->add(‘superComputer’);
}

Укажите, что означают эти числа и строки, и присвойте их переменной со значимым именем, например так:

1
2
3
4
5
6
7
8
function someFunct() {
    $orderId = 23;
    $selectedProductName = ‘superComputer’;
 
    $this->order->set($orderId);
    $this->order->addProduct($selectedProductName);
    $this->shoppingList->add($selectedProductName);
}

Хотя некоторые могут утверждать, что мы без необходимости создаем переменные, снижение производительности незначительно. Читаемость всегда имеет приоритет. Помните: не оптимизируйте производительность, пока не сможете описать, почему это необходимо.

Используйте встроенные функции массива вместо foreach() .

Не идеально:

1
2
3
foreach (&$myArray as $key =>$element) {
   if ($element > 5) unset ($myArray[$key]);
}

Лучше:

1
$myArray = array_filter($myArray, function ($element) { return $element <= 5;});

PHP предлагает множество методов массива. Сначала они сбивают с толку, но потратьте день и постарайтесь выучить как можно больше.

Переменные легко использовать, но помните, что переменные хранятся в памяти. Для каждой создаваемой переменной система должна выделить память для этой переменной. Посмотрите на этот код:

1
2
3
4
5
public function get_posts() {
    $query = $this->db->get(‘posts’);
    $result = $query->result();
    return $result;
}

Переменная $result не обязательна. Следующий код пропускает эту переменную:

1
2
3
4
public function get_posts() {
    $query = $this->db->get(‘posts’);
    return $query->result();
}

Разница невелика, но мы смогли улучшить этот простой пример. Мы сохранили переменную $ query, потому что она относится к базе данных, а $ result больше относится к нашей логике.


Все, что меньше, — это запах кода.

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

Например, вы можете избежать избыточных запросов к базе данных при многих обстоятельствах. Большинство сценариев управления пользователями по принципу «включай и работай» используют два запроса для регистрации пользователей: один для проверки того, существует ли уже электронная почта / имя пользователя, а другой для фактического добавления его в базу данных. Гораздо лучший подход — установить для поля имени пользователя значение UNIQUE . Затем вы можете использовать встроенные функции MySQL, чтобы проверить, была ли запись добавлена ​​в базу данных.

Дни именования ваших переменных x , y , z прошли (если, конечно, вы не имеете дело с системой координат). Переменная представляет важную часть вашей логики. Не хотите вводить длинное имя? Получите лучшую IDE. Современные среды IDE автоматически заполняют имена переменных в мгновение ока.

Всегда будь кодирующим в течение шести месяцев. Вы уверены, что вспомните, к чему эти переменные $sut относятся через год? Вероятно, нет: быть описательным. Все, что меньше, — это запах кода.

Случаются ошибки; ключ должен учиться у них.

Назовите ваши методы с глаголами, представляющими действия, которые они выполняют Основная концепция является полной противоположностью схемы именования переменных. Используйте короткое, но описательное имя в большой области (т. Е. Публичные методы), и используйте более короткое и более подробное имя в короткой области (т. Е. Частные / защищенные методы). Это помогает сделать ваш код читаемым как хорошо написанную прозу.

Также избегайте любых языков, кроме английского, когда называете ваши методы. Досадно читать имена функций, такие как 做些 什麼 () или делатьчтото () в вашем проекте. Для других программистов может быть невозможным понять ваше намерение. Хотя это может показаться высокомерным, в любом случае английский язык является принятым языком кода. Попробуйте использовать его, если мы работаем в большой команде.

Наконец, структура кода так же важна для удобства чтения и сопровождения, как и все, о чем мы говорили сегодня. Вот две рекомендации:

  • Отступ с четырьмя или двумя вкладками ширины. Что-то большее, например, восемь пробелов, слишком много и сделает ваш код трудным для чтения.
  • Установите разумную ширину линии и соблюдайте ее. Сорок символов в строке? Мы больше не в 70-х; установите ограничение в 120 символов, отметьте на экране и заставьте себя или свою IDE соблюдать этот предел. 120 символов дают вам хорошую ширину без прокрутки.

«Я никогда не делал глупую ошибку в программировании». — Никто никогда.

Случаются ошибки; ключ должен учиться у них. Мы в Nettuts + допустили и будем совершать ошибки. Мы надеемся, что вы учитесь на наших ошибках, чтобы избежать их в будущем. Но, честно говоря, лучший способ изучить лучшие практики — это делать ошибки самостоятельно!

Спасибо за прочтение!