Дек
7
2012

15 основных ошибок новичков в программировании

preview0-180x160

Независимо от нашего уровня мастерства, все мы когда-то были новичками. И все мы время от времени можем допустить глупую ошибку. Сегодня мы попросили авторов Nettuts+ поделиться их списками ошибок и их решениями, в различных языках.

Учитесь на наших ошибках; и не повторяйте их!



Советы по JavaScript

1 — Ненужные манипуляции с DOM

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

// anti-pattern
for (var i = 0; i < 100; i++){
	var li = $("<li>").html("Это элемент списка №" + (i+1));
	$("#someUL").append(li);
}

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

var liststring = "";
for (var i = 100; i > 0; i--){
	liststring += "<li>Это элемент списка №" + (99- i);
}
document.getElementById("someUL").innerHTML(liststring);

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

var liststring = "<li>"
var lis = [];
for (var i = 100; i > 0; i--){
	lis.push("This is list item #" + (99- i));
}
liststring += lis.join("</li><li>") + "</li>";
document.getElementById("someUL").innerHTML(liststring);

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

2 — Нелогичные имена Переменных и Функций в JavaScript

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

var foo = "bar";
var plant = "green";
var car = "red";

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

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

function subtractFive(number){
	return number - 5;
}

Называя функцию, которая прибавляет к числу пять и возвращает результат используем тот же шаблон, и получим:

function addFive(number){
	return number + 5;
}

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

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

function Dog(color){
	this.color = color;
}

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

3 — Использование hasOwnProperty() в циклах for...in

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

for (var prop in someObject) {
    alert(someObject[prop]); // выдаст значение свойства
}

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

Можно решить эту проблему используя метод hasOwnProperty(). Вот пример:

for (var prop in someObject) {
    if (someObject.hasOwnProperty(prop)) {
        alert(someObject[prop]); // выдаст значение свойства
    }
}

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

4 — Сравнение булевых переменных

Сравнение булевых переменных это пустая трата вычислительного времени. Рассмотрим это на следующем примере:

if (foo == true) {
    // делаем что-нибудь если истина
} else {
    // делаем что-нибудь если ложь
}

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

if (foo) {
    // делаем что-нибудь если истина
} else {
    // делаем что-нибудь если ложь
}

Для значения false, используем логический оператор NOT (НЕ), как показано ниже:

if (!foo) {
    // делаем что-нибудь если ложь
} else {
    // делаем что-нибудь если истина
}

5 — Назначение событий

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

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

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

<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 (плохой):

$('a').on('click', function() {
	callLightbox(this);
});

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

$("#grid-container").on("click", "a", function(event) {
	callLightbox(event.target);
});

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

6 — Тернарная избыточность

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

// javascript
return foo.toString() !== "" ? true : false;
// php
return (something()) ? true : false;

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

// javascript
return foo.toString() !== "";
// php
return something();

Советы по PHP

7 — Используйте тернарный оператор при необходимости

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

if ($greeting)
{
    $post->message = 'Hello';
}
else
{
    $post->message = 'Goodbye';
}

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

$post->message = $greeting ? 'Hello' : 'Goodbye';

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

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

8 — Используйте защищенные классы

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

function someFunction($param) {
    if ($param == 'OK') {
       $this->doSomething();
       return true;
    } else {
       return false;
    }
}

Однако, такое решение может сильно усложнить читаемость вашего кода. Вы можете улучшить этот код всего лишь инвертировав условие. Вот улучшенная версия:

function someFunction($param) {
    if ($param != 'OK') return false;
    $this->doSomething();
    return true;
}

Легче читается, не правда ли? Это простое изменение, которое делает резкое различие в читабельности кода.

9 — Поддерживайте понятность методов

Это одна из наиболее распространенных ошибок у новичков.

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

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();
		}
	}
}

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

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();
	}
}

И вуаля наш код стал чище и легче для отладки!

10 — Избегайте большой вложенности

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

function doSomething() {
    if ($someCondition) {
        if ($someOtherCondition) {
            if ($yetSomeOtherCondition) {
                doSomethingSpecial();
            }
            doSomethingElse();
        }
    }
}

Используя ранее озвученный совет, "развернем" некоторые условия.

function doSomething() {
    if (!$someCondition) {
        return false;
    }
    if (!$someOtherCondition) {
        return false;
    }
    if ($yetSomeOtherCondition) {
        doSomethingSpecial();
    }
    doSomethingElse();
}

Этот код определенно лучше для восприятия, а делает тоже что и пример выше.

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

function someFunc() {
	if($oneThing) {
		$this->doSomething();
		if($anotherThing)
			$this->doSomethingElse();
	}
}

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

function someFunc() {
	if($oneThing) {
		$this->doSomething();
		$this->doAnotherThing($anotherThing);
	}
}
private doAnotherThing($anotherThing) {
	if($anotherThing)
		$this->doSomethingElse();
}

11 — Избегайте магических чисел и строк

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

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

function someFunct() {
	$this->order->set(23);
	$this->order->addProduct('superComputer');
	$this->shoppingList->add('superComputer');
}

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

function someFunct() {
	$orderId = 23;
	$selectedProductName = 'superComputer';
	$this->order->set($orderId);
	$this->order->addProduct($selectedProductName);
	$this->shoppingList->add($selectedProductName);
}

Кто-то может сказать что мы создаем ненужные переменные, но производительность пострадает незначительно. Читаемость кода всегда в приоритете. Запомните: не зацикливайтесь на производительности пока не сможете сказать зачем она вам нужна.

12 — Не борщите с переменными

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

public function get_posts() {
	$query = $this->db->get('posts');
	$result = $query->result();
	return $result;
}

Переменная $result здесь совсем ни к чему. Что и показывает следующий фрагмент:

public function get_posts() {
	$query = $this->db->get('posts');
	return $query->result();
}

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


Общие рекомендации про программированию

13 — Разумно называйте свои переменные

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

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

14 — Методы описывающие действие

Ошибки случаются; важно учиться на них.

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

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

15 — Рекомендации по структуре

И наконец что касается структуры кода. Читаемость и понятность кода также важна как и все о чем мы сегодня говорили. Две рекомендации:

  • Используйте отступы в 2 или 4 пробела. Немного больше, например 8 пробелов, это слишком много и сделает ваш код сложным для чтения.
  • Установите разумную ширину и придерживайтесь ее. 40 символов в строке? Нет, мы уже не в 70х; установите ширину в 120 знаков, поставьте отметку на экране, и заставьте себя или вашу IDE соблюдать этот лимит. 120 знаков прекрасная ширина, не заставляющая скроллить.

Заключение.

"Я никогда не делал глупых ошибок программирования"

Никто, нигде.

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

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

Похожие записи

Немного об авторе: Сергей Белянин

Основной автор этого блога. Еще совсем недавно студент, а сегодня уже Microsoft Certifed Professional, с неукротимым желанием сделать жизнь людей проще, автоматизировав скучную рутинную работу :)