Ранее в этой главе мы обсуждали строки, и тогда я обратил ваше внимание на странную особенность этого типа данных: для работы со строками используется объектная нотация.1 Например, типичная операция со строками может выгля_ деть следующим образом:
var s = "These are the times that try people's souls.";
var last_word = s.substring(s.lastIndexOf(" ")+1, s.length);
Если бы вы не знали, то могли бы подумать, что s – это объект, и вы вызываете методы и читаете значения свойств этого объекта.
Что же происходит? Являются ли строки объектами или это элементарный тип данных? Оператор typeof (см. главу 5) убеждает нас, что строки имеют строко_ вый тип данных, отличный от объектного типа. Почему же тогда для манипуля_ ций со строками используется объектная нотация?
Дело в том, что для каждого из трех базовых типов данных определен соответст_ вующий класс объектов. То есть помимо поддержки числовых, строковых и ло_ гических типов данных JavaScript поддерживает классы Number, String и Boolean. Эти классы представляют собой «обертки» для базовых типов данных. Обертка (wrapper) содержит такое же значение базового типа, но кроме этого определяет еще свойства и методы, которые могут использоваться для манипуляций с этим значением.
JavaScript может гибко преобразовывать один тип в другой. Когда мы использу_ ем строку в объектном контексте, т. е. когда пытаемся обратиться к свойству или методу строки, JavaScript создает внутри себя объект_обертку для строкового значения. Этот объект String используется вместо базового строкового значения. Для объекта определены свойства и методы, поэтому удается задействовать зна_
1 Этот раздел содержит достаточно сложный материал, который при первом про_ чтении можно пропустить.
3.13. Объекты*обертки для элементарных типов данных
чение базового типа в объектном контексте. То же самое, конечно, верно и для других базовых типов и соответствующих им объектов_оберток; мы просто не ра_ ботаем с другими типами в объектном контексте так же часто, как со строками.
Следует отметить, что объект String, созданный при использовании строки в объ_ ектном контексте, временный – он служит для того, чтобы обеспечить доступ к свойству или методу, после чего необходимость в нем отпадает, и потому он ути_ лизируется системой. Предположим, что s – это строка, и мы определяем длину строки с помощью следующего предложения:
var len = s.length;
Здесь s остается строкой, и ее исходное значение не меняется. Создается новый временный объект String, позволяющий обращаться к свойству length, а затем этот объект удаляется, не меняя исходного значения переменной s. Если эта схе_ ма кажется вам одновременно и элегантной, и неестественно сложной, вы пра_ вы. Однако обычно реализации JavaScript выполняют внутреннее преобразова_ ние очень эффективно, и вам не стоит об этом беспокоиться.
Чтобы явно использовать объект String в своей программе, надо создать постоян_ ный объект, который не будет автоматически удаляться системой. Объекты String создаются так же, как и другие объекты, – с помощью оператора new. Например:
var s = "hello world"; // Значение строкового типа var S = new String("Hello World"); // Объект String
Что же можно делать с созданным объектом S типа String? Ничего такого, что нельзя сделать с соответствующим значением базового типа. Если мы воспользу_ емся оператором typeof, он сообщит нам, что S – это объект, а не строковое значе_ ние, но кроме того, мы не увидим различий между базовым строковым значени_ ем и объектом String.1 Как мы уже видели, строки автоматически преобразуются в объекты String, когда это требуется. Оказывается, что обратное тоже верно. Ко_ гда мы используем объект String там, где предполагается значение базового стро_ кового типа, JavaScript автоматически преобразует объект String в строку. По_ этому, если мы используем наш объект String с оператором +, для выполнения операции конкатенации создается временное значение базового строкового типа:
msg = S + '!';
Имейте в виду, все, что говорилось в этом разделе о строковых значениях и объ_ ектах String, также применимо к числовым и логическим значениям и соответ_ ствующим объектам Number и Boolean. Более подробную информацию об этих классах можно получить из соответствующих статей в третьей части книги.
Наконец, следует отметить, что любые строки, числа или логические значения могут быть преобразованы в соответствующий объект_обертку с помощью функ_ ции Object():
var number_wrapper = Object(3);
1 Однако при этом метод eval() рассматривает строковые значения и объекты String по_разному, и если непреднамеренно передать ему объект String вместо значения базового строкового типа, он поведет себя не так, как вы предполагаете.