--- language: javascript contributors: - ["Adam Brenecki", "http://adam.brenecki.id.au"] - ["Ariel Krakowski", "http://www.learneroo.com"] translators: - ["Maxim Koretskiy", "http://github.com/maximkoretskiy"] filename: javascript-ru.js lang: ru-ru --- Javascript был разработан Бренданом Айком из Netcape в 1995. Изначально предполагалось, что он станет простым вариантом скриптового языка для сайтов, дополняющий к Java, который бы в свою очередь использовался для более сложных web-приложений. Но тонкая интегрированность javascript с web-страницей и встроенная поддержка в браузерах привели к тому, чтобы он стал более распространен в frontend-разработке, чем Java. Использование JavaScript не ограничивается браузерами. Проект Node.js, предоставляющий независимую среду выполнения на движке Google Chrome V8 JavaScript, становится все более популярным. Обратная связь важна и нужна! Вы можете написаться мне на [@adambrenecki](https://twitter.com/adambrenecki) или [adam@brenecki.id.au](mailto:adam@brenecki.id.au). ```js // Комментарии оформляются как в C. // Однострочнные коментарии начинаются с двух слешей, /* а многострочные с слеша и звездочки и заканчиваются звездочеий и слешом */ // Выражения разделяются с помощью ; doStuff(); // ... но этого можно и не делать, разделители подставляются автоматически // после перехода на новую строку за исключением особых случаев doStuff() // Это может приводить к непредсказуемому результату и поэтому мы будем // использовать разделители в этом туре. // Because those cases can cause unexpected results, we'll keep on using // semicolons in this guide. /////////////////////////////////// // 1. Числа, Строки и Операторы // 1. Numbers, Strings and Operators // В Javasript всего 1 числовой тип - 64-битное число с плавающей точкой // стандарта IEEE 754) // Числа имеют 52-битную мантиссу, чего достаточно для хранения хранения целых // чисел до 9✕10¹⁵ приблизительно. 3; // = 3 1.5; // = 1.5 // В основном базовая арифметика работает предсказуемо 1 + 1; // = 2 .1 + .2; // = 0.30000000000000004 8 - 1; // = 7 10 * 2; // = 20 35 / 5; // = 7 // Включая нецелочисленное деление 5 / 2; // = 2.5 // Двоичные операции тоже есть. Если применить двоичную операцию к float-числу, // оно будет приведено к 32-битному целому со знаком. 1 << 2; // = 4 // (todo:перевести) // Приоритет выполнения операций можно менять с помощью скобок. (1 + 3) * 2; // = 8 // Есть три особых не реальных числовых значения: Infinity; // допустим, результат операции 1/0 -Infinity; // допустим, результат операции -1/0 NaN; // допустим, результат операции 0/0 // Так же есть тип булевых данных. true; false; // Строки создаются с помощью ' или ". 'abc'; "Hello, world"; // Оператор ! означает отрицание !true; // = false !false; // = true // Равество это === 1 === 1; // = true 2 === 1; // = false // Неравенство это !== 1 !== 1; // = false 2 !== 1; // = true // Еще сравнения 1 < 10; // = true 1 > 10; // = false 2 <= 2; // = true 2 >= 2; // = true // Строки складываются с помощью + "Hello " + "world!"; // = "Hello world!" // и сравниваются с помощью < и > "a" < "b"; // = true // Приведение типов выполняется при сравнении с ==... "5" == 5; // = true null == undefined; // = true // ...в отличие от === "5" === 5; // = false null === undefined; // = false // Для доступа к конкретному символу строки используйте charAt "This is a string".charAt(0); // = 'T' // ... или используйте substring для получения подстроки "Hello world".substring(0, 5); // = "Hello" // length(длина) - свойство, не используйте () "Hello".length; // = 5 // Есть null и undefined null; // используется что бы указать явно, что значения нет undefined; // испрользуется чтобы показать, что значения не было установлено // собственно, undefined так и переводится // false, null, undefined, NaN, 0 и "" являются falsy-значениями(при приведении // в булеву типу становятся false) // Обратите внимание что 0 приводится к false, а "0" к true, // не смотря на то, что "0"==0 /////////////////////////////////// // 2. Переменные, массивы и объекты // Переменные объявляются ключевым словом var. Javascript динамически // типизируемый, так что указывать тип не нужно. // Присвоение значения описывается с помощью оператора = var someVar = 5; // если не указать ключевого слова var, ошибки не будет... someOtherVar = 10; // ...но переменная будет создана в глобальном контексте, в не области // видимости, в которой она была объявлена. // Переменные объявленные без присвоения значения, содержать undefined var someThirdVar; // = undefined // Для математических операций над переменными есть короткая запись: someVar += 5; // тоже что someVar = someVar + 5; someVar равно 10 теперь someVar *= 10; // а теперь -- 100 // еще более короткая запись для добавления и вычитания 1 someVar++; // теперь someVar равно 101 someVar--; // обратно к 100 // Массивы -- упорядоченные списки значений любых типов. var myArray = ["Hello", 45, true]; // Для доступу к элементам массивов используйте квадратные скобки. // Индексы массивов начинаются с 0 myArray[1]; // = 45 // Массивы мутабельны(изменяемы) и имеют переменную длину. myArray.push("World"); // добавить элемент myArray.length; // = 4 // Добавить или изменить значение по конкретному индексу myArray[3] = "Hello"; // Объекты javascript похожи на dictionary или map из других языков // программирования. Это неупорядочнные коллекции пар ключ-значение. var myObj = {key1: "Hello", key2: "World"}; // Ключи -- это строки, но кавычки не требуются если названия явлюятся // корректными javascript идентификаторами. Значения могут быть любого типа. var myObj = {myKey: "myValue", "my other key": 4}; // Доступ к атрибту объекта можно получить с помощью квадратных скобок myObj["my other key"]; // = 4 // ... или используя точечную нотацию, при условии что ключ является // корректным идентификатором. myObj.myKey; // = "myValue" // Объекты мутабельны. В существуюещем объекте можно изменить значние // или добавить новый атрибут. myObj.myThirdKey = true; // При попытке доступа к атрибуту, который до этого не был создан, будет // возвращен undefined myObj.myFourthKey; // = undefined /////////////////////////////////// // 3. Логика и Управляющие структуры // Синтаксис управляющих структур очень похож на его реализацию в Java. // if работает так как вы ожидаете. var count = 1; if (count == 3){ // выполнится, если значение count равно 3 } else if (count == 4){ // выполнится, если значение count равно 4 } else { // выполнится, если значение count не будет равно ни 3 ни 4 } // Поведение while тоже вполне предсказуемо while (true){ // Бесконечный цикл } // Циклы do-while похожи на while, но они всегда выполняются хотябы 1 раз. // Do-while loops are like while loops, except they always run at least once. var input do { input = getInput(); } while (!isValid(input)) // Цикл for такой же как в C и Java: // инициализация; условие продолжения; итерация for (var i = 0; i < 5; i++){ // выполнится 5 раз } // && - логическое и, || - логическое или if (house.size == "big" && house.color == "blue"){ house.contains = "bear"; } if (color == "red" || color == "blue"){ // если цвет или красный или синий } // && и || удобны для установки значений по умолчанию. // && and || "short circuit", which is useful for setting default values. var name = otherName || "default"; // выражение switch проверяет равество с помощью === // используйте 'break' после каждого case, // иначе помимо правильного case выполнятся и все последующие. grade = '4'; // оценка switch (grade) { case '5': console.log("Великолепно"); break; case '4': console.log("Неплохо"); break; case '3': console.log("Можно и лучше"); break; default: console.log("Да уж."); break; } /////////////////////////////////// // 4. Функции, Область видимости и Замыкания // Функции в JavaScript объявляются с помощью ключевого слова function. function myFunction(thing){ return thing.toUpperCase(); // приведение к верхнему регистру } myFunction("foo"); // = "FOO" // Помните, что значение, которое должно быть возкращено должно начинаться // на той же строке, где расположено ключевое слово 'return'. В противном случае // будет возвращено undefined. Такое поведения объясняется автоматической // вставкой разделителей ';'. Помните этот факт, если используете // BSD стиль оформления кода. // Note that the value to be returned must start on the same line as the // 'return' keyword, otherwise you'll always return 'undefined' due to // automatic semicolon insertion. Watch out for this when using Allman style. function myFunction() { return // <- разделитель автоматически будет вставлен здесь { thisIsAn: 'object literal' } } myFunction(); // = undefined // Функции в JavaScript являются объектами, поэтому их можно назначить в // переменные с разными названиями и передавать в другие функции, как аргументы, // на пример, при указании обработчика события. function myFunction(){ // этот фрагмент кода будет вызван через 5 секунд } setTimeout(myFunction, 5000); // Обратите внимание, что setTimeout не является частью языка, однако он // доступен в API браузеров и Node.js. // Объект функции на самом деле не обязательно объявлять с именем - можно // создать анонимную функцию прямо в аргументах другой функции. setTimeout(function(){ // этот фрагмент кода будет вызван через 5 секунд }, 5000); // В JavaScript есть области видимости. У функций есть собственные области // видимости, у других блоков их нет. if (true){ var i = 5; } i; // = 5, а не undefined, как это было бы в языке, создающем // области видисти для блоков кода // Это привело к появлению паттерна общего назначения "immediately-executing // anonymous functions" (сразу выполняющиеся анонимные функции), который // позволяет предотвратить запись временных переменных в общую облать видимости. (function(){ var temporary = 5; // Для доступа к глобальной области видимости можно использовать запись в // некоторый 'глобальный объект', для браузеров это 'window'. Глобальный // объект может называться по-разному в небраузерных средах, таких Node.js. window.permanent = 10; })(); temporary; // вызывает исключение ReferenceError permanent; // = 10 // Одной из сильных сторон JavaScript являются замыкания. Если функция // объявлена в внутри другой функции, внутренняя функция имеет доступ ко всем // переменным внешней функции, даже после того, как внешняя функции завершила // свое выполнение. function sayHelloInFiveSeconds(name){ var prompt = "Привет, " + name + "!"; // Внутренние функции помещаются в локальную область видимости, как-будто // они объявлены с ключевым словом 'var'. function inner(){ alert(prompt); } setTimeout(inner, 5000); // setTimeout является асинхроннной, и поэтому функция sayHelloInFiveSeconds // завершит свое выполнение сразу и setTimeout вызовет inner позже. // Однако, так как inner "закрыта внутри" или "замкнута в" // sayHelloInFiveSeconds, inner все еще будет иметь доступ к переменной // prompt, когда будет вызвана. } sayHelloInFiveSeconds("Вася"); // откроет модальное окно с сообщением // "Привет, Вася" по истечении 5 секунд. /////////////////////////////////// // 5. Немного еще об Объектах. Конструкторы и Прототипы // Объекты могут содержать функции var myObj = { myFunc: function(){ return "Hello world!"; } }; myObj.myFunc(); // = "Hello world!" // Когда функции прикрепленные к объекту вызываются, они могут получить доступ // к данным объекта, ипользуя ключевое слово this. myObj = { myString: "Hello world!", myFunc: function(){ return this.myString; } }; myObj.myFunc(); // = "Hello world!" // Содержание this определяется исходя из того, как была вызвана функция, а // не места её определения. По этой причине наша функция не будет работать вне // контекста объекта. var myFunc = myObj.myFunc; myFunc(); // = undefined // И напротив, функция может быть присвоена объекту и получить доступ к нему // через this, даже если она не была прикреплена к объекту в момент её создания. var myOtherFunc = function(){ return this.myString.toUpperCase(); } myObj.myOtherFunc = myOtherFunc; myObj.myOtherFunc(); // = "HELLO WORLD!" // Также можно указать контекс выполнения фунции с помощью 'call' или 'apply'. var anotherFunc = function(s){ return this.myString + s; } anotherFunc.call(myObj, " And Hello Moon!"); // = "Hello World! And Hello Moon!" // Функция 'apply' очень похожа, но принимает массив со списком аргументов. anotherFunc.apply(myObj, [" And Hello Sun!"]); // = "Hello World! And Hello Sun!" // Это может быть удобно, когда работаешь с фунцией принимающей на вход // последовательность аргументов и нужно передать их в виде массива. Math.min(42, 6, 27); // = 6 Math.min([42, 6, 27]); // = NaN (uh-oh!) Math.min.apply(Math, [42, 6, 27]); // = 6 // Однако, 'call' и 'apply' не имеют постоянного эффекта. Если вы хотите // зафиксировать контекст для функции, используйте bind. var boundFunc = anotherFunc.bind(myObj); boundFunc(" And Hello Saturn!"); // = "Hello World! And Hello Saturn!" // bind также можно использовать чтобы частично передать аргументы в // функцию (каррировать). var product = function(a, b){ return a * b; } var doubler = product.bind(this, 2); doubler(8); // = 16 // Когда функция вызывается с ключевым словом new, создается новый объект. // Данный объект становится доступным функции через ключевое слово this. // Функции спроектированные вызываться таким способом называются конструкторами. var MyConstructor = function(){ this.myNumber = 5; } myNewObj = new MyConstructor(); // = {myNumber: 5} myNewObj.myNumber; // = 5 // Любой объект в JavaScript имеет 'прототип'. Когда вы пытаетесь получить // доступ к свойству не объявленному в данном объекте, интерпретатор будет // искать его в прототипе. // Некоторые реализации JS позволяют получить доступ к прототипу с помощью // "волшебного" свойства __proto__. До момента пока оно не являются стандартным, // __proto__ полезно лишь для изучения прототипов в JavaScript. Мы разберемся // со стандартными способами работы с прототипом несколько позже. var myObj = { myString: "Hello world!" }; var myPrototype = { meaningOfLife: 42, myFunc: function(){ return this.myString.toLowerCase() } }; myObj.__proto__ = myPrototype; myObj.meaningOfLife; // = 42 myObj.myFunc(); // = "hello world!" // Естественно, если в свойства нет в прототипе, поиск будет продолжен в // прототипе прототипа и так далее. myPrototype.__proto__ = { myBoolean: true }; myObj.myBoolean; // = true // Ничего не копируется, каждый объект хранит ссылку на свой прототип. Если // изменить прототип, все изменения отразятся во всех его потомках. myPrototype.meaningOfLife = 43; myObj.meaningOfLife; // = 43 // Как было сказано выше, __proto__ не является стандартом, и нет стандартного // способа изменить прототип у созданного объекта. Однако, есть два способа // создать объект с заданным прототипом. // Первый способ - Object.create, был добавлен в JS относительно недавно, и // потому не доступен в более ранних реализациях языка. var myObj = Object.create(myPrototype); myObj.meaningOfLife; // = 43 // Второй вариан доступен везде и связан с конструкторами. Конструкторы имеют // свойство prototype. Это вовсе не прототип функции конструктора, напротив, // это прототип, который присваевается новым объектам, созданным с этим // конструктором с помощью ключевого слова new. MyConstructor.prototype = { myNumber: 5, getMyNumber: function(){ return this.myNumber; } }; var myNewObj2 = new MyConstructor(); myNewObj2.getMyNumber(); // = 5 myNewObj2.myNumber = 6 myNewObj2.getMyNumber(); // = 6 // У встроенных типов таких, как строки и числа, тоже есть конструкторы, // создающие эквивалентные объекты-обертки. var myNumber = 12; var myNumberObj = new Number(12); myNumber == myNumberObj; // = true // Правда, они не совсем эквивалентны. typeof myNumber; // = 'number' typeof myNumberObj; // = 'object' myNumber === myNumberObj; // = false if (0){ // Этот фрагмент кода не будет выпонен, так как 0 приводится к false } if (Number(0)){ // Этот фрагмент кода *будет* выпонен, так как Number(0) приводится к true. } // Однако, оберточные объекты и обычные встроенные типы имеют общие прототипы, // следовательно вы можете расширить функциональность строки, например. String.prototype.firstCharacter = function(){ return this.charAt(0); } "abc".firstCharacter(); // = "a" // Этот факт часто используется для создания полифилов(polyfill) - реализации // новых возможностей JS в его более старых версиях для того чтобы их можно было // использовать в устаревших браузерах. // Например, мы упомянули, что Object.create не доступен во всех версиях // JavaScript, но мы можем создать полифилл. if (Object.create === undefined){ // не переопределяем, если есть Object.create = function(proto){ // создаем временный конструктор с заданным прототипом var Constructor = function(){}; Constructor.prototype = proto; // создаём новый объект с правильным прототипом return new Constructor(); } } ``` ## Что еще почитать [Современный учебник JavaScript](http://learn.javascript.ru/) от Ильи Кантора является довольно качественным и глубоким учебным материалом, освещающим все особенности современного языка. Помимо учебника на том же домене можно найти [перевод спецификации ECMAScript 5.1](http://es5.javascript.ru/) и справочник по возможностям языка. [JavaScript Garden](http://bonsaiden.github.io/JavaScript-Garden/ru/) позволяет довольно быстро изучить основные тонкие места в работе с JS, но фокусируется только на таких моментах [Справочник](https://developer.mozilla.org/ru/docs/JavaScript) от MDN (Mozilla Development Network) содержит информацию о возможностях языка на английском. Название проекта ["Принципы написания консистентного, идиоматического кода на JavaScript"](https://github.com/rwaldron/idiomatic.js/tree/master/translations/ru_RU) говорит само за себя.