Повторное введение в JavaScript

  • URL ревизии: Повторное_введение_в_JavaScript
  • Заголовок ревизии: Повторное введение в JavaScript
  • ID ревизии: 267047
  • Создано:
  • Автор: Niquola
  • Это текущая версия? Нет
  • комментировать /* Числа */

Содержание версии

Введение

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

JavaScript был создан в 1995 Эриком Брэнданом (Brendan Eich), инженером Netscape, и использован в Netscape 2 в начале 1996. Его изначальное название было LiveScript, однако в следствии ошибочного маркетингового хода LiveScript был переименован в JavaScript, что вносит некоторую путаницу для начинающих,поскольку языки Java и JavaScript имеют очень немного общего.

Через несколько месяцев Microsoft выпустила вцелом совместимую версию языка JScript для IE 3. Netscape отправил запрос в Ecma International о стандартизации. В результате в 1997 году была создана первая редакция ECMAScript. Стандарт был значительно исправлен в 1999 году - ECMAScript edition 3 и с тех пор является достатоно устоявшимся. Ведется работа над 4 редакцией.

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

В отличии от большинства языков программирования JavaScript не имеет своей системы ввода/вывода. Он предназначен для встраивания в различные среды (in a host environment), которые и должны обеспечить механизм общения JavaScript с остальным миром. Наиболее распространненой средой в которую встраивается JavaScript является браузер. Но интерпретаторы JavaScript можно найти и в Adobe Acrobat, Photoshop, Yahoo!'s Widget engine, Eclipse и так джалее.

Обзор

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

... и еще Undefined и Null, которые немного в стороне. А также Arrays, который является специфическим объектом. Плюс еще встроенные объекты Dates и Regular Expressions. Строго говоря функция тоже является специальным объектом. Таким образом можно переписать список подругому:

  • Number
  • String
  • Boolean
  • Object
    • Function
    • Array
    • Date
    • RegExp
  • Null
  • Undefined

А еще забыли Error. Для простоты можно пользоваться первым списком.

Числа

Все числа в JavaScript согласно спецификации "64-битные двойной точности - формат IEEE 754". Будте внимательны в JavaScript нет типа Iteger, это может привести к неожиданным последствиям. например:

0.1 + 0.2 = 0.30000000000000004

Поддержаны стандартный арифметические операторы, такие как сложение,деление, остаток от деления и так далее. Здесь стоит вспомнить встроенные объект Math содержащий математические методы:

Math.sin(3.5);
d = Math.PI * r * r;

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

> parseInt("123", 10)
123
> parseInt("010", 10)
10

а если не указать базу то получим:

> parseInt("010")
8

Это произошло потому что parseInt посчитало число восьмеричным из-за предшествующего 0.

Вот еще пример:

> parseInt("11", 2)
3

Если функция не может преобразовать строку в чило она возвращает специальное значение NaN (сокращение от "Не Число") :

> parseInt("hello", 10)
NaN

NaN заразный: если он попадет в математические операции то их результатвсегда будет NaN:

> NaN + 5
NaN

Вы можете проверить значение на равенство NaN переменной с помощю встроенной функции isNaN() :

> isNaN(NaN)
true

В JavaScript есть также значения Infinity и -Infinity:

> 1 / 0
Infinity
> -1 / 0
-Infinity

Strings

Strings в JavaScript это последовательность символов. А точнее последовательность Core JavaScript 1.5 Guide:Юникод символов, где кажый символ кодируется 16-битным числом. Это хорошая новость для тех кто будет иметь дело с интернационализацией.

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

Для получения длины строки и объектов есть свойство length:

> "hello".length
5

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

> "hello".charAt(0)
h
> "hello, world".replace("hello", "goodbye")
goodbye, world
> "hello".toUpperCase()
HELLO

//todo Many modern languages (especially functional languages) employ “the immutable object” paradigm, which solves a lot of problems including the memory conservation, the cache localization, addressing concurrency concerns, and so on. The idea is simple: if object is immutable, it can be represented by a reference (a pointer, a handle), and passing a reference is the same as passing an object by value — if object is immutable, its value cannot be changed => a pointer pointing to this object can be dereferenced producing the same original value. It means we can replicate pointers without replicating objects. And all of them would point to the same object. What do we do when we need to change the object? One popular solution is to use Copy-on-write (COW). Under COW principle we have a pointer to the object (a reference, a handle), we clone the object it points to, we change the value of the pointer so now it points to the cloned object, and proceed with our mutating algorithm. All other references are still the same.

JavaScript performs all of “the immutable object” things for strings, but it doesn’t do COW on strings because it uses even simpler trick: there is no way to modify a string. All strings are immutable by virtue of the language specification and the standard library it comes with. For example: str.replace(”a”, “b”) doesn’t modify str at all, it returns a new string with the replacement executed. Or not. If the pattern was not found I suspect it’ll return a pointer to the original string. Every “mutating” operation for strings actually returns a new object leaving the original string unchanged: replace, concat, slice, substr, split, and even exotic anchor, big, bold, and so on.

http://lazutkin.com/

Другие типы

JavaScript различает специальный объект null типа 'object', который выражает явное (программно-установленное) пустое значения, и значение undefined типа 'undefined', которое говорит о том, что значение еще не было назначено.

 >> typeof null
 object
 >> typeof undefined
 undefined

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

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

  1. false, 0, пустая строка (""), NaN, null, и undefined трактуются как false
  2. все остальное как true

Преобразование можно провести в явном виде с использованием функции Boolean():

> Boolean("")
false
> Boolean(234)
true

эта возможность используется достаточно редко, поскольку такое преобразование производится автоматически в тех случаях когда ожидается логическое значение, как на пример в выражении if(см ниже). Поэтому обычно просто говорят о "истиных значениях" и "ложных значениях" подразумевая в какое из логических значений оно преобразуется true или false. Приведем еще и слэнговые варианты "truthy" и "falsy".

Конечно поддержаны стандартные логические операторы && (логическое и), || (логическое или), и ! (логическое не).

Переменные

Переменная в JavaScript объявляется с использованием ключевого слова var

var a;
var name = "simon";

Еще раз напомним, что если вы не назначили значение переменной, то ее значение будет типа undefined. should note the absence of block-scoping in JS

Операторы

В JavaScript есть стандартные математические операторы +, -, *, / и % - последний это оператор возвращающий остатк от деления. Значения присваиваются оператором =, также есть операторы составного присваивания += и -=. Они позволяют записать короче часто встречающиеся выражения типа: x = x operator y.

x += 5
x = x + 5

Вы можете использовать ++ и-- для увеличения или уменьшения значения переменной на единицу. Помещеть этот оператор можно как за символом переменной (i++) так и перед или (++i).

Оператор + operator применяется еще и для конкатенации строк:

> "hello" + " world"
hello world

При добавлении строки к числу последнее автоматически конвертируется в строку (поскольку JavaScript слабо типизированный язык, нужно быть очень осторожным ):

> "3" + 4 + 5
345
> 3 + 4 + "5"
75

Добавление пустой строки к чему-либо можно использовать, как преобразование к строке.

В JavaScript Comparisons есть следующие операторы сравнения <, >, <= и >=. Их можно использовать и со строками и с числами. Сравнение значений уже несколько сложнее. Оператор двойного равенства производит преобразование типов, что может привести к несколько неожиданным результатам:

> "dog" == "dog"
true
> 1 == true
true

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

> 1 === true
false
> true === true
true

Есть также обратные операторы != и !== для двойного и тройного равенства соответственно.

В JavaScript также есть побитовые операторы.

Control structures

JavaScript has a similar set of control structures to other languages in the C family. Conditional statements are supported by if and else; you can chain them together if you like:

var name = "kittens";
if (name == "puppies") {
  name += "!";
} else if (name == "kittens") {
  name += "!!";
} else {
  name = "!" + name;
}
name == "kittens!!"

JavaScript has while loops and do-while loops. The first is good for basic looping; the second for loops where you wish to ensure that the body of the loop is executed at least once:

while (true) {
  // an infinite loop!
}

do {
  var input = get_input();
} while (inputIsNotValid(input))

JavaScript's for loop is the same as that in C and Java: it lets you provide the control information for your loop on a single line.

for (var i = 0; i < 5; i++) {
  // Will execute 5 times
}

The && and || operators use short-circuit logic, which means whether they will execute their second operand is dependent on the first. This is useful for checking for null objects before accessing their attributes:

var name = o && o.getName();

Or for setting default values:

var name = otherName || "default";

JavaScript has a tertiary operator for one-line conditional statements:

var allowed = (age > 18) ? "yes" : "no";

The switch statement can be used for multiple branches based on a number or string:

switch(action) {
    case 'draw':
        drawit();
        break;
    case 'eat':
        eatit();
        break;
    default:
        donothing();
}

If you don't add a break statement, execution will "fall through" to the next level. This is very rarely what you want - in fact it's worth specifically labelling deliberate fallthrough with a comment if you really meant it to aid debugging:

switch(a) {
    case 1: // fallthrough
    case 2:
        eatit();
        break;
    default:
        donothing();
}

The default clause is optional. You can have expressions in both the switch part and the cases if you like; comparisons take place between the two using the === operator:

switch(1 + 3):
    case 2 + 2:
        yay();
        break;
    default:
        neverhappens();
}

Objects

JavaScript objects are simply collections of name-value pairs. As such, they are similar to:

  • Dictionaries in Python
  • Hashes in Perl and Ruby
  • Hash tables in C and C++
  • HashMaps in Java
  • Associative arrays in PHP

The fact that this data structure is so widely used is a testament to its versatility. Since everything (bar core types) in JavaScript is an object, any JavaScript program naturally involves a great deal of hash table lookups. It's a good thing they're so fast!

The "name" part is a JavaScript string, while the value can be any JavaScript value - including more objects. This allows you to build data structures of arbitrary complexity.

There are two basic ways to create an empty object:

var obj = new Object();

And:

var obj = {};

These are semantically equivalent; the second is called object literal syntax, and is more convenient. Object literal syntax was not present in very early versions of the language which is why you see so much code using the old method.

Once created, an object's properties can again be accessed in one of two ways:

obj.name = "Simon"
var name = obj.name;

And...

obj["name"] = "Simon";
var name = obj["name"];

These are also semantically equivalent. The second method has the advantage that the name of the property is provided as a string, which means it can be calculated at run-time. It can also be used to set and get properties with names that are reserved words:

obj.for = "Simon"; // Syntax error, because 'for' is a reserved word
obj["for"] = "Simon"; // works fine

Object literal syntax can be used to initialise an object in its entirety:

var obj = {
    name: "Carrot",
    "for": "Max",
    details: {
        color: "orange",
        size: 12
    }
}

Attribute access can be chained together:

> obj.details.color
orange
> obj["details"]["size"]
12

Arrays

Arrays in JavaScript are actually a special type of object. They work very much like regular objects (numerical properties can naturally be accessed only using [] syntax) but they have one magic property called 'length'. This is always one more than the highest index in the array.

The old way of creating arrays is as follows:

> var a = new Array();
> a[0] = "dog";
> a[1] = "cat";
> a[2] = "hen";
> a.length
3

A more convenient notation is to use an array literal:

> var a = ["dog", "cat", "hen"];
> a.length
3

Leaving a trailing comma at the end of an array literal is inconsistent across browsers, so don't do it.

Note that array.length isn't necessarily the number of items in the array. Consider the following:

> var a = ["dog", "cat", "hen"];
> a[100] = "fox";
> a.length
101

Remember - the length of the array is one more than the highest index.

If you query a non-existent array index, you get undefined:

> typeof(a[90])
undefined

If you take the above into account, you can iterate over an array using the following:

for (var i = 0; i < a.length; i++) {
    // Do something with a[i]
}

This is slightly inefficient as you are looking up the length property once every loop. An improvement is this:

for (var i = 0, len = a.length; i < len; i++) {
    // Do something with a[i]
}

An even nicer idiom is:

for (var i = 0, item; item = a[i]; i++) {
    // Do something with item
}

Here we are setting up two variables. The assignment in the middle part of the for loop is also tested for truthfulness - if it succeeds, the loop continues. Since i is incremented each time, items from the array will be assigned to item in sequential order. The loop stops when a "falsy" item is found (such as undefined).

Note that this trick should only be used for arrays which you know do not contain "falsy" values (arrays of objects or DOM nodes for example). If you are iterating over numeric data that might include a 0 or string data that might include the empty string you should use the i, j idiom instead.

Another way to iterate is to use the for...in loop. Note that if someone added new properties to Array.prototype, they will also be iterated over by this loop:

for (var i in a) {
  // Do something with a[i]
}

If you want to append an item to an array, the safest way to do it is like this:

a[a.length] = item;                 // same as a.push(item);

Since a.length is one more than the highest index, you can be assured that you are assigning to an empty position at the end of the array.

Arrays come with a number of methods:

a.toString(), a.toLocaleString(), a.concat(item, ..), a.join(sep),
a.pop(), a.push(item, ..), a.reverse(), a.shift(), a.slice(start, end),
a.sort(cmpfn), a.splice(start, delcount, [item]..), a.unshift([item]..)
  • concat returns a new array with the items added on to it.
  • pop removes and returns the last item
  • push adds one or more items to the end (like our ar{{mediawiki.external('ar.length')}} idiom)
  • slice returns a sub-array
  • sort takes an optional comparison function
  • splice lets you modify an array by deleting a section and replacing it with more items
  • unshift prepends items to the start of the array

Functions

Along with objects, functions are the core component in understanding JavaScript. The most basic function couldn't be much simpler:

function add(x, y) {
    var total = x + y;
    return total;
}

This demonstrates everything there is to know about basic functions. A JavaScript function can take 0 or more named parameters. The function body can contain as many statements as you like, and can declare its own variables which are local to that function. The return statement can be used to return a value at any time, terminating the function. If no return statement is used (or an empty return with no value), JavaScript returns undefined.

The named parameters turn out to be more like guidelines than anything else. You can call a function without passing the parameters it expects, in which case they will be set to undefined.

> add()
NaN // You can't perform addition on undefined

You can also pass in more arguments than the function is expecting:

> add(2, 3, 4)
5 // added the first two; 4 was ignored

That may seem a little silly, but functions have access to an additional variable inside their body called arguments, which is an array-like object holding all of the values passed to the function. Let's re-write the add function to take as many values as we want:

function add() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum;
}

> add(2, 3, 4, 5)
14

That's really not any more useful than writing 2 + 3 + 4 + 5 though. Let's create an averaging function:

function avg() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}
> avg(2, 3, 4, 5)
3.5

This is pretty useful, but introduces a new problem. The avg() function takes a comma separated list of arguments - but what if you want to find the average of an array? You could just rewrite the function as follows:

function avgArray(arr) {
    var sum = 0;
    for (var i = 0, j = arr.length; i < j; i++) {
        sum += arr[i];
    }
    return sum / arr.length;
}
> avgArray([2, 3, 4, 5])
3.5

But it would be nice to be able to reuse the function that we've already created. Luckily, JavaScript lets you call a function and call it with an arbitrary array of arguments, using the apply() method of any function object.

> avg.apply(null, [2, 3, 4, 5])
3.5

The second argument to apply() is the array to use as arguments; the first will be discussed later on. This emphasizes the fact that functions are objects too.

JavaScript lets you create anonymous functions.

var avg = function() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}

This is semantically equivalent to the function avg() form. It's extremely powerful, as it lets you put a full function definition anywhere that you would normally put an expression. This enables all sorts of clever tricks. Here's a way of "hiding" some local variables - like block scope in C:

> var a = 1;
> var b = 2;
> (function() {
    var b = 3;
    a += b;
})();
> a
4
> b
2

JavaScript allows you to call functions recursively. This is particularly useful for dealing with tree structures, such as you get in the browser DOM.

function countChars(elm) {
    if (elm.nodeType == 3) { // TEXT_NODE
        return elm.nodeValue.length;
    }
    var count = 0;
    for (var i = 0, child; child = elm.childNodes[i]; i++) {
        count += countChars(child);
    }
    return count;
}

This highlights a potential problem with anonymous functions: how do you call them recursively if they don't have a name? The answer lies with the arguments object, which in addition to acting as a list of arguments also provides a property called arguments.callee. This always refers to the current function, and hence can be used to make recursive calls:

var charsInBody = (function(elm) {
    if (elm.nodeType == 3) { // TEXT_NODE
        return elm.nodeValue.length;
    }
    var count = 0;
    for (var i = 0, child; child = elm.childNodes[i]; i++) {
        count += arguments.callee(child);
    }
    return count;
})(document.body);

Since arguments.callee is the current function, and all functions are objects, you can use arguments.callee to save information across multiple calls to the same function. Here's a function that remembers how many times it has been called:

function counter() {
    if (!arguments.callee.count) {
        arguments.callee.count = 0;
    }
    return arguments.callee.count++;
}

> counter()
0
> counter()
1
> counter()
2

Пользовательские объекты

В классичеких объектно-ориентированных языках, объект это коллекция данных и методов, оперирующих этими данными. Давайте рассмотрим объект person c полями first и last name (имя фамилия). И предположим нам надо иметь два метода для различных отображений полного имени: как "first last" или как "last, first". Мы можем сделать это с использованием объекта и функций вот так (не очень элегантно):

function makePerson(first, last) {
    return {
        first: first,
        last: last
    }
}
function personFullName(person) {
    return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
    return person.last + ', ' + person.first
}
> s = makePerson("Simon", "Willison");
> personFullName(s)
Simon Willison
> personFullNameReversed(s)
Willison, Simon

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

function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}
> s = makePerson("Simon", "Willison")
> s.fullName()
Simon Willison
> s.fullNameReversed()
Willison, Simon

Здесь мы встречаемся с ключевым словом 'this'. При использовании внутри функции 'this' ссылается на текущий объект. Чему будет равно 'this' на самом деле зависит от того как была вызвана функция. Если мы вызываем функцию через точечную нотацию или квадратные скобки , тогда 'this' указывает на этот объект. Если просто вызвать функцию, то 'this' будет указывать на глобальный объект. Непонимание этого может привести к неприятным последствиям:

> s = makePerson("Simon", "Willison")
> var fullName = s.fullName;
> fullName()
undefined undefined

Когда мы вызываем fullName(), в роли 'this' выступает глобальный объект. И поскольку у нас нет глобальных переменных first и last для каждой из них мы получаем undefined.

Мы можем использовать 'this' для усовершенствования нашей функции makePerson:

function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = function() {
        return this.first + ' ' + this.last;
    }
    this.fullNameReversed = function() {
        return this.last + ', ' + this.first;
    }
}
var s = new Person("Simon", "Willison");

Здесь мы видим ключевое слово 'new'. new связан с 'this'. Итак чтоже делает new. Он создает пустой объект и вызывает указанную функцию, внутри которой 'this' указывает на этот новый объект. Функции которые будут использоватся вместе с 'new' называются 'конструкторами'. Обычно название таких функций начинается с заглавной буквы, и напоминает о том, что функция должна быть использована вместе с new.

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

function personFullName() {
    return this.first + ' ' + this.last;
}
function personFullNameReversed() {
    return this.last + ', ' + this.first;
}
function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = personFullName;
    this.fullNameReversed = personFullNameReversed;
}

Так немного лучше - функции будут создаваться только один раз, а ссылки на них назначаются в конструкторе. Можноли сделать еще лучше? Да:

function Person(first, last) {
    this.first = first;
    this.last = last;
}
Person.prototype.fullName = function() {
    return this.first + ' ' + this.last;
}
Person.prototype.fullNameReversed = function() {
    return this.last + ', ' + this.first;
}

Person.prototype это объект общий для всех объектов созданых с помощью Person. Он входит в "prototype chain" (цепочку поиска): каждый раз когда вы пытаетесь получить свозство объекта Person, которое у него отсутствует, JavaScript проверяет нет ли такого свойства у Person.prototype. Таким образом, все что будет присвоено Person.prototype становится доступно всем объектам порожденным конструктором Person, в том числе и через объект this.

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

> s = new Person("Simon", "Willison");
> s.firstNameCaps();
TypeError on line 1: s.firstNameCaps is not a function
> Person.prototype.firstNameCaps = function() {
    return this.first.toUpperCase()
}
> s.firstNameCaps()
SIMON

Вы можете расширить даже prototype (прототипы) встроенных JavaScript объектов. Для примера добавим метод переворачивающий строку к String:

> var s = "Simon";
> s.reversed()
TypeError on line 1: s.reversed is not a function
> String.prototype.reversed = function() {
    var r = "";
    for (var i = this.length - 1; i >= 0; i--) {
        r += this[i];
    }
    return r;
}
> s.reversed()
nomiS

Наш метод будет работать даже для строковых литералов!

> "This can now be reversed".reversed()
desrever eb won nac sihT

As I mentioned before, the prototype forms part of a chain. The root of that chain is Object.prototype, whose methods include toString() - it is this method that is called when you try to represent an object as a string. This is useful for debugging our Person objects:

> var s = new Person("Simon", "Willison");
> s
[object Object]
> Person.prototype.toString = function() {
    return '<Person: ' + this.fullName() + '>';
}
> s
<Person: Simon Willison>

Remember how avg.apply() had a null first argument? We can revisit that now. The first argument to apply() is the object that should be treated as 'this'. For example, here's a trivial implementation of 'new':

function trivialNew(constructor) {
    var o = {}; // Create an object
    constructor.apply(o, arguments);
    return o;
}

This isn't an exact replica of new as it doesn't set up the prototype chain. apply() is difficult to illustrate - it's not something you use very often, but it's useful to know about.

apply() has a sister function named call, which again lets you set 'this' but takes an expanded argument list as opposed to an array.

function lastNameCaps() {
    return this.last.toUpperCase();
}
var s = new Person("Simon", "Willison");
lastNameCaps.call(s);
// Is the same as:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps();

Inner functions

JavaScript function declarations are allowed inside other functions. We've seen this once before, with an earlier makePerson() function. An important detail of nested functions in JavaScript is that they can access variables in their parent function's scope:

function betterExampleNeeded() {
    var a = 1;
    function oneMoreThanA() {
        return a + 1;
    }
    return oneMoreThanA();
}

This provides a great deal of utility in writing more maintainable code. If a function relies on one or two other functions that are not useful to any other part of your code, you can nest those utility functions inside the function that will be called from elsewhere. This keeps the number of functions that are in the global scope down, which is always a good thing.

This is also a great counter to the lure of global variables. When writing complex code it is often tempting to use global variables to share values between multiple functions - which leads to code that is hard to maintain. Nested functions can share variables in their parent, so you can use that mechanism to couple functions together when it makes sense without polluting your global namespace - 'local globals' if you like. This technique should be used with caution, but it's a useful ability to have.

Closures

This leads us to one of the most powerful abstractions that JavaScript has to offer - but also the most potentially confusing. What does this do?

function makeAdder(a) {
    return function(b) {
        return a + b;
    }
}
x = makeAdder(5);
y = makeAdder(20);
x(6)
?
y(7)
?

The name of the makeAdder function should give it away: it creates new 'adder' functions, which when called with one argument add it to the argument that they were created with.

What's happening here is pretty much the same as was happening with the inner functions earlier on: a function defined inside another function has access to the outer function's variables. The only difference here is that the outer function has returned, and hence common sense would seem to dictate that its local variables no longer exist. But they do still exist - otherwise the adder functions would be unable to work. What's more, there are two different "copies" of makeAdder's local variables - one in which a is 5 and one in which a is 20. So the result of those function calls is as follows:

x(6) // returns 11
y(7) // returns 27

Here's what's actually happening. Whenever JavaScript executes a function, a 'scope' object is created to hold the local variables created within that function. It is initialised with any variables passed in as function parameters. This is similar to the global object that all global variables and functions live in, but with a couple of important differences: firstly, a brand new scope object is created every time a function starts executing, and secondly, unlike the global object (which in browsers is accessible as window) these scope objects cannot be directly accessed from your JavaScript code. There is no mechanism for iterating over the properties of the current scope object for example.

So when makeAdder is called, a scope object is created with one property: a, which is the argument passed to the makeAdder function. makeAdder then returns a newly created function. Normally JavaScript's garbage collector would clean up the scope object created for makeAdder at this point, but the returned function maintains a reference back to that scope object. As a result, the scope object will not be garbage collected until there are no more references to the function object that makeAdder returned.

Scope objects form a chain called the scope chain, similar to the prototype chain used by JavaScript's object system.

A closure is the combination of a function and the scope object in which it was created.

Closures let you save state - as such, they can often be used in place of objects.

Memory leaks

An unfortunate side effect of closures is that they make it trivially easy to leak memory in Internet Explorer. JavaScript is a garbage collected language - objects are allocated memory upon their creation and that memory is reclaimed by the browser when no references to an object remain. Objects provided by the host environment are handled by that environment.

Browser hosts need to manage a large number of objects representing the HTML page being presented - the objects of the DOM. It is up to the browser to manage the allocation and recovery of these.

Internet Explorer uses its own garbage collection scheme for this, separate from the mechanism used by JavaScript. It is the interaction between the two that can cause memory leaks.

A memory leak in IE occurs any time a circular reference is formed between a JavaScript object and a native object. Consider the following:

function leakMemory() {
    var el = document.getElementById('el');
    var o = { 'el': el };
    el.o = o;
}

The circular reference formed above creates a memory leak; IE will not free the memory used by el and o until the browser is completely restarted.

The above case is likely to go unnoticed; memory leaks only become a real concern in long running applications or applications that leak large amounts of memory due to large data structures or leak patterns within loops.

Leaks are rarely this obvious - often the leaked data structure can have many layers of references, obscuring the circular reference.

Closures make it easy to create a memory leak without meaning to. Consider this:

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
}

The above code sets up the element to turn red when it is clicked. It also creates a memory leak. Why? Because the reference to el is inadvertently caught in the closure created for the anonymous inner function. This creates a circular reference between a JavaScript object (the function) and a native object (el).

There are a number of workarounds for this problem. The simplest is this:

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
    el = null;
}

This works by breaking the circular reference.

Surprisingly, one trick for breaking circular references introduced by a closure is to add another closure:

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
    }
    (function() {
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}

The inner function is executed straight away, and hides its contents from the closure created with clickHandler.

Another good trick for avoiding closures is breaking circular references during the window.onunload event. Many event libraries will do this for you. Note that doing so disables bfcache in Firefox 1.5, so you should not register an unload listener in Firefox, unless you have other reasons to do so.

{{ wiki.languages( { "en": "en/A_re-introduction_to_JavaScript", "fr": "fr/Une_r\u00e9introduction_\u00e0_JavaScript", "it": "it/Una_re-introduzione_a_Javascript", "ja": "ja/A_re-introduction_to_JavaScript", "ko": "ko/A_re-introduction_to_JavaScript", "pl": "pl/JavaScript/Na_pocz\u0105tek", "zh-cn": "cn/A_re-introduction_to_JavaScript" } ) }}

Revision Source

<h3 name=".D0.92.D0.B2.D0.B5.D0.B4.D0.B5.D0.BD.D0.B8.D0.B5"> Введение</h3>
<p>Почему повторное? Потому что <a href="ru/JavaScript">JavaScript</a> является действительно <a class="external" href="http://javascript.crockford.com/javascript.html">самым непонятым(недооцененным) языком программирования</a>. Он часто воспринимается как игрушка, однако несмотря на его простоту этот язык обладает большими возможностями. Начиная с 2005 года появляется все больше и больше впечетляющих приложений интенсивно использующих JavaScript.
</p><p>JavaScript был создан в 1995 Эриком Брэнданом (Brendan Eich), инженером Netscape, и использован в Netscape 2 в начале 1996. Его изначальное название было LiveScript, однако в следствии ошибочного маркетингового хода LiveScript был переименован в JavaScript, что вносит некоторую путаницу для начинающих,поскольку языки Java и JavaScript имеют очень немного общего. 
</p><p>Через несколько месяцев Microsoft выпустила вцелом совместимую версию языка JScript для IE 3. Netscape отправил запрос в <a class="external" href="http://www.ecma-international.org/">Ecma International</a> о стандартизации. В результате в 1997 году была создана первая редакция <a href="ru/ECMAScript">ECMAScript</a>. Стандарт был значительно исправлен в 1999 году - <a class="external" href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript edition 3</a> и с тех пор является достатоно устоявшимся. Ведется работа над 4 редакцией.
</p><p>В этой статье будут в основном рассмотрены возможности 3 редакиии и для однообразия везде будет использоватся термин JavaScript.
</p><p>В отличии от большинства языков программирования JavaScript не имеет своей системы ввода/вывода. Он предназначен для встраивания в различные среды (in a host environment), которые и должны обеспечить механизм общения JavaScript с остальным миром. Наиболее распространненой средой в которую встраивается JavaScript является браузер. Но интерпретаторы JavaScript можно найти и в Adobe Acrobat, Photoshop, Yahoo!'s Widget engine, Eclipse и так джалее.
</p>
<h3 name=".D0.9E.D0.B1.D0.B7.D0.BE.D1.80"> Обзор </h3>
<p>Давайте посмотрим на строительные блоки любого языка: типы. JavaScript программа манипулирует переменными, которые имеют тип. В JavaScript имеются следующие типы:
</p>
<ul><li> <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Number">Numbers</a>
</li><li> <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/String">Strings</a>
</li><li> <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Boolean">Booleans</a>
</li><li> <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Function">Functions</a>
</li><li> <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Object">Objects</a>
</li></ul>
<p>... и еще Undefined и Null, которые немного в стороне. А также <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Array">Arrays</a>, который является специфическим объектом. Плюс еще встроенные объекты <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Date">Dates</a> и <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/RegExp">Regular Expressions</a>. Строго говоря функция тоже является специальным объектом. Таким образом можно переписать список подругому: 
</p>
<ul><li> Number
</li><li> String
</li><li> Boolean
</li><li> Object
<ul><li> Function
</li><li> Array
</li><li> Date
</li><li> RegExp
</li></ul>
</li><li> Null
</li><li> Undefined
</li></ul>
<p>А еще забыли <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Error">Error</a>. Для простоты можно пользоваться первым списком.
</p>
<h3 name=".D0.A7.D0.B8.D1.81.D0.BB.D0.B0"> Числа </h3>
<p>Все числа в JavaScript согласно спецификации "64-битные двойной точности - формат IEEE 754". Будте внимательны в JavaScript нет типа Iteger, это может привести к неожиданным последствиям. например:
</p>
<pre class="eval">0.1 + 0.2 = 0.30000000000000004
</pre>
<p>Поддержаны стандартный  <a href="ru/Core_JavaScript_1.5_Reference/Operators/Arithmetic_Operators"> арифметические операторы</a>, такие как сложение,деление, остаток от деления и так далее. Здесь стоит вспомнить встроенные объект <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Math">Math</a> содержащий математические методы:
</p>
<pre class="eval">Math.sin(3.5);
d = Math.PI * r * r;
</pre>
<p>Вы можете преобразовать строку в число с использованием встроенной функции <code><a href="ru/Core_JavaScript_1.5_Reference/Global_Functions/parseInt">parseInt()</a></code>. Вторым параметром эта фукция принемает базу, которую стоит всегда указывать во избежание казусов:
</p>
<pre class="eval">&gt; parseInt("123", 10)
123
&gt; parseInt("010", 10)
10
</pre>
<p>а если не указать базу то получим:
</p>
<pre class="eval">&gt; parseInt("010")
8
</pre>
<p>Это произошло  потому что <code>parseInt</code> посчитало число восьмеричным из-за предшествующего 0.
</p><p>Вот еще пример:
</p>
<pre class="eval">&gt; parseInt("11", 2)
3
</pre>
<p>Если функция не может преобразовать строку в чило она возвращает специальное значение <code><a href="ru/Core_JavaScript_1.5_Reference/Global_Properties/NaN">NaN</a></code> (сокращение от "Не Число") :
</p>
<pre class="eval">&gt; parseInt("hello", 10)
NaN
</pre>
<p><code>NaN</code> заразный: если он попадет в математические операции то их результатвсегда будет <code>NaN</code>:
</p>
<pre class="eval">&gt; NaN + 5
NaN
</pre>
<p>Вы можете проверить значение на равенство <code>NaN</code> переменной с помощю встроенной функции  <code><a href="ru/Core_JavaScript_1.5_Reference/Global_Functions/isNaN">isNaN()</a></code> :
</p>
<pre class="eval">&gt; isNaN(NaN)
true
</pre>
<p>В JavaScript есть также значения <code><a href="ru/Core_JavaScript_1.5_Reference/Global_Properties/Infinity">Infinity</a></code> и <code>-Infinity</code>:
</p>
<pre class="eval">&gt; 1 / 0
Infinity
&gt; -1 / 0
-Infinity
</pre>
<h3 name="Strings"> Strings </h3>
<p>Strings в JavaScript это последовательность символов. А точнее последовательность <a href="ru/Core_JavaScript_1.5_Guide/%d0%ae%d0%bd%d0%b8%d0%ba%d0%be%d0%b4_%d1%81%d0%b8%d0%bc%d0%b2%d0%be%d0%bb%d0%be%d0%b2">Core JavaScript 1.5 Guide:Юникод символов</a>, где кажый символ кодируется 16-битным числом. Это хорошая новость для тех кто будет иметь дело с интернационализацией.
</p><p>Для представления одного символа вы можете использовать строку единичной длины.
</p><p>Для получения длины строки и объектов есть свойство <code><a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/String/length">length</a></code>:
</p>
<pre class="eval">&gt; "hello".length
5
</pre>
<p>Упс я забыл вам сказать что в JavaScript строка тоже объект, у которого есть набор методов <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/String#Methods">methods</a>:
</p>
<pre class="eval">&gt; "hello".charAt(0)
h
&gt; "hello, world".replace("hello", "goodbye")
goodbye, world
&gt; "hello".toUpperCase()
HELLO
</pre>
<p>//todo
Many modern languages (especially functional languages) employ “the immutable object” paradigm, which solves a lot of problems including the memory conservation, the cache localization, addressing concurrency concerns, and so on. The idea is simple: if object is immutable, it can be represented by a reference (a pointer, a handle), and passing a reference is the same as passing an object by value — if object is immutable, its value cannot be changed =&gt; a pointer pointing to this object can be dereferenced producing the same original value. It means we can replicate pointers without replicating objects. And all of them would point to the same object. What do we do when we need to change the object? One popular solution is to use Copy-on-write (COW). Under COW principle we have a pointer to the object (a reference, a handle), we clone the object it points to, we change the value of the pointer so now it points to the cloned object, and proceed with our mutating algorithm. All other references are still the same.
</p><p>JavaScript performs all of “the immutable object” things for strings, but it doesn’t do COW on strings because it uses even simpler trick: there is no way to modify a string. All strings are immutable by virtue of the language specification and the standard library it comes with. For example: str.replace(”a”, “b”) doesn’t modify str at all, it returns a new string with the replacement executed. Or not. If the pattern was not found I suspect it’ll return a pointer to the original string. Every “mutating” operation for strings actually returns a new object leaving the original string unchanged: replace, concat, slice, substr, split, and even exotic anchor, big, bold, and so on. 
</p><p><a class="external" href="http://lazutkin.com/|">http://lazutkin.com/</a>
</p>
<h3 name=".D0.94.D1.80.D1.83.D0.B3.D0.B8.D0.B5_.D1.82.D0.B8.D0.BF.D1.8B"> Другие типы </h3>
<p>JavaScript различает специальный объект <code>null</code> типа 'object', который выражает явное (программно-установленное) пустое значения, и значение <code>undefined</code> типа 'undefined', которое говорит о том, что значение еще не было назначено. 
</p>
<pre class="eval"> &gt;&gt; typeof null
 object
 &gt;&gt; typeof undefined
 undefined
</pre>
<p>Мы поговорим о переменных позже, но сейчас заметим, что в JavaScript можно объявить переменную не инициализируя ее. В этом случае значение переменной будет равно  <code>undefined</code>.
</p><p>В JavaScript есть логический тип с возможными значениями <code>true</code> и <code>false</code> (оба являются ключевыми словами в JavaScript).Любое значение может быть преобразовано к логическому в согласии со следующими правилами:
</p>
<ol><li> <code>false</code>, <code>0</code>, пустая строка (<code>""</code>), <code>NaN</code>, <code>null</code>, и <code>undefined</code> трактуются как <code>false</code>
</li><li> все остальное как <code>true</code>
</li></ol>
<p>Преобразование можно провести в явном виде с использованием функции <code>Boolean()</code>:
</p>
<pre class="eval">&gt; Boolean("")
false
&gt; Boolean(234)
true
</pre>
<p>эта возможность используется достаточно редко, поскольку такое преобразование производится автоматически в тех случаях когда ожидается логическое значение, как на пример в выражении <code>if</code>(см ниже). Поэтому  обычно просто говорят о "истиных значениях" и "ложных значениях" подразумевая в какое из логических значений оно преобразуется <code>true</code> или <code>false</code>. Приведем еще и слэнговые варианты "truthy" и "falsy".
</p><p>Конечно поддержаны стандартные логические операторы <code>&amp;&amp;</code> (логическое <i>и</i>), <code>||</code> (логическое <i>или</i>), и <code>!</code> (логическое <i>не</i>).
</p>
<h3 name=".D0.9F.D0.B5.D1.80.D0.B5.D0.BC.D0.B5.D0.BD.D0.BD.D1.8B.D0.B5"> Переменные </h3>
<p>Переменная в JavaScript объявляется с использованием ключевого слова <code><a href="ru/Core_JavaScript_1.5_Reference/Statements/var">var</a></code>
</p>
<pre class="eval">var a;
var name = "simon";
</pre>
<p>Еще раз напомним, что если вы не назначили значение переменной, то ее значение будет типа <code>undefined</code>.
<span class="comment">should note the absence of block-scoping in JS</span>
</p>
<h3 name=".D0.9E.D0.BF.D0.B5.D1.80.D0.B0.D1.82.D0.BE.D1.80.D1.8B"> Операторы </h3>
<p>В JavaScript есть стандартные математические операторы <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code> и <code>%</code> - последний это оператор возвращающий остатк от деления. Значения присваиваются оператором <code>=</code>, также есть операторы составного присваивания <code>+=</code> и <code>-=</code>. Они позволяют записать короче часто встречающиеся выражения  типа: <code>x = x <i>operator</i> y</code>.
</p>
<pre class="eval">x += 5
x = x + 5
</pre>
<p>Вы можете использовать <code>++</code> и<code>--</code> для увеличения или уменьшения значения переменной на единицу. Помещеть этот оператор можно как за символом переменной (<code>i++</code>) так и перед или (<code>++i</code>).
</p><p>Оператор <a href="ru/Core_JavaScript_1.5_Reference/Operators/String_Operators"><code>+</code> operator</a> применяется еще и для конкатенации строк:
</p>
<pre class="eval">&gt; "hello" + " world"
hello world
</pre>
<p>При добавлении строки к числу последнее автоматически конвертируется в строку (поскольку JavaScript  слабо типизированный язык, нужно быть очень осторожным ):
</p>
<pre class="eval">&gt; "3" + 4 + 5
345
&gt; 3 + 4 + "5"
75
</pre>
<p>Добавление пустой строки к чему-либо можно использовать, как преобразование к строке.
</p><p>В JavaScript <a href="ru/Core_JavaScript_1.5_Reference/Operators/Comparison_Operators">Comparisons</a> есть следующие операторы сравнения <code>&lt;</code>, <code>&gt;</code>, <code>&lt;=</code> и <code>&gt;=</code>. Их можно использовать и со строками и с числами. Сравнение значений уже несколько сложнее. Оператор двойного равенства производит преобразование типов, что может привести к несколько неожиданным результатам:
</p>
<pre class="eval">&gt; "dog" == "dog"
true
&gt; 1 == true
true
</pre>
<p>Чтобы этого избежать следует использовать оператор тройного равенства, который учитывает типы:
</p>
<pre class="eval">&gt; 1 === true
false
&gt; true === true
true
</pre>
<p>Есть также обратные операторы <code>!=</code> и <code>!==</code> для двойного и тройного равенства соответственно.
</p><p>В JavaScript также есть <a href="ru/Core_JavaScript_1.5_Reference/Operators/Bitwise_Operators">побитовые операторы</a>.
</p>
<h3 name="Control_structures"> Control structures </h3>
<p>JavaScript has a similar set of control structures to other languages in the C family. Conditional statements are supported by <code>if</code> and <code>else</code>; you can chain them together if you like:
</p>
<pre class="eval">var name = "kittens";
if (name == "puppies") {
  name += "!";
} else if (name == "kittens") {
  name += "!!";
} else {
  name = "!" + name;
}
name == "kittens!!"
</pre>
<p>JavaScript has <code>while</code> loops and <code>do-while</code> loops. The first is good for basic looping; the second for loops where you wish to ensure that the body of the loop is executed at least once:
</p>
<pre class="eval">while (true) {
  // an infinite loop!
}

do {
  var input = get_input();
} while (inputIsNotValid(input))
</pre>
<p>JavaScript's <code>for</code> loop is the same as that in C and Java: it lets you provide the control information for your loop on a single line.
</p>
<pre class="eval">for (var i = 0; i &lt; 5; i++) {
  // Will execute 5 times
}
</pre>
<p>The <code>&amp;&amp;</code> and <code>||</code> operators use short-circuit logic, which means whether they will execute their second operand is dependent on the first. This is useful for checking for null objects before accessing their attributes:
</p>
<pre class="eval">var name = o &amp;&amp; o.getName();
</pre>
<p>Or for setting default values:
</p>
<pre class="eval">var name = otherName || "default";
</pre>
<p>JavaScript has a tertiary operator for one-line conditional statements:
</p>
<pre class="eval">var allowed = (age &gt; 18) ? "yes" : "no";
</pre>
<p>The switch statement can be used for multiple branches based on a number or string:
</p>
<pre class="eval">switch(action) {
    case 'draw':
        drawit();
        break;
    case 'eat':
        eatit();
        break;
    default:
        donothing();
}
</pre>
<p>If you don't add a <code>break</code> statement, execution will "fall through" to the next level. This is very rarely what you want - in fact it's worth specifically labelling deliberate fallthrough with a comment if you really meant it to aid debugging:
</p>
<pre class="eval">switch(a) {
    case 1: // fallthrough
    case 2:
        eatit();
        break;
    default:
        donothing();
}
</pre>
<p>The default clause is optional. You can have expressions in both the switch part and the cases if you like; comparisons take place between the two using the <code>===</code> operator:
</p>
<pre class="eval">switch(1 + 3):
    case 2 + 2:
        yay();
        break;
    default:
        neverhappens();
}
</pre>
<h3 name="Objects"> Objects </h3>
<p>JavaScript objects are simply collections of name-value pairs. As such, they are similar to:
</p>
<ul><li> Dictionaries in Python
</li><li> Hashes in Perl and Ruby
</li><li> Hash tables in C and C++
</li><li> HashMaps in Java
</li><li> Associative arrays in PHP
</li></ul>
<p>The fact that this data structure is so widely used is a testament to its versatility. Since everything (bar core types) in JavaScript is an object, any JavaScript program naturally involves a great deal of hash table lookups. It's a good thing they're so fast!
</p><p>The "name" part is a JavaScript string, while the value can be any JavaScript value - including more objects. This allows you to build data structures of arbitrary complexity.
</p><p>There are two basic ways to create an empty object:
</p>
<pre class="eval">var obj = new Object();
</pre>
<p>And:
</p>
<pre class="eval">var obj = {};
</pre>
<p>These are semantically equivalent; the second is called object literal syntax, and is more convenient. Object literal syntax was not present in very early versions of the language which is why you see so much code using the old method.
</p><p>Once created, an object's properties can again be accessed in one of two ways:
</p>
<pre class="eval">obj.name = "Simon"
var name = obj.name;
</pre>
<p>And...
</p>
<pre class="eval">obj["name"] = "Simon";
var name = obj["name"];
</pre>
<p>These are also semantically equivalent. The second method has the advantage that the name of the property is provided as a string, which means it can be calculated at run-time. It can also be used to set and get properties with names that are <a href="ru/Core_JavaScript_1.5_Reference/Reserved_Words">reserved words</a>:
</p>
<pre class="eval">obj.for = "Simon"; // Syntax error, because 'for' is a reserved word
obj["for"] = "Simon"; // works fine
</pre>
<p>Object literal syntax can be used to initialise an object in its entirety:
</p>
<pre class="eval">var obj = {
    name: "Carrot",
    "for": "Max",
    details: {
        color: "orange",
        size: 12
    }
}
</pre>
<p>Attribute access can be chained together:
</p>
<pre class="eval">&gt; obj.details.color
orange
&gt; obj["details"]["size"]
12
</pre>
<h3 name="Arrays"> Arrays </h3>
<p>Arrays in JavaScript are actually a special type of object. They work very much like regular objects (numerical properties can naturally be accessed only using [] syntax) but they have one magic property called '<code>length</code>'. This is always one more than the highest index in the array.
</p><p>The old way of creating arrays is as follows:
</p>
<pre class="eval">&gt; var a = new Array();
&gt; a[0] = "dog";
&gt; a[1] = "cat";
&gt; a[2] = "hen";
&gt; a.length
3
</pre>
<p>A more convenient notation is to use an array literal:
</p>
<pre class="eval">&gt; var a = ["dog", "cat", "hen"];
&gt; a.length
3
</pre>
<p>Leaving a trailing comma at the end of an array literal is inconsistent across browsers, so don't do it.
</p><p>Note that <code>array.length</code> isn't necessarily the number of items in the array. Consider the following:
</p>
<pre class="eval">&gt; var a = ["dog", "cat", "hen"];
&gt; a[100] = "fox";
&gt; a.length
101
</pre>
<p>Remember - the length of the array is one more than the highest index.
</p><p>If you query a non-existent array index, you get <code>undefined</code>:
</p>
<pre class="eval">&gt; typeof(a[90])
undefined
</pre>
<p>If you take the above into account, you can iterate over an array using the following:
</p>
<pre class="eval">for (var i = 0; i &lt; a.length; i++) {
    // Do something with a[i]
}
</pre>
<p>This is slightly inefficient as you are looking up the length property once every loop. An improvement is this:
</p>
<pre class="eval">for (var i = 0, len = a.length; i &lt; len; i++) {
    // Do something with a[i]
}
</pre>
<p>An even nicer idiom is:
</p>
<pre class="eval">for (var i = 0, item; item = a[i]; i++) {
    // Do something with item
}
</pre>
<p>Here we are setting up two variables. The assignment in the middle part of the <code>for</code> loop is also tested for truthfulness - if it succeeds, the loop continues. Since <code>i</code> is incremented each time, items from the array will be assigned to item in sequential order. The loop stops when a "falsy" item is found (such as <code>undefined</code>).
</p><p>Note that this trick should only be used for arrays which you know do not contain "falsy" values (arrays of objects or <a href="ru/DOM">DOM</a> nodes for example). If you are iterating over numeric data that might include a 0 or string data that might include the empty string you should use the <code>i, j</code> idiom instead.
</p><p>Another way to iterate is to use the <code><a href="ru/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a></code> loop. Note that if someone added new properties to <code>Array.prototype</code>, they will also be iterated over by this loop:
</p>
<pre class="eval">for (var i in a) {
  // Do something with a[i]
}
</pre>
<p>If you want to append an item to an array, the safest way to do it is like this:
</p>
<pre class="eval">a[a.length] = item;                 // same as a.push(item);
</pre>
<p>Since <code>a.length</code> is one more than the highest index, you can be assured that you are assigning to an empty position at the end of the array.
</p><p>Arrays come with a number of methods:
</p>
<pre class="eval">a.toString(), a.toLocaleString(), a.concat(item, ..), a.join(sep),
a.pop(), a.push(item, ..), a.reverse(), a.shift(), a.slice(start, end),
a.sort(cmpfn), a.splice(start, delcount, [item]..), a.unshift([item]..)
</pre>
<ul><li> <code>concat</code> returns a new array with the items added on to it.
</li><li> <code>pop</code> removes and returns the last item
</li><li> <code>push</code> adds one or more items to the end (like our <code>ar{{mediawiki.external('ar.length')}}</code> idiom)
</li><li> <code>slice</code> returns a sub-array
</li><li> <code>sort</code> takes an optional comparison function
</li><li> <code>splice</code> lets you modify an array by deleting a section and replacing it with more items
</li><li> <code>unshift</code> prepends items to the start of the array
</li></ul>
<h3 name="Functions"> Functions </h3>
<p>Along with objects, functions are the core component in understanding JavaScript. The most basic function couldn't be much simpler:
</p>
<pre class="eval">function add(x, y) {
    var total = x + y;
    return total;
}
</pre>
<p>This demonstrates everything there is to know about basic functions. A JavaScript function can take 0 or more named parameters. The function body can contain as many statements as you like, and can declare its own variables which are local to that function. The <code>return</code> statement can be used to return a value at any time, terminating the function. If no return statement is used (or an empty return with no value), JavaScript returns <code>undefined</code>.
</p><p>The named parameters turn out to be more like guidelines than anything else. You can call a function without passing the parameters it expects, in which case they will be set to <code>undefined</code>.
</p>
<pre class="eval">&gt; add()
NaN // You can't perform addition on undefined
</pre>
<p>You can also pass in more arguments than the function is expecting:
</p>
<pre class="eval">&gt; add(2, 3, 4)
5 // added the first two; 4 was ignored
</pre>
<p>That may seem a little silly, but functions have access to an additional variable inside their body called <a href="ru/Core_JavaScript_1.5_Reference/Functions/arguments"><code>arguments</code></a>, which is an array-like object holding all of the values passed to the function. Let's re-write the add function to take as many values as we want:
</p>
<pre class="eval">function add() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i &lt; j; i++) {
        sum += arguments[i];
    }
    return sum;
}

&gt; add(2, 3, 4, 5)
14
</pre>
<p>That's really not any more useful than writing <code>2 + 3 + 4 + 5</code> though. Let's create an averaging function:
</p>
<pre class="eval">function avg() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i &lt; j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}
&gt; avg(2, 3, 4, 5)
3.5
</pre>
<p>This is pretty useful, but introduces a new problem. The <code>avg()</code> function takes a comma separated list of arguments - but what if you want to find the average of an array? You could just rewrite the function as follows:
</p>
<pre class="eval">function avgArray(arr) {
    var sum = 0;
    for (var i = 0, j = arr.length; i &lt; j; i++) {
        sum += arr[i];
    }
    return sum / arr.length;
}
&gt; avgArray([2, 3, 4, 5])
3.5
</pre>
<p>But it would be nice to be able to reuse the function that we've already created. Luckily, JavaScript lets you call a function and call it with an arbitrary array of arguments, using the <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply"><code>apply()</code></a> method of any function object.
</p>
<pre class="eval">&gt; avg.apply(null, [2, 3, 4, 5])
3.5
</pre>
<p>The second argument to <code>apply()</code> is the array to use as arguments; the first will be discussed later on. This emphasizes the fact that functions are objects too.
</p><p>JavaScript lets you create anonymous functions.
</p>
<pre class="eval">var avg = function() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i &lt; j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}
</pre>
<p>This is semantically equivalent to the <code>function avg()</code> form. It's extremely powerful, as it lets you put a full function definition anywhere that you would normally put an expression. This enables all sorts of clever tricks. Here's a way of "hiding" some local variables - like block scope in C:
</p>
<pre class="eval">&gt; var a = 1;
&gt; var b = 2;
&gt; (function() {
    var b = 3;
    a += b;
})();
&gt; a
4
&gt; b
2
</pre>
<p>JavaScript allows you to call functions recursively. This is particularly useful for dealing with tree structures, such as you get in the browser <a href="ru/DOM">DOM</a>.
</p>
<pre class="eval">function countChars(elm) {
    if (elm.nodeType == 3) { // TEXT_NODE
        return elm.nodeValue.length;
    }
    var count = 0;
    for (var i = 0, child; child = elm.childNodes[i]; i++) {
        count += countChars(child);
    }
    return count;
}
</pre>
<p>This highlights a potential problem with anonymous functions: how do you call them recursively if they don't have a name? The answer lies with the <code>arguments</code> object, which in addition to acting as a list of arguments also provides a property called <code>arguments.callee</code>. This always refers to the current function, and hence can be used to make recursive calls:
</p>
<pre class="eval">var charsInBody = (function(elm) {
    if (elm.nodeType == 3) { // TEXT_NODE
        return elm.nodeValue.length;
    }
    var count = 0;
    for (var i = 0, child; child = elm.childNodes[i]; i++) {
        count += arguments.callee(child);
    }
    return count;
})(document.body);
</pre>
<p>Since <code>arguments.callee</code> is the current function, and all functions are objects, you can use <code>arguments.callee</code> to save information across multiple calls to the same function. Here's a function that remembers how many times it has been called:
</p>
<pre class="eval">function counter() {
    if (!arguments.callee.count) {
        arguments.callee.count = 0;
    }
    return arguments.callee.count++;
}

&gt; counter()
0
&gt; counter()
1
&gt; counter()
2
</pre>
<h3 name=".D0.9F.D0.BE.D0.BB.D1.8C.D0.B7.D0.BE.D0.B2.D0.B0.D1.82.D0.B5.D0.BB.D1.8C.D1.81.D0.BA.D0.B8.D0.B5_.D0.BE.D0.B1.D1.8A.D0.B5.D0.BA.D1.82.D1.8B"> Пользовательские объекты </h3>
<p>В классичеких объектно-ориентированных языках, объект это коллекция данных и методов, оперирующих этими данными. Давайте рассмотрим объект person c полями  first и last name (имя фамилия).  И предположим нам надо иметь два метода для различных отображений полного имени: как "first last" или как "last, first". Мы можем сделать это с использованием объекта и функций вот так (не очень элегантно):
</p>
<pre class="eval">function makePerson(first, last) {
    return {
        first: first,
        last: last
    }
}
function personFullName(person) {
    return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
    return person.last + ', ' + person.first
}
&gt; s = makePerson("Simon", "Willison");
&gt; personFullName(s)
Simon Willison
&gt; personFullNameReversed(s)
Willison, Simon
</pre>
<p>Это работает, но выглядет кривовато. Если вы будете прграммировать в таком стиле то рано или поздно исчерпаете все удобные имена для функций в глобальном пространстве имен. Нам бы хотелось как-то присоединять имена функций к объектам. и поскольку функция является объектом это можно легко сделать:
</p>
<pre class="eval">function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}
&gt; s = makePerson("Simon", "Willison")
&gt; s.fullName()
Simon Willison
&gt; s.fullNameReversed()
Willison, Simon
</pre>
<p>Здесь мы встречаемся с ключевым словом '<code><a href="ru/Core_JavaScript_1.5_Reference/Operators/Special_Operators/this_Operator">this</a></code>'.  При использовании внутри функции '<code>this</code>' ссылается на текущий объект. Чему будет равно '<code>this</code>' на самом деле зависит от того как была вызвана функция. Если мы вызываем функцию через  <a href="ru/Core_JavaScript_1.5_Reference/Operators/Member_Operators">точечную нотацию или квадратные скобки</a>
, тогда '<code>this</code>' указывает на этот объект. Если просто вызвать функцию, то '<code>this</code>' будет указывать на глобальный объект. Непонимание этого может привести к неприятным последствиям:
</p>
<pre class="eval">&gt; s = makePerson("Simon", "Willison")
&gt; var fullName = s.fullName;
&gt; fullName()
undefined undefined
</pre>
<p>Когда мы вызываем <code>fullName()</code>, в роли '<code>this</code>' выступает глобальный объект. И поскольку у нас нет глобальных переменных <code>first</code> и <code>last</code> для каждой из них мы получаем <code>undefined</code>.
</p><p>Мы можем использовать '<code>this</code>' для усовершенствования нашей функции <code>makePerson</code>:
</p>
<pre class="eval">function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = function() {
        return this.first + ' ' + this.last;
    }
    this.fullNameReversed = function() {
        return this.last + ', ' + this.first;
    }
}
var s = new Person("Simon", "Willison");
</pre>
<p>Здесь мы видим ключевое слово '<code><a href="ru/Core_JavaScript_1.5_Reference/Operators/Special_Operators/new_Operator">new</a></code>'. <code>new</code> связан с  '<code>this</code>'. Итак чтоже делает <code>new</code>. Он создает пустой объект и вызывает указанную функцию, внутри которой '<code>this</code>' указывает на этот новый объект. Функции которые будут использоватся вместе с '<code>new</code>' называются 'конструкторами'. Обычно название таких функций начинается с заглавной буквы, и напоминает о том, что функция должна быть использована вместе с <code>new</code>.
</p><p>Наш объект person стал лучше. Но есть еще некоторые углы. Каждый раз когда мы создаем объект мы создаем по две новых функции внутри него. А нельзя ли разделть эти функции между всеми объектами?
</p>
<pre class="eval">function personFullName() {
    return this.first + ' ' + this.last;
}
function personFullNameReversed() {
    return this.last + ', ' + this.first;
}
function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = personFullName;
    this.fullNameReversed = personFullNameReversed;
}
</pre>
<p>Так немного лучше - функции будут создаваться только один раз, а ссылки на них назначаются в конструкторе. Можноли сделать еще лучше? Да:
</p>
<pre class="eval">function Person(first, last) {
    this.first = first;
    this.last = last;
}
Person.prototype.fullName = function() {
    return this.first + ' ' + this.last;
}
Person.prototype.fullNameReversed = function() {
    return this.last + ', ' + this.first;
}
</pre>
<p><code>Person.prototype</code> это объект общий для всех объектов созданых с помощью <code>Person</code>. Он входит в "prototype chain" (цепочку поиска):
каждый раз когда вы пытаетесь получить свозство объекта  <code>Person</code>, которое у него отсутствует, JavaScript проверяет нет ли такого свойства у <code>Person.prototype</code>. Таким образом, все что будет присвоено <code>Person.prototype</code> становится доступно всем объектам порожденным конструктором <code>Person</code>, в том числе и через объект <code>this</code>.
</p><p>Это необычайно мощное средство. В JavaScript  вы можете модифицировать prototype (прототипы) объектов в любое время и в любом месте программы, что означает что вы можете добавлять методы к объектам в процессе выполнения программы.
</p>
<pre class="eval">&gt; s = new Person("Simon", "Willison");
&gt; s.firstNameCaps();
TypeError on line 1: s.firstNameCaps is not a function
&gt; Person.prototype.firstNameCaps = function() {
    return this.first.toUpperCase()
}
&gt; s.firstNameCaps()
SIMON
</pre>
<p>Вы можете расширить даже prototype (прототипы) встроенных JavaScript объектов. Для примера добавим метод переворачивающий строку к <code>String</code>:
</p>
<pre class="eval">&gt; var s = "Simon";
&gt; s.reversed()
TypeError on line 1: s.reversed is not a function
&gt; String.prototype.reversed = function() {
    var r = "";
    for (var i = this.length - 1; i &gt;= 0; i--) {
        r += this[i];
    }
    return r;
}
&gt; s.reversed()
nomiS
</pre>
<p>Наш метод будет работать даже для строковых литералов!
</p>
<pre class="eval">&gt; "This can now be reversed".reversed()
desrever eb won nac sihT
</pre>
<p>As I mentioned before, the prototype forms part of a chain. The root of that chain is <code>Object.prototype</code>, whose methods include <code>toString()</code> - it is this method that is called when you try to represent an object as a string. This is useful for debugging our <code>Person</code> objects:
</p>
<pre class="eval">&gt; var s = new Person("Simon", "Willison");
&gt; s
[object Object]
&gt; Person.prototype.toString = function() {
    return '&lt;Person: ' + this.fullName() + '&gt;';
}
&gt; s
&lt;Person: Simon Willison&gt;
</pre>
<p>Remember how <code>avg.apply()</code> had a null first argument? We can revisit that now. The first argument to <code>apply()</code> is the object that should be treated as '<code>this</code>'. For example, here's a trivial implementation of '<code>new</code>':
</p>
<pre class="eval">function trivialNew(constructor) {
    var o = {}; // Create an object
    constructor.apply(o, arguments);
    return o;
}
</pre>
<p>This isn't an exact replica of <code>new</code> as it doesn't set up the prototype chain. <code>apply()</code> is difficult to illustrate - it's not something you use very often, but it's useful to know about.
</p><p><code>apply()</code> has a sister function named <a href="ru/Core_JavaScript_1.5_Reference/Global_Objects/Function/call"><code>call</code></a>, which again lets you set '<code>this</code>' but takes an expanded argument list as opposed to an array.
</p>
<pre class="eval">function lastNameCaps() {
    return this.last.toUpperCase();
}
var s = new Person("Simon", "Willison");
lastNameCaps.call(s);
// Is the same as:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps();
</pre>
<h3 name="Inner_functions"> Inner functions </h3>
<p>JavaScript function declarations are allowed inside other functions. We've seen this once before, with an earlier <code>makePerson()</code> function. An important detail of nested functions in JavaScript is that they can access variables in their parent function's scope:
</p>
<pre class="eval">function betterExampleNeeded() {
    var a = 1;
    function oneMoreThanA() {
        return a + 1;
    }
    return oneMoreThanA();
}
</pre>
<p>This provides a great deal of utility in writing more maintainable code. If a function relies on one or two other functions that are not useful to any other part of your code, you can nest those utility functions inside the function that will be called from elsewhere. This keeps the number of functions that are in the global scope down, which is always a good thing.
</p><p>This is also a great counter to the lure of global variables. When writing complex code it is often tempting to use global variables to share values between multiple functions - which leads to code that is hard to maintain. Nested functions can share variables in their parent, so you can use that mechanism to couple functions together when it makes sense without polluting your global namespace - 'local globals' if you like. This technique should be used with caution, but it's a useful ability to have.
</p>
<h3 name="Closures"> Closures </h3>
<p>This leads us to one of the most powerful abstractions that JavaScript has to offer - but also the most potentially confusing. What does this do?
</p>
<pre class="eval">function makeAdder(a) {
    return function(b) {
        return a + b;
    }
}
x = makeAdder(5);
y = makeAdder(20);
x(6)
?
y(7)
?
</pre>
<p>The name of the <code>makeAdder</code> function should give it away: it creates new 'adder' functions, which when called with one argument add it to the argument that they were created with.
</p><p>What's happening here is pretty much the same as was happening with the inner functions earlier on: a function defined inside another function has access to the outer function's variables. The only difference here is that the outer function has returned, and hence common sense would seem to dictate that its local variables no longer exist. But they <i>do</i> still exist - otherwise the adder functions would be unable to work. What's more, there are two different "copies" of <code>makeAdder</code>'s local variables - one in which <code>a</code> is 5 and one in which <code>a</code> is 20. So the result of those function calls is as follows:
</p>
<pre class="eval">x(6) // returns 11
y(7) // returns 27
</pre>
<p>Here's what's actually happening. Whenever JavaScript executes a function, a 'scope' object is created to hold the local variables created within that function. It is initialised with any variables passed in as function parameters. This is similar to the global object that all global variables and functions live in, but with a couple of important differences: firstly, a brand new scope object is created every time a function starts executing, and secondly, unlike the global object (which in browsers is accessible as window) these scope objects cannot be directly accessed from your JavaScript code. There is no mechanism for iterating over the properties of the current scope object for example.
</p><p>So when <code>makeAdder</code> is called, a scope object is created with one property: <code>a</code>, which is the argument passed to the <code>makeAdder</code> function. <code>makeAdder</code> then returns a newly created function. Normally JavaScript's garbage collector would clean up the scope object created for <code>makeAdder</code> at this point, but the returned function maintains a reference back to that scope object. As a result, the scope object will not be garbage collected until there are no more references to the function object that <code>makeAdder</code> returned.
</p><p>Scope objects form a chain called the scope chain, similar to the prototype chain used by JavaScript's object system.
</p><p>A closure is the combination of a function and the scope object in which it was created.
</p><p>Closures let you save state - as such, they can often be used in place of objects.
</p>
<h3 name="Memory_leaks"> Memory leaks </h3>
<p>An unfortunate side effect of closures is that they make it trivially easy to leak memory in Internet Explorer. JavaScript is a garbage collected language - objects are allocated memory upon their creation and that memory is reclaimed by the browser when no references to an object remain. Objects provided by the host environment are handled by that environment.
</p><p>Browser hosts need to manage a large number of objects representing the HTML page being presented - the objects of the <a href="ru/DOM">DOM</a>. It is up to the browser to manage the allocation and recovery of these.
</p><p>Internet Explorer uses its own garbage collection scheme for this, separate from the mechanism used by JavaScript. It is the interaction between the two that can cause memory leaks.
</p><p>A memory leak in IE occurs any time a circular reference is formed between a JavaScript object and a native object. Consider the following:
</p>
<pre class="eval">function leakMemory() {
    var el = document.getElementById('el');
    var o = { 'el': el };
    el.o = o;
}
</pre>
<p>The circular reference formed above creates a memory leak; IE will not free the memory used by <code>el</code> and <code>o</code> until the browser is completely restarted.
</p><p>The above case is likely to go unnoticed; memory leaks only become a real concern in long running applications or applications that leak large amounts of memory due to large data structures or leak patterns within loops.
</p><p>Leaks are rarely this obvious - often the leaked data structure can have many layers of references, obscuring the circular reference.
</p><p>Closures make it easy to create a memory leak without meaning to. Consider this:
</p>
<pre class="eval">function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
}
</pre>
<p>The above code sets up the element to turn red when it is clicked. It also creates a memory leak. Why? Because the reference to <code>el</code> is inadvertently caught in the closure created for the anonymous inner function. This creates a circular reference between a JavaScript object (the function) and a native object (<code>el</code>).
</p><p>There are a number of workarounds for this problem. The simplest is this:
</p>
<pre class="eval">function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
    el = null;
}
</pre>
<p>This works by breaking the circular reference.
</p><p>Surprisingly, one trick for breaking circular references introduced by a closure is to add another closure:
</p>
<pre class="eval">function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
    }
    (function() {
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}
</pre>
<p>The inner function is executed straight away, and hides its contents from the closure created with <code>clickHandler</code>.
</p><p>Another good trick for avoiding closures is breaking circular references during the <code>window.onunload</code> event. Many event libraries will do this for you. Note that doing so disables <a href="ru/Using_Firefox_1.5_caching">bfcache in Firefox 1.5</a>, so you should not register an <code>unload</code> listener in Firefox, unless you have other reasons to do so.
</p>
<div class="noinclude">
</div>
{{ wiki.languages( { "en": "en/A_re-introduction_to_JavaScript", "fr": "fr/Une_r\u00e9introduction_\u00e0_JavaScript", "it": "it/Una_re-introduzione_a_Javascript", "ja": "ja/A_re-introduction_to_JavaScript", "ko": "ko/A_re-introduction_to_JavaScript", "pl": "pl/JavaScript/Na_pocz\u0105tek", "zh-cn": "cn/A_re-introduction_to_JavaScript" } ) }}
Вернуть эту версию