Pull to refresh

Ненормальный Javascript

Reading time 5 min
Views 2.7K
В этом топике я хочу рассказать о необычных конструкциях js, а так же на наглядных примерах разобрать некоторые моменты, связанные с объектами и вызовами методов, которые, при использовании нетривиального синтаксиса могут вызывать вопросы у почти всех начинающих использовать js.

Цель топика (и сразу же дисклеймер) — помочь начинающим не впадать в кататонический ступор при виде чего-то вроде
user[(os[((user.microsoft_adept ? microsoft : apple).system || "linux")].install_carma <= user.carma) ? "install" : "cant_install"](os[((user.microsoft_adept ? microsoft : apple).system || "linux")].name);

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

Приступим, помолясь и по-порядку

Объекты, свойства и методы
var obj = {
	property: 0,
	method: function() {}
}

Это — объект со свойством property и методом method, обратиться к которым можно несколькими способами:

obj.property; //каноничный, используется чаще всего
obj.method();

obj["property"]; //чуть реже, но тоже используется
obj["method"]();


Тернарный оператор
Нужен для простой записи несложной условной конструкции. Так, например, следующее условие
if (a > b) {
	console.log("a больше b");
}
else {
	console.log("a меньше или равно b");
}

можно записать короче:
(a > b) ? console.log("a больше b") : console.log("a меньше или равно b");

или ещё короче, чему способствует низкий приоритет тернарного оператора:
console.log( (a > b) ? "a больше b" : "a меньше или равно b" );


Условный оператор ||
В Javascript интересен тем, что возвращает первое ненулевое значение, что удобно для задания значений по умолчанию.
function get_object(id) {
	return document.getElementById(id) || "Объект не найден"; //Функция поиска объекта по id вернёт строку «Объект не найден», если указан несуществующий идентификатор
}
function do_something() {
	return false || 0 || Math.PI || "Что за дебильный пример?"; //Функция вернёт число пи
}


Возвращаемся к нашим баранам

В свете вышеописанного, что-то в исходном примере становится яснее, но разберём мы его от и до. Итак, вот наша мозголомка:
user[(os[((user.microsoft_adept ? microsoft : apple).system || "linux")].install_carma <= user.carma) ? "install" : "cant_install"](os[((user.microsoft_adept ? microsoft : apple).system || "linux")].name);

Некий жопа-программист Ваш покорный слуга решил сэкономить несколько байт кода и записал в одну строку решение задачи «проверить предпочтения объекта user касательно операционных систем разных производителей и, в зависимости от квалификации пользователя разрешить ему установить систему или же отправить на повышение квалификации». Объектная модель выглядит так (все совпадения, разумеется, случайны и я прошу никого не обижаться: все параллели проводятся just for lulz):
var user = {
	microsoft_adept: false, //любит ли пользователь продукты Микрософта
	carma: 10, //условная квалификация пользователя
	install: function(system) { console.log("I'am installing " + system + "!"); },
	cant_install: function(system) { console.log("I can't install " + system + "..."); }
}
	
var os = {
	windows: {
		name: "Windows",
		install_carma: 30 //квалификация, необходимая для успешной установки
	},
	macos: {
		name: "Mac OS",
		install_carma: 50
	},
	linux: {
		name: "Linux",
		install_carma: 70
	}
}
	
var microsoft = {
	system: "windows"
}
	
var apple = {
	system: "macos"
}

Во-первых, поступим как известный итальянский скульптор и отсечём всё лишнее. Получится следующего вида конструкция:
user[ () ? "install" : "cant_install"]();

Сразу видно, что в зависимости от результата некоторого условия вызывается соответствующий метод объекта user. А именно, install в случае, если условие истинно и cant_install, если ложно. Обоим методам передаётся нечто в качестве параметра. Предлагаю разобраться именно с этим нечтом:
os[((user.microsoft_adept ? microsoft : apple).system || "linux")].name;

Нечто — название желаемой операционной системы в зависимости от предпочтений пользователя. Диктую по буквам: если пользователь предпочитает продукты Микрософта, то мы читаем свойство system объекта microsoft. Если нет, то тоже свойство читается у объекта apple. Если такого свойства не существует (вендекапец-там или ещё что-то нехорошее), то пользователю, кроме Линукса ничего не остаётся. Таким образом, мы получаем один из трёх вариантов (см. объектную модель):
os[microsoft.system].name; // т. е. Windows
os[apple.system].name; // т. е. Mac OS
os["linux"].name; // т. е. Linux

В условии, которое определяет вызывать ли метод install или же cant_install у объекта user, мы таким же образом докапываемся до свойства install_carma, в зависимости от пользовательских предпочтений и «ситуации на рынке» и сравниваем значение этого свойства со значением свойства carma и объекта user. Если карма пользователя достаточна, то всё в порядке — вызываем метод user[«install»]. Если же нет, то тоже всё в порядке, но вызываем метод user[«cant_install»] и передаём вызванному в качестве параметра название системы.

Исходный код примера полностью (для копипаста)

var user = {
	microsoft_adept: false, //любит ли пользователь продукты Микрософта
	carma: 10, //условная квалификация пользователя
	install: function(system) { console.log("I'am installing " + system + "!"); },
	cant_install: function(system) { console.log("I can't install " + system + "..."); }
}
	
var os = {
	windows: {
		name: "Windows",
		install_carma: 30 //квалификация, необходимая для успешной установки
	},
	macos: {
		name: "Mac OS",
		install_carma: 50
	},
	linux: {
		name: "Linux",
		install_carma: 70
	}
}
	
var microsoft = {
	system: "windows"
}
	
var apple = {
	system: "macos"
}

user[(os[((user.microsoft_adept ? microsoft : apple).system || "linux")].install_carma <= user.carma) ? "install" : "cant_install"](os[((user.microsoft_adept ? microsoft : apple).system || "linux")].name);


Заключение

Я ещё раз повторюсь, что статья рассчитана в первую очередь на начинающих Javascript-программистов, которым по долгу службы придётся столкнуться с самым разным «чужим кодом», который надо будет читать. Кроме того, ряд примеров нетривиального js-синтаксиса может пригодиться в некоторых случаях, но (sic!) ещё и ещё раз напоминаю, что в статье приведён пример как делать не надо! Старайтесь всегда писать красивый и читабельный код. И я не удержусь от того, чтобы не процитировать известный афоризм: «Пишите код, исходя из того, что все программисты, которые будут сопровождать вашу программу, — склонные к насилию психопаты, знающие, где вы живёте.»
Tags:
Hubs:
+17
Comments 49
Comments Comments 49

Articles