В процессе загрузки ресурсов... загрузка...

4.1 Язык JavaScript быстрое начало

Автор:Доброта, Создано: 2019-06-25 13:28:33, Обновлено: 2023-11-11 17:06:12

img

История

В этом разделе рассказывается немного о JavaScript, чтобы помочь вам понять, почему он такой.

JavaScript против ECMAScript

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

  • JavaScript означает язык программирования.
  • ECMAScript - это название, используемое спецификацией языка. Поэтому, когда люди говорят о версиях языка, они говорят ECMAScript.

Влияние и характер языка

Создатель JavaScript, Брендан Эйх, не имел выбора, кроме как создать язык очень быстро (или другие, худшие технологии были бы приняты Netscape). Он позаимствовал из нескольких языков программирования: Java (синтаксис, примитивные значения против объектов), Scheme и AWK (функции первого класса), Self (прототипное наследие) и Perl и Python (строки, массивы и регулярные выражения).

JavaScript не имел обработки исключений до ECMAScript 3, что объясняет, почему язык так часто автоматически преобразует значения и так часто терпит неудачу: изначально он не мог бросать исключения.

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

Учитывая его влияние, неудивительно, что JavaScript обеспечивает стиль программирования, который является смесью функционального программирования (функции высшего порядка; встроенная карта, уменьшить и т. Д.) и объектно-ориентированного программирования (объекты, наследование).

Синтаксис

В этом разделе объясняются основные синтаксические принципы JavaScript.

Обзор синтаксиса

Несколько примеров синтаксиса:

// Two slashes start single-line comments

var x;  // declaring a variable

x = 3 + y;  // assigning a value to the variable `x`

foo(x, y);  // calling function `foo` with parameters `x` and `y`
obj.bar(3);  // calling method `bar` of object `obj`

// A conditional statement
if (x === 0) {  // Is `x` equal to zero?
    x = 123;
}

// Defining function `baz` with parameters `a` and `b`
function baz(a, b) {
    return a + b;
}

Обратите внимание на два различных использования знака равенства:

  • Один знак равен (=) используется для присвоения значения переменной.
  • Знак тройного равенства (===) используется для сравнения двух значений (см. Операторы равенства).

Заявления против выражений

Чтобы понять синтаксис JavaScript, вы должны знать, что он имеет две основные синтаксические категории: заявления и выражения:

  • Заявления do things. Программа - это последовательность заявлений. Вот пример заявления, которое декларирует (создает) переменную foo:
var foo;
  • Выражения производят значения. Это аргументы функции, правая сторона задания и т. д. Вот пример выражения:
3 * 7

Различие между инструкциями и выражениями лучше всего проиллюстрировано тем, что JavaScript имеет два различных способа выполнения if-then-else как инструкции:

var x;
if (y >= 0) {
    x = y;
} else {
    x = -y;
}

или как выражение:

var x = y >= 0 ? y : -y;

Вы можете использовать последний как аргумент функции (но не первый):

myFunction(y >= 0 ? y : -y)

Наконец, там, где JavaScript ожидает заявление, вы также можете использовать выражение; например:

foo(7, 1);

Вся прямая является утверждением (так называемое утверждение выражения), но вызов функции foo ((7, 1) является выражением.

Колонки

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

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

// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { };  // function expr. inside var decl.

Комментарии

JavaScript имеет два вида комментариев: комментарии с одной строкой и комментарии с несколькими строками.

x++; // single-line comment

Комментарии с несколькими строками обозначаются * и *:

/* This is
   a multiline
   comment.
 */

Переменные и назначение

Переменные в JavaScript декларируются до их использования:

var foo;  // declare variable `foo`

Назначение

Вы можете объявить переменную и назначить значение одновременно:

var foo = 6;

Вы также можете назначить значение существующей переменной:

foo = 4;  // change variable `foo`

Операторы соединенных заданий

Существуют операторы сложного назначения, такие как +=. Следующие два назначения эквивалентны:

x += 1;
x = x + 1;

Идентификаторы и названия переменных

Идентификаторы - это имена, которые играют различные синтаксические роли в JavaScript. Например, имя переменной является идентификатором.

В общем, первым символом идентификатора может быть любая буква Unicode, знак доллара ($) или подчерк (_).

arg0
_tmp
$elem
π

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

img

Следующие три идентификатора не являются зарезервированными словами, но вы должны относиться к ним так, как если бы они были:

img

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

Ценности

JavaScript имеет множество значений, которые мы ожидаем от языков программирования: булевы, числа, строки, массивы и т. д. Все значения в JavaScript имеют свойства. Каждое свойство имеет ключ (или имя) и значение.

value.propKey

Например, строка abc имеет свойство длины:

> var str = 'abc';
> str.length
3

Предыдущее также может быть написано так:

> 'abc'.length
3
The dot operator is also used to assign a value to a property:

> var obj = {};  // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123

И вы можете использовать его для вызова методов:

> 'hello'.toUpperCase()
'HELLO'

В предыдущем примере мы вызвали метод toUpperCase() на значении hello.

Примитивные ценности против предметов

JavaScript делает несколько произвольное различие между значениями:

  • Примитивные значения - булевы, числа, строки, нуль и неопределенные.
  • Все остальные значения - объекты. Основное различие между ними заключается в том, как они сравниваются; каждый объект имеет уникальную идентичность и только (строго) равен себе:
> var obj1 = {};  // an empty object
> var obj2 = {};  // another empty object
> obj1 === obj2
false
> obj1 === obj1
true

Напротив, все примитивные значения, кодирующие одно и то же значение, считаются одинаковыми:

> var prim1 = 123;
> var prim2 = 123;
> prim1 === prim2
true

В следующих двух разделах более подробно объясняются примитивные значения и объекты.

Первобытные ценности

Ниже приведены все примитивные значения:

  • Булевы: истинные, ложные (см. Булевы)
  • Числа: 1736, 1.351 (см. Числа)
  • Стрелы: abc, abc (см. Стрелы)
  • Два незначения: неопределенные, нулевые (см. неопределенные и нулевые)

Примитивные имеют следующие характеристики:

Сравнение по стоимости

содержание сравнивается:

> 3 === 3
true
> 'abc' === 'abc'
true

### Всегда неизменный Свойства не могут быть изменены, добавлены или удалены:

> var str = 'abc';

> str.length = 1; // try to change property `length`
> str.length      // ⇒ no effect
3

> str.foo = 3; // try to create property `foo`
> str.foo      // ⇒ no effect, unknown property
undefined

(Чтение неизвестного свойства всегда возвращает неопределенное.)

Объекты

Все непримитивные значения являются объектами.

  • Простые объекты, которые могут быть созданы объектами буквальных (см. Однородные объекты):
{
    firstName: 'Jane',
    lastName: 'Doe'
}

Предыдущий объект имеет два свойства: значение свойства firstName - Jane и значение свойства lastName - Doe.

  • Массивы, которые могут быть созданы буквальными массивами (см. Массивы):
[ 'apple', 'banana', 'cherry' ]

Предыдущий массив имеет три элемента, к которым можно получить доступ с помощью числовых индексов.

  • Регулярные выражения, которые могут быть созданы буквами регулярного выражения (см. Регулярные выражения):
/^a+b+$/

Объекты имеют следующие характеристики:

Сравнение по ссылкам

Идентификации сравниваются; каждое значение имеет свою собственную идентичность:

> ({} === {})  // two different empty objects
false

> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true

Изменяется по умолчанию

Обычно вы можете свободно изменять, добавлять и удалять свойства (см. Единые объекты):

> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123

неопределенный и нулевой

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

  • undefined означает без значения. Неинтилированные переменные неопределены:
> var foo;
> foo
undefined

Отсутствующие параметры не определены:

> function f(x) { return x }
> f()
undefined

Если вы читаете несуществующее свойство, вы получаете неопределенное:

> var obj = {}; // empty object
> obj.foo
undefined
  • null означает нет объекта. Он используется как незначение всякий раз, когда ожидается объект (параметры, последний в цепочке объектов и т.д.).

УВЕДЕНИЕ

undefined и null не имеют свойств, даже стандартные методы, такие как toString().

Проверка на неопределенность или нуль

Функции обычно позволяют указывать недостающее значение через undefined или null.

if (x === undefined || x === null) {
    ...
}

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

if (!x) {
    ...
}

УВЕДЕНИЕ

false, 0, NaN и также считаются ложными (см. Truthy и Falsy).

Категоризация значений с использованием типа и экземпляра

Существует два оператора для классификации значений: typeof используется в основном для примитивных значений, а instanceof используется для объектов. Вид выглядит так:

typeof value

Он возвращает строку, описывающую type значения.

> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'

В следующей таблице перечислены все результаты типа:

img

typeof null возвращающий object является ошибкой, которую нельзя исправить, потому что это нарушит существующий код.

Пример выглядит так:

value instanceof Constr

Он возвращает true, если значение является объектом, который был создан конструктором Constr (см. Конструкторы: Фабрики для объектов).

> var b = new Bar();  // object created by constructor Bar
> b instanceof Bar
true

> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object  // Array is a subconstructor of Object
true

> undefined instanceof Object
false
> null instanceof Object
false

Булевы

Примитивный булевой тип включает в себя значения true и false.

  • Бинарные логические операторы: && (And),
  • Логический оператор префикса:! (Не)
  • Операторы сравнения: Операторы равенства: ===,!==, ==,!=
  • Операторы упорядочения (для строки и чисел): >, >=, <, <=

Истинное и ложное

Всякий раз, когда JavaScript ожидает булевое значение (например, для условия if), можно использовать любое значение. Оно будет интерпретироваться как true или false. Следующие значения интерпретируются как false:

  • неопределенный, нулевой
  • Булево: ложно
  • Число: 0, NaN
  • Строка:

Все остальные значения (включая все объекты!) считаются истинными. Значения, интерпретированные как ложные, называются ложными, а значения, интерпретированные как истинные, называются истинными.

> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
true

Бинарные логические операторы

Бинарные логические операторы в JavaScript имеют короткое замыкание. То есть, если первого операнда достаточно для определения результата, второй операнд не оценивается. Например, в следующих выражениях функция foo() никогда не называется:

false && foo()
true  || foo()

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

И (&&)

Если первый оперант ложный, верните его. В противном случае верните второй оперант:

> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'

Или (показывает)

Если первый оперант верен, верните его. В противном случае верните второй оперант:

> 'abc' || 123
'abc'
> '' || 123
123

Операторы по вопросам равенства

JavaScript имеет два вида равенства:

  • Нормальный, или уступчивый, (неравен): == и!=
  • Строго (не) равенство: === и!==

Нормальное равенство считает (слишком) многими значениями равными (подробности объясняются в Нормальном (Лениентном) Равенстве (==,!=)), что может скрыть ошибки.

Числа

Все числа в JavaScript - скользящая запятая:

> 1 === 1.0
true

Специальные номера включают следующие:

NaN (не число) Значение ошибки:

> Number('xyz')  // 'xyz' can’t be converted to a number
NaN

Бесконечность Также в основном значение ошибки:

> 3 / 0
Infinity
> Math.pow(2, 1024)  // number too large
Infinity

Бесконечность больше любого другого числа (кроме NaN). Аналогично, - бесконечность меньше любого другого числа (кроме NaN). Это делает эти числа полезными в качестве значения по умолчанию (например, когда вы ищете минимум или максимум).

Операторы

JavaScript имеет следующие арифметические операторы (см. Арифметические операторы):

  • Добавление: No1 + No2
  • Вычитание: номер 1 - номер 2
  • Умножение: число 1 * число 2
  • Раздел: No1 / No2
  • Остальная часть: No1 % No2
  • Инкремент: ++переменный, переменный++
  • Декремент: переменный, переменный
  • Отрицательно: -значение
  • Преобразовать в число: +значение

Глобальный объект Math (см. Math) обеспечивает больше арифметических операций через функции.

JavaScript также имеет операторы для битовых операций (например, битовые и; см. Битовые операторы).

Струнные

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

'abc'
"abc"

'Did she say "Hello"?'
"Did she say \"Hello\"?"

'That\'s nice!'
"That's nice!"

'Line 1\nLine 2'  // newline
'Backlash: \\'

Доступ к одним символам осуществляется через квадратные скобки:

> var str = 'abc';
> str[1]
'b'

В свойстве length подсчитывается количество символов в строке:

> 'abc'.length
3

Как и все примитивы, строки неизменны; вам нужно создать новую строку, если вы хотите изменить существующую.

Операторы строки

Строки соединены через оператор плюс (+), который преобразует другой оперант в строку, если один из операндов является строкой:

> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'

Чтобы соединить строки в несколько шагов, используйте оператор +=:

> var str = '';
> str += 'Multiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Multiple pieces are concatenated.'

Методы строки

У струн есть много полезных методов (см. Методы прототипов струн). Вот несколько примеров:

> 'abc'.slice(1)  // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'

> '\t xyz  '.trim()  // trim whitespace
'xyz'

> 'mjölnir'.toUpperCase()
'MJÖLNIR'

> 'abc'.indexOf('b')  // find a string
1
> 'abc'.indexOf('x')
-1

Заявления

Условия и петли в JavaScript представлены в следующих разделах.

Условные

Указание if имеет then-заголовок и дополнительный else-заголовок, которые выполняются в зависимости от булевого условия:

if (myvar === 0) {
    // then
}

if (myvar === 0) {
    // then
} else {
    // else
}

if (myvar === 0) {
    // then
} else if (myvar === 1) {
    // else-if
} else if (myvar === 2) {
    // else-if
} else {
    // else
}

Я рекомендую всегда использовать скобки (они обозначают блоки с нулем или более заявлений). Но вам не нужно делать это, если пункт представляет собой только одно заявление (то же самое относится к заявлениям потока управления для и в то время как):

if (x < 0) return -x;

Следующее указание - значение фрукта, которое определяет, какой случай выполняется:

switch (fruit) {
    case 'banana':
        // ...
        break;
    case 'apple':
        // ...
        break;
    default:  // all other cases
        // ...
}

operand после случая может быть любым выражением; он сравнивается через === с параметром переключения.

Кружки

Ключа for имеет следующий формат:

for (⟦«init»⟧; ⟦«condition»⟧; ⟦«post_iteration»⟧)
    «statement»

init выполняется в начале цикла. condition проверяется перед каждой итерацией цикла; если он становится ложным, то цикл заканчивается. post_iteration выполняется после каждой итерации цикла.

Этот пример распечатывает все элементы массива arr на консоли:

for (var i=0; i < arr.length; i++) {
    console.log(arr[i]);
}

Период цикла продолжает циркулировать над его телом, пока его состояние сохраняется:

// Same as for loop above:
var i = 0;
while (i < arr.length) {
    console.log(arr[i]);
    i++;
}

Поскольку состояние следует за телом, тело всегда выполняется по крайней мере один раз:

do {
    // ...
} while (condition);

Во всех циклах:

  • Разрыв оставляет петлю.
  • продолжение начинает новую итерацию петли.

Функции

Один из способов определения функции - через декларацию функции:

function add(param1, param2) {
    return param1 + param2;
}

Предыдущий код определяет функцию, которая имеет два параметра, param1 и param2, и возвращает сумму обоих параметров.

> add(6, 1)
7
> add('a', 'b')
'ab'

Другой способ определения add (() - это присвоение функционного выражения переменной add:

var add = function (param1, param2) {
    return param1 + param2;
};

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

someOtherFunction(function (p1, p2) { ... });

Заявления о должностях поднимаются

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

function foo() {
    bar();  // OK, bar is hoisted
    function bar() {
        ...
    }
}

Обратите внимание, что в то время как декларации var также поднимаются (см. Переменные поднимаются), задания, выполняемые ими, не являются:

function foo() {
    bar();  // Not OK, bar is still undefined
    var bar = function () {
        // ...
    };
}

Аргументы специальной переменной

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

> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0]  // read element at index 0
'a'

Слишком много или слишком мало споров

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

function f(x, y) {
    console.log(x, y);
    return toArray(arguments);
}

Дополнительные параметры игнорируются (кроме аргументов):

> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]

Отсутствие параметров приведет к неопределенному значению:

> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]

Факультативные параметры

Ниже приведена общая схема присвоения параметрам значения по умолчанию:

function pair(x, y) {
    x = x || 0;  // (1)
    y = y || 0;
    return [ x, y ];
}

В строке (1), оператор "вывести" возвращает x, если он является истинным (не нулевым, неопределенным и т. д.). В противном случае он возвращает второй оперант:

> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]

Принуждение к гармонии

Если вы хотите ввести arity (определенное количество параметров), вы можете проверить arguments.length:

function pair(x, y) {
    if (arguments.length !== 2) {
        throw new Error('Need exactly 2 arguments');
    }
    ...
}

Преобразование аргументов в массив

arguments - это не массив, это только массивный (см. Array-Like Objects and Generic Methods). Он имеет длину свойства, и вы можете получить доступ к его элементам через индексы в квадратных скобках. Вы, однако, не можете удалять элементы или вызывать какие-либо из методов массива на нем. Таким образом, иногда вам нужно конвертировать аргументы в массив, что делает следующая функция (это объясняется в Array-Like Objects and Generic Methods):

function toArray(arrayLikeObject) {
    return Array.prototype.slice.call(arrayLikeObject);
}

Обработка исключений

Наиболее распространенным способом обращения с исключениями (см. главу 14) является следующее:

function getPerson(id) {
    if (id < 0) {
        throw new Error('ID must not be negative: '+id);
    }
    return { id: id }; // normally: retrieved from database
}

function getPersons(ids) {
    var result = [];
    ids.forEach(function (id) {
        try {
            var person = getPerson(id);
            result.push(person);
        } catch (exception) {
            console.log(exception);
        }
    });
    return result;
}

Клауза try окружает критический код, а клауза catch выполняется, если исключение бросается внутри клаузы try.

> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]

Строгий режим

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

'use strict';

Вы также можете включить строгий режим на функцию:

function functionInStrictMode() {
    'use strict';
}

Переменный охват и закрытия

В JavaScript вы объявляете переменные через var перед их использованием:

> var x;
> x
undefined
> y
ReferenceError: y is not defined

Вы можете объявить и инициировать несколько переменных с помощью одного заявления var:

var x = 1, y = 2, z = 3;

Но я рекомендую использовать одно утверждение на переменную (причина объясняется в синтаксисе). Таким образом, я перепишу предыдущее утверждение на:

var x = 1;
var y = 2;
var z = 3;

Из-за подъема (см. Переменные поднимаются), обычно лучше всего объявлять переменные в начале функции.

Переменные определяются по функциям

Объем переменной всегда является полной функцией (в отличие от текущего блока).

function foo() {
    var x = -512;
    if (x < 0) {  // (1)
        var tmp = -x;
        ...
    }
    console.log(tmp);  // 512
}

Мы видим, что переменная tmp не ограничивается блоком, начинающимся в строке (1); она существует до конца функции.

Переменные поднимаются

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

function foo() {
    console.log(tmp); // undefined
    if (false) {
        var tmp = 3;  // (1)
    }
}

Внутренне предыдущая функция выполняется следующим образом:

function foo() {
    var tmp;  // hoisted declaration
    console.log(tmp);
    if (false) {
        tmp = 3;  // assignment stays put
    }
}

Закрытия

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

function createIncrementor(start) {
    return function () {  // (1)
        start++;
        return start;
    }
}

Функция, начинающаяся в строке (1), выходит из контекста, в котором она была создана, но остается подключенной к живой версии старта:

> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8

Закрытие представляет собой функцию плюс соединение с переменными ее окружающих областей. Таким образом, то, что возвращает createIncrementor(), является закрытием.

Образец IIFE: введение новой сферы применения

Иногда вы хотите ввести новую область действия переменной, например, чтобы предотвратить глобализацию переменной. В JavaScript вы не можете использовать блок для этого; вы должны использовать функцию. Но есть шаблон для использования функции в блоковом виде. Он называется IIFE (немедленно вызванное выражение функции, произносится iffy):

(function () {  // open IIFE
    var tmp = ...;  // not a global variable
}());  // close IIFE

Убедитесь, что вы ввели предыдущий пример точно так же, как показано (за исключением комментариев). IIFE - это выражение функции, которое вызывается сразу после его определения. Внутри функции существует новый объем, что предотвращает глобализацию tmp.

Случай использования IIFE: непреднамеренное совместное использование через закрытия

Закрытия сохраняют свои связи с внешними переменными, что иногда не то, что вы хотите:

var result = [];
for (var i=0; i < 5; i++) {
    result.push(function () { return i });  // (1)
}
console.log(result[1]()); // 5 (not 1)
console.log(result[3]()); // 5 (not 3)

Значение, возвращаемое в строке (1), всегда является текущим значением i, а не значением, которое оно имело при создании функции. После завершения цикла i имеет значение 5, поэтому все функции в массиве возвращают это значение. Если вы хотите, чтобы функция в строке (1) получила снимок текущего значения i, вы можете использовать IIFE:

for (var i=0; i < 5; i++) {
    (function () {
        var i2 = i; // copy current i
        result.push(function () { return i2 });
    }());
}

Объекты и конструкторы

В этом разделе рассматриваются два основных объектно-ориентированных механизма JavaScript: одиночные объекты и конструкторы (которые являются фабриками для объектов, аналогичны классам в других языках).

Единые цели

Как и все значения, объекты имеют свойства. Вы можете, на самом деле, рассматривать объект как набор свойств, где каждое свойство является парой (ключ, значение). Ключ является строкой, а значение - любое значение JavaScript.

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

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

Предыдущий объект имеет свойства name и describe. Вы можете читать (get) и писать (set) свойства:

> jane.name  // get
'Jane'
> jane.name = 'John';  // set
> jane.newProperty = 'abc';  // property created automatically

Свойства с значениями функций, такие как describe, называются методами.

> jane.describe()  // call method
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'

Оператор in проверяет наличие свойства:

> 'newProperty' in jane
true
> 'foo' in jane
false

Если вы читаете свойство, которое не существует, вы получаете значение undefined.

> jane.newProperty !== undefined
true
> jane.foo !== undefined
false

Оператор " Удалить " удаляет свойство:

> delete jane.newProperty
true
> 'newProperty' in jane
false

Арбитражные ключи собственности

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

> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;

Квадратные скобки также позволяют вычислить ключ свойства:

> var obj = { hello: 'world' };
> var x = 'hello';

> obj[x]
'world'
> obj['hel'+'lo']
'world'

Методы добычи

Если вы извлекаете метод, он теряет связь с объектом. сам по себе функция больше не является методом, и это имеет значение undefined (в строгом режиме).

В качестве примера, давайте вернемся к предыдущему объекту Jane:

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

Мы хотим извлечь метод describe из jane, поместить его в переменную func, и вызвать его.

> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined

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

> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'

Функции внутри метода

Каждая функция имеет свою специальную переменную this. Это неудобно, если вы встраиваете функцию внутри метода, потому что вы не можете получить доступ к методу this из функции. Ниже приведен пример, где мы называем forEach с функцией для итерации над массивом:

var jane = {
    name: 'Jane',
    friends: [ 'Tarzan', 'Cheeta' ],
    logHiToFriends: function () {
        'use strict';
        this.friends.forEach(function (friend) {
            // `this` is undefined here
            console.log(this.name+' says hi to '+friend);
        });
    }
}

Призыв logHiToFriends приводит к ошибке:

> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined

Во-первых, мы можем хранить это в другой переменной:

logHiToFriends: function () {
    'use strict';
    var that = this;
    this.friends.forEach(function (friend) {
        console.log(that.name+' says hi to '+friend);
    });
}

Или, forEach имеет второй параметр, который позволяет предоставить значение для этого:

logHiToFriends: function () {
    'use strict';
    this.friends.forEach(function (friend) {
        console.log(this.name+' says hi to '+friend);
    }, this);
}

Функционные выражения часто используются в качестве аргументов в вызовах функций в JavaScript.

Конструкторы: фабрики для объектов

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

В дополнение к тому, что функции являются реальными функциями и методами, функции играют еще одну роль в JavaScript: они становятся конструкторамифабриками для объектов, если они вызваны через новый оператор. Таким образом, конструкторы являются примерным аналогом классов в других языках.

// Set up instance data
function Point(x, y) {
    this.x = x;
    this.y = y;
}
// Methods
Point.prototype.dist = function () {
    return Math.sqrt(this.x*this.x + this.y*this.y);
};

Мы видим, что конструктор имеет две части. Во-первых, функция Point устанавливает данные экземпляров. Во-вторых, свойство Point.prototype содержит объект с методами. Первые данные специфичны для каждого экземпляра, в то время как последние данные обмениваются между всеми экземплярами.

Чтобы использовать Point, мы вызываем его через новый оператор:

> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301

p представляет собой экземпляр Point:

> p instanceof Point
true

Массивы

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

Литералы массива

Литералы массивов полезны для создания массивов:

> var arr = [ 'a', 'b', 'c' ];

Предыдущий массив имеет три элемента: строки a, b и c. Вы можете получить к ним доступ через индексы целых чисел:

> arr[0]
'a'
> arr[0] = 'x';
> arr
[ 'x', 'b', 'c' ]

С помощью свойства length можно добавлять элементы и удалять элементы:

> var arr = ['a', 'b'];
> arr.length
2

> arr[arr.length] = 'c';
> arr
[ 'a', 'b', 'c' ]
> arr.length
3

> arr.length = 1;
> arr
[ 'a' ]

Оператор in работает и для массивов:

> var arr = [ 'a', 'b', 'c' ];
> 1 in arr // is there an element at index 1?
true
> 5 in arr // is there an element at index 5?
false

Обратите внимание, что массивы являются объектами и, следовательно, могут иметь свойства объектов:

> var arr = [];
> arr.foo = 123;
> arr.foo
123

Методы массива

У массивов много методов (см. Методы прототипов массивов). Вот несколько примеров:

> var arr = [ 'a', 'b', 'c' ];

> arr.slice(1, 2)  // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]

> arr.push('x')  // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]

> arr.pop()  // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]

> arr.shift()  // remove first element
'a'
> arr
[ 'b', 'c' ]

> arr.unshift('x')  // prepend an element
3
> arr
[ 'x', 'b', 'c' ]

> arr.indexOf('b')  // find the index of an element
1
> arr.indexOf('y')
-1

> arr.join('-')  // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'

Итерация над массивами

Существует несколько методов массива для итерации элементов (см. Итерация (неразрушительная)).

forEach повторяется над массивом и передает текущий элемент и его индекс функции:

[ 'a', 'b', 'c' ].forEach(
    function (elem, index) {  // (1)
        console.log(index + '. ' + elem);
    });

Предыдущий код дает следующий результат:

0. a
1. b
2. c

Обратите внимание, что функция в строке (1) может игнорировать аргументы.

map создает новый массив, применяя функцию к каждому элементу существующего массива:

> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]

Регулярные выражения

JavaScript имеет встроенную поддержку регулярных выражений.

/^abc$/
/[A-Za-z0-9]+/

Тест метода: есть совпадение?

> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false

Метод exec ((): Совпадение и группы захвата

> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]

Возвращенный массив содержит полное совпадение в индексе 0, захват первой группы в индексе 1 и так далее.

Метод replace ((): Поиск и замена

> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'

Первый параметр replace должен быть регулярным выражением с флагом /g; в противном случае, заменяется только первое occurrence.

Математика

Математика - это объект с арифметическими функциями. Вот несколько примеров:

> Math.abs(-2)
2

> Math.pow(3, 2)  // 3 to the power of 2
9

> Math.max(2, -1, 5)
5

> Math.round(1.9)
2

> Math.PI  // pre-defined constant for π
3.141592653589793

> Math.cos(Math.PI)  // compute the cosine for 180°
-1

Связанные

Больше