14 января 2009

Недавно на хабрахабре проскочила замечательная статья по той теме на которую я сам хотел написать. Статья на мой взгляд хороша настолько что лучше я врятли сделаю. А посему хочу продолжить эпопею JavaScript в моем блоге тупым копипастом этой статейки :)

Объекты в JavaScript

Во многих статьях встречается фраза «В JavaScript — всё объект». Технически это не совсем верно, однако производит должное впечатление на новичков :)

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

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


Итак, в JavaScript есть 6 базовых типов данных — это Undefined (обозначающий отсутствие значения), Null, Boolean (булев тип), String (строка), Number (число) и Object (объект).
При этом первые 5 являются примитивными типами данных, а Object — нет. Кроме того, условно можно считать, что у типа Object есть «подтипы»: массив (Array), функция (Function), регулярное выражение (RegExp) и другие.
Это несколько упрощенное описание, но на практике обычно достаточное.

Кроме того, примитивные типы String, Number и Boolean определенным образом связаны с не-примитивными «подтипами» Object: String, Number и Boolean соответственно.
Это означает, что строку ‘Hello, world’, например, можно создать и как примитивное значение, и как объект типа String.
Если вкратце, то это сделано для того, чтобы программист мог и в работе с примитивными значениями использовать методы и свойства, как будто это объекты. А подробнее об этом можно будет прочитать в соответствующем разделе данной статьи.

Работа по ссылке

Ссылка — это средство доступа к объекту под различными именами. Работа с любыми объектами ведется исключительно по ссылке.
Продемонстрируем это на примере:

    test=function() {alert('Hello!')} //Создадим функцию {alert('Hello!')} (а функция, как мы помним, является полноправным объектом) и сделаем переменную test ссылкой на нее
    test_link=test; //test_link теперь тоже ссылается на нашу функцию
    test(); //Hello!
    test_link(); //Hello!

Как мы видим, и первая ссылка, и вторая дают один и тот же результат.
Необходимо осознать, что у нас нет никакой функции с именем test, и что переменная test не является какой-то «главной» или «основной» ссылкой, а «test_link» — второстепенной.

Наша функция, как и любой другой объект — просто область в памяти, и все ссылки на эту область абсолютно равнозначны. Более того, объект может вообще не иметь ссылок — в таком случае он называется анонимным, и может быть использован только непосредственно сразу после создания (например, передан в функцию), иначе доступ к нему получить будет невозможно и в скором времени он будет уничтожен сборщиком мусора (garbage collection), который и занимается тем, что удаляет объекты без ссылок.

Посмотрим, почему так важно это понимать:

    test={prop: 'sometext'} //Создаем объект со свойством prop
    test_link=test; //Создаем еще одну ссылку на этот объект

    alert(test.prop); //sometext
    alert(test_link.prop); //sometext

    //Изменяем свойство объекта
    test_link.prop='newtext';

    alert(test.prop); //newtext
    alert(test_link.prop); //newtext
    /*Можно было бы сказать, что свойство изменилось и там и тут - но это не так.
    Объект-то один. Так что свойство изменилось в нем один раз, а ссылки просто продолжают указывать туда, куда и указывают. */

    //Добавляем новое свойство и удаляем старое
    test.new_prop='hello';
    delete test.prop;

    alert(test_link.prop); //undefined - такого свойства больше нет
    alert(test_link.new_prop); //hello - что и следовало ожидать

    //Удаляем ссылку
    delete test;
    alert(test.new_prop);
    /*В этом месте скрипт выкинет ошибку, потому что test уже не существует, и test.new_prop не существует тем более */
    alert(test_link.new_prop); //hello
    /* а вот тут все в порядке, ведь мы удалили не сам объект, а лишь ссылку на него. Теперь на наш объект указывает единственная ссылка test_link */

    //Создаем новый объект
    test=test_link; //Сперва снова создадим ссылку test
    test_link={prop: 'sometext'} //А вот и новый объект

    alert(test_link.prop); //sometext
    alert(test.prop); //undefined
    /* Cоздание нового объекта разрывает ссылочную связь, и теперь test и test_link указывают на разные объекты.
    Фактически, это равносильно удалению ссылки test_link и созданию ее заново, но уже указывающей на другой объект */
    alert(test.new_prop); //hello - теперь test содержит ссылку на наш самый первый объект

Такое поведение объектов часто вызывает массу вопросов у начинающих разработчиков, так что надеюсь данный текст внесет некоторую ясность. Если же мы хотим создать действительно новую, независимую копию объекта, а не ссылку — то единственный способ сделать это — создать новый объект и скопировать туда требуемые свойства.

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

Примитивные значения

Как я упоминал выше, типы данных String и Number могут быть как объектами, так и примитивными значениями.

    obj=new String('hello'); //Создаем строку как объект
    simple='hello'; //Создаем примитивное значение

    alert(obj); //hello
    alert(simple); //hello - пока все предсказуемо

    alert(obj.length); //6 - у объекта типа String есть свойство length, хранящее длину строки
    alert(simple.length); //6
    /* Хотя simple - не объект, мы можем обращаться к тому же набору свойств, что и у объекта типа String. Это довольно удобно */

    obj.prop='text';
    simple.prop='text';

    alert(obj.prop); //text - раз obj обычный объект, то мы можем запросто придать ему еще одно свойство
    alert(simple.prop); //undefined - а вот simple не объект, и этот номер у нас не пройдет

Все то же самое справедливо и для типа Number, и для Boolean (ну, кроме того, что в них нет свойства length, а есть ряд других замечательных свойств).
Использование строк и чисел как объектов не несет в себе никакой практической пользы, т.к. примитивные значения удобнее в работе, но сохраняют при этом весь необходимый функционал. Тем не менее, для полноты картины необходимо понимать этот механизм.

Не стоит путать использование примитивных значений с использованием литералов — например, независимо от того, создаем мы массив как «test=new Array()» или как «test=[]», в результате все равно будет один и тот же объект. Никаких примитивных значений мы не получим.

Возможно это тоже будет интересно
Комментариев нет

Оставить комментарий