Несколько раз в первой и второй главах этой книги мы упоминали различные нативные объекты, такие как String
и Number
. Давай рассмотрим их детальнее.
Вот список часто используемых стандартных объектов:
String()
Number()
Boolean()
Array()
Object()
Function()
RegExp()
Date()
Error()
Symbol()
-- добавлено в ES6!
Как вы можете заметить, на деле все они являются встроенными функциями.
Если вы пришли в JS из языков подобных Java, то, наверное, заметили, что выражение String()
схоже с конструктором String(..)
, используемым в Java для создания строк. Скоро станет понятно, что можно делать подобные вещи:
var s = new String( "Hello World!" );
console.log( s.toString() ); // "Hello World!"
На самом деле каждый из этих встроенных объектов может быть использован в качестве конструктора. Но продукт его вызова может быть не таким, как вам кажется.
var a = new String( "abc" );
typeof a; // "object" ... не "String"
a instanceof String; // true
Object.prototype.toString.call( a ); // "[object String]"
Результатом создания значений с помощью вызова конструктора (new String("abc")
) является объект-обертка над примитивным ("abc"
) значением.
Вызов typeof
показывает, что эти объекты не имеют какой-то уникальный тип
, точнее определить их подтипами типа object
.
Эту обертку над значением можно наблюдать с помощью:
console.log( a );
Результаты вывода могут отличаться в разных браузерах, так как консоли разработчика вправе выбирать как сериализировать объекты для инспектирования их разработчиками.
Замечание: В момент написания этой книги последняя версия Chrome печатает что-то вроде этого: String {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}
. Но старые версии Chrome печатали просто строку: String {0: "a", 1: "b", 2: "c"}
. Последний Firefox на данный момент выводит String ["a", "b", "c"]
, но до этого печатал курсивом "abc"
, на который можно было кликнуть и открыть окно инспектора. Конечно, все это быстро меняется, и ваш опыт может сильно разниться с приведенным в книге.
Цель создания обертки над строчкой "abc"
с помощью new String("abc")
- не только примитивное значение "abc"
.
Значения, для которых typeof
возвращает "object"
(как, например, массив), дополнительно помечаются внутренним свойством [[Class]]
(думайте об этом больше как о внутренней классификации, нежели о классах из традиционного ООП). Это свойство не имеет прямого доступа, но его его можно обнаружить, одолжив стандартный метод Object.prototype.toString()
и вызвав его с желаемым значением. К примеру:
Object.prototype.toString.call( [1,2,3] ); // "[object Array]"
Object.prototype.toString.call( /regex-literal/i ); // "[object RegExp]"
Видно, что для массива в этом примере, внутренний [[Class]]
имеет значение "Array"
, а для регулярного выражения "RegExp"
. В большинстве случаев, значение внутреннего свойства [[Class]]
соответствует стандартному встроенному конструктору (см. ниже), связанного со значением, хотя это не всегда так.
А что насчет примитивных значений? Рассмотрим, null
и undefined
:
Object.prototype.toString.call( null ); // "[object Null]"
Object.prototype.toString.call( undefined ); // "[object Undefined]"
Вы заметите, что нет собственных конструкторов Null() или Undefined(), но Null и Undefined, тем не менее, являются внутренними общедоступными значениями [[Class]]
.
Но для других простых примитивов, таких как string
, number
и boolean
, на самом деле срабатывает другое поведение, которое обычно называется "упаковкой" (см. раздел "Обертки упаковки" далее):
Object.prototype.toString.call( "abc" ); // "[object String]"
Object.prototype.toString.call( 42 ); // "[object Number]"
Object.prototype.toString.call( true ); // "[object Boolean]"
В этом фрагменте каждый из простых примитивов автоматически упаковывается соответствующими объектными оболочками, поэтому "String"
, "Number"
и "Boolean"
раскрываются как соответствующие внутренние [[Class]]
значения.
Примечание: Поведение toString()
и [[Class]]
, как показано здесь, немного изменилось с ES5 на ES6, но мы рассмотрим эти детали в заголовке ES6 и далее этой серии. .
Эти оболочки объектов служат очень важной цели. Примитивные значения не имеют свойств или методов, поэтому для доступа к .length
или .toString()
вам нужна оболочка объекта вокруг значения. К счастью, JS автоматически упаковывает (или обертывает) примитивное значение для выполнения таких обращений.
var a = "abc";
a.length; // 3
a.toUpperCase(); // "ABC"
Итак, если вы собираетесь регулярно обращаться к этим свойствам/методам строковых значений, например, к условию i < a.length
в цикле for
, может показаться, что имеет смысл просто иметь объектную форму значения с самого начала, поэтому движку JS не нужно неявно создавать его для вас.
Но оказывается, это плохая идея. Браузеры уже давно оптимизировали производительность в таких распространенных случаях, как .length
, что означает, что ваша программа фактически будет работать медленнее, если вы попытаетесь "предварительно оптимизировать" непосредственно с помощью формы объекта (которая не есть путём к оптимизации).
В общем, нет никакой причины использовать форму объекта напрямую. Лучше просто позволить боксу происходить неявно там, где это необходимо. Другими словами, никогда не делайте такие вещи, как new String("abc")
, new Number(42)
и т. д. Всегда предпочитайте использовать литеральные примитивные значения "abc"
и 42
.
Есть некоторые подводные камни, связанные с непосредственным использованием объектов-оболочек, о которых вам следует знать, если вы решите когда-либо их использовать.
Например, рассмотрите Boolean
обёртку:
var a = new Boolean( false );
if (!a) {
console.log( "Oops" ); // никогда не запустится
}
Проблема в том, что вы создали объектную оболочку вокруг значения false
, но сами объекты являются "истинными" (см. главу 4), поэтому использование объекта ведет себя противоположно использованию самого лежащего в основе значения false
, что весьма вопреки обычному ожиданию.
Если вы хотите вручную упаковать примитивное значение, вы можете использовать функцию Object(..)
(без ключевого слова new
):
var a = "abc";
var b = new String( a );
var c = Object( a );
typeof a; // "string"
typeof b; // "object"
typeof c; // "object"
b instanceof String; // true
c instanceof String; // true
Object.prototype.toString.call( b ); // "[object String]"
Object.prototype.toString.call( c ); // "[object String]"
Опять же, прямое использование упакованных объектов-оболочек (например, b
и c
выше) обычно не рекомендуется, но могут быть некоторые редкие случаи, когда вы столкнетесь с тем, что они могут быть полезны.
Если у вас есть оболочка объекта и вы хотите получить базовое примитивное значение, вы можете использовать метод valueOf()
:
var a = new String( "abc" );
var b = new Number( 42 );
var c = new Boolean( true );
a.valueOf(); // "abc"
b.valueOf(); // 42
c.valueOf(); // true
Распаковка также может происходить неявно, при использовании значения оболочки объекта способом, требующим примитивного значения. Этот процесс (принуждение) будет рассмотрен более подробно в главе 4, но кратко:
var a = new String( "abc" );
var b = a + ""; // `b` имеет распакованное примитивное значение "abc"
typeof a; // "object"
typeof b; // "string"
Для массива
, объекта
, функции
и значений регулярных выражений почти повсеместно предпочтительно использовать литеральную форму для создания значений, но литеральная форма создает тот же тип объекта, что и форма конструктора ( то есть нет неупакованного значения).
Как мы видели выше с другими стандартными объектами, этих форм-конструкторов обычно следует избегать, если вы действительно не уверены, что они вам нужны, в основном потому, что они вводят исключения и ловушки, с которыми вы, вероятно, не хотите иметь дело.
var a = new Array( 1, 2, 3 );
a; // [1, 2, 3]
var b = [1, 2, 3];
b; // [1, 2, 3]
Примечание: Конструктор Array(..)
не требует ключевого слова new
перед ним. Если вы его опустите, он будет вести себя так, как будто вы его все равно использовали. Таким образом, «Массив (1,2,3)» — это тот же результат, что и «новый массив (1,2,3)».
Конструктор Array
имеет специальную форму, в которой, если передается только один аргумент number
, вместо того, чтобы предоставлять это значение как содержимое массива, оно берется как длина для «предварительного размера массива» (ну, вроде как).
Это ужасная идея. Во-первых, вы можете случайно споткнуться об эту форму, так как ее легко забыть.
Но что еще более важно, нет такой вещи, как предварительное определение размера массива. Вместо этого вы создаете пустой массив, но устанавливаете для свойства length
массива указанное числовое значение.
Массив, который не имеет явных значений в своих слотах, но имеет свойство length
, которое подразумевает, что слоты существуют, является странным экзотическим типом структуры данных в JS с очень странным и запутанным поведением. Возможность создания такого значения исходит исключительно из старых, устаревших, исторических функций («массивоподобные объекты», такие как объект «аргументы»).
Примечание. Массив, в котором есть хотя бы один «пустой слот», часто называют «разреженным массивом».
Дело не в том, что это еще один пример, когда консоли разработчиков браузера различаются в зависимости от того, как они представляют такой объект, что порождает еще большую путаницу.
Например:
var a = new Array( 3 );
a.length; // 3
a;
Сериализация a
в Chrome (на момент написания): [ undefined x 3 ]
. Это действительно прискорбно. Это означает, что в слотах этого массива есть три неопределенных
значения, хотя на самом деле слоты не существуют (так называемые "пустые слоты" - тоже плохое название!)
Чтобы визуализировать разницу, попробуйте следующее:
var a = new Array( 3 );
var b = [ undefined, undefined, undefined ];
var c = [];
c.length = 3;
a; // [empty × 3]
b; // [undefined, undefined, undefined]
c; // [empty × 3]
Примечание: Как видно из c
в этом примере, пустые слоты в массиве могут появиться после создания массива. Изменяя «длину» массива так, чтобы он превышал количество фактически определенных значений слотов, вы неявно вводите пустые слоты. На самом деле, вы могли бы даже вызвать delete b[1]
в приведенном выше фрагменте, и это добавит пустой слот в середину b
.
Для b
(в настоящее время в Chrome) вы найдете [ undefined, undefined, undefined ]
в качестве сериализации, в отличие от [ undefined x 3 ]
для a
и c
. Озадачены? Да, как и все остальные.
Хуже того, на момент написания Firefox сообщает [ , , , ]
для a
и c
. Вы поняли, почему это так запутанно? Посмотрите внимательно. Три запятые означают четыре слота, а не три слота, как мы ожидали.
Что!? Firefox добавляет здесь дополнительный ,
в конце своей сериализации, потому что начиная с ES5 разрешены конечные запятые в списках (значения массива, списки свойств и т. д.) (и, таким образом, отбрасываются и игнорируются). Таким образом, если бы вы ввели значение [ , , ]
в свою программу или консоль, вы фактически получили бы базовое значение, похожее на [ , , ]
(то есть массив с тремя пустыми слотами). Этот выбор, хотя и сбивает с толку при чтении консоли разработчика, защищается тем, что вместо этого делает поведение копирования и вставки точным.
Если вы сейчас качаете головой или закатываете глаза, вы не одиноки! Пожимает плечами.
К сожалению, это становится хуже. a
и b
из приведенного выше фрагмента кода больше, чем просто сбивающий с толку вывод консоли, на самом деле ведут себя одинаково в некоторых случаях но по-разному в других:
a.join( "-" ); // "--"
b.join( "-" ); // "--"
a.map(function(v,i){ return i; }); // [ undefined x 3 ]
b.map(function(v,i){ return i; }); // [ 0, 1, 2 ]
Фу.
Вызов a.map(..)
терпит неудачу, потому что слоты на самом деле не существуют, поэтому map(..)
нечего перебирать. join(..)
работает по-другому. По сути, мы можем думать о его реализации примерно так:
function fakeJoin(arr,connector) {
var str = "";
for (var i = 0; i < arr.length; i++) {
if (i > 0) {
str += connector;
}
if (arr[i] !== undefined) {
str += arr[i];
}
}
return str;
}
var a = new Array( 3 );
fakeJoin( a, "-" ); // "--"
Как видите, join(..)
работает, просто предполагая, что слоты существуют, и зацикливается до значения length
. Что бы ни делала map(..)
внутри, она (очевидно) не делает такого предположения, поэтому результат от странного массива "пустых слотов" является неожиданным и, вероятно, приведет к сбою.
Итак, если вы хотите на самом деле создать массив фактических «неопределенных» значений (а не просто «пустых слотов»), как вы можете это сделать (кроме как вручную)?
var a = Array.apply( null, { length: 3 } );
a; // [ undefined, undefined, undefined ]
Озадачены? Ага. Вот примерно как это работает.
apply(..)
- это утилита, доступная для всех функций, которая вызывает функцию, с которой она используется, но особым образом.
Первый аргумент — это привязка объекта this
(описанная в заголовке this & Object Prototypes этой серии), которая нам здесь не нужна, поэтому мы устанавливаем для нее значение null
. Предполагается, что второй аргумент должен быть массивом (или чем-то подобным массиву, также известному как "array-like object"). Содержимое этого «массива» «распространяется» как аргументы рассматриваемой функции.
Таким образом, Array.apply(..)
вызывает функцию Array(..)
и распределяет значения (значения объекта {length: 3 }
) в качестве аргументов.
Внутри apply(..)
мы можем представить себе еще один цикл for
(что-то вроде join(..)
выше), который идет от 0
до length
(3
в нашем случае).
Для каждого индекса он извлекает этот ключ из объекта. Таким образом, если бы параметр объекта массива был назван arr
внутри функции apply(..)
, доступ к свойству был бы: arr[0]
, arr[1]
и arr[2]. ]
. Конечно, ни одно из этих свойств не существует в значении объекта { length: 3 }
, поэтому все три доступа к этим свойствам вернут значение undefined
.
Другими словами, это заканчивается вызовом Array(..)
в основном следующим образом: Array(undefined,undefined,undefined)
, так мы получаем массив, заполненный undefined
значениями, а не этими (сумасшедшими) пустыми слотами.
Хотя Array.apply( null, {length: 3 } )
- это странный и многословный способ создания массива, заполненного неопределенными
значениями, он значительно лучше и надежнее, чем то, что вы получаете с ножным ружьем Array(3)
пустые слоты.
Итог: никогда, ни при каких обстоятельствах вы не должны намеренно создавать и использовать эти экзотические массивы пустых слотов. Просто не делай этого. Они сумасшедшие.
Конструкторы Object(..)
/Function(..)
/RegExp(..)
также обычно необязательны (и поэтому их обычно следует избегать, если они специально не требуются):
var c = new Object();
c.foo = "bar";
c; // { foo: "bar" }
var d = { foo: "bar" };
d; // { foo: "bar" }
var e = new Function( "a", "return a * 2;" );
var f = function(a) { return a * 2; };
function g(a) { return a * 2; }
var h = new RegExp( "^a*b+", "g" );
var i = /^a*b+/g;
Практически нет причин когда-либо использовать форму конструктора new Object()
, тем более, что она вынуждает вас добавлять свойства одно за другим, а не многие сразу в литеральной форме объекта.
Конструктор Function
полезен только в самых редких случаях, когда вам нужно динамически определить параметры функции и/или ее тело функции. Не рассматривайте Function(..)
просто как альтернативную форму eval(..)
. Вам почти никогда не потребуется динамически определять функцию таким образом.
Регулярные выражения, определенные в литеральной форме (/^a*b+/g
), настоятельно предпочтительнее не только из-за простоты синтаксиса, но и из соображений производительности - механизм JS предварительно компилирует и кэширует их перед выполнением кода. В отличие от других форм конструктора, которые мы видели до сих пор, RegExp(..)
имеет разумную полезность: для динамического определения шаблона для регулярного выражения.
var name = "Kyle";
var namePattern = new RegExp( "\\b(?:" + name + ")+\\b", "ig" );
var matches = someText.match( namePattern );
Такой сценарий время от времени возникает в программах JS, поэтому вам нужно использовать форму new RegExp("pattern","flags")
.
Собственные конструкторы Date(..)
и Error(..)
намного полезнее, чем другие нативные конструкторы, потому что ни для одного из них нет литеральной формы.
Чтобы создать значение объекта даты, вы должны использовать new Date()
. Конструктор Date(..)
принимает необязательные аргументы для указания используемой даты/времени, но если они опущены, предполагается текущая дата/время.
На сегодняшний день наиболее распространенной причиной создания объекта даты является получение текущего значения метки времени (целое число со знаком в миллисекундах с 1 января 1970 года). Вы можете сделать это, вызвав getTime()
для экземпляра объекта даты.
Но еще более простой способ — просто вызвать статическую вспомогательную функцию, определенную в ES5: Date.now()
. А полифилить это для pre-ES5 довольно просто:
if (!Date.now) {
Date.now = function(){
return (new Date()).getTime();
};
}
Примечание: Если вы вызываете Date()
без new
, вы получите строковое представление даты/времени в этот момент. Точная форма этого представления не указана в спецификации языка, хотя браузеры, как правило, соглашаются на что-то близкое к: «Пт, 18 июля 2014 г., 00:31:02 GMT-0500 (CDT)».
Конструктор Error(..)
(очень похожий на Array()
выше) ведет себя одинаково с ключевым словом new
, присутствующим или опущенным.
Основная причина, по которой вы хотите создать объект ошибки, заключается в том, что он захватывает текущий контекст стека выполнения в объект (в большинстве JS-движков после создания он раскрывается как свойство .stack
, доступное только для чтения). Этот контекст стека включает в себя стек вызовов функций и номер строки, в которой был создан объект ошибки, что значительно упрощает отладку этой ошибки.
Обычно вы используете такой объект ошибки с оператором throw
:
function foo(x) {
if (!x) {
throw new Error( "x wasn't provided" );
}
// ..
}
Экземпляры объекта ошибки обычно имеют как минимум свойство message
, а иногда и другие свойства (которые следует рассматривать как доступные только для чтения), например type
. Однако, помимо проверки вышеупомянутого свойства stack
, обычно лучше просто вызвать toString()
для объекта ошибки (либо явно, либо неявно с помощью приведения – см. главу 4), чтобы получить сообщение об ошибке в понятном формате. сообщение.
Совет: Технически, в дополнение к общему нативному коду Error(..), есть несколько других нативных типов для конкретных ошибок:
EvalError(..),
RangeError(..),
ReferenceError(..),
SyntaxError(..),
TypeError(..)и
URIError(..). Но очень редко можно вручную использовать эти конкретные ошибки. Они автоматически используются, если ваша программа действительно страдает от реального исключения (например, ссылка на необъявленную переменную и получение ошибки
ReferenceError`).
В ES6 добавлен дополнительный примитивный тип значения, который называется «Символ». Символы — это специальные «уникальные» (не строго гарантированные!) значения, которые можно использовать в качестве свойств объектов, практически не опасаясь каких-либо столкновений. В первую очередь они предназначены для специального встроенного поведения конструкций ES6, но вы также можете определить свои собственные символы.
Символы можно использовать в качестве имен свойств, но вы не можете увидеть или получить доступ к фактическому значению символа из своей программы или из консоли разработчика. Если вы оцениваете символ в консоли разработчика, то, что показано, выглядит, например, как Symbol(Symbol.create)
.
В ES6 есть несколько предопределенных символов, доступ к которым осуществляется как к статическим свойствам функционального объекта Symbol
, например, Symbol.create
, Symbol.iterator
и т.д. Чтобы использовать их, сделайте что-то вроде:
obj[Symbol.iterator] = function(){ /*..*/ };
Чтобы определить свои собственные символы, используйте родной Symbol(..)
. Собственный "конструктор" Symbol(..)
уникален тем, что вам не разрешено использовать с ним new
, так как это вызовет ошибку.
var mysym = Symbol( "my own symbol" );
mysym; // Symbol(my own symbol)
mysym.toString(); // "Symbol(my own symbol)"
typeof mysym; // "symbol"
var a = { };
a[mysym] = "foobar";
Object.getOwnPropertySymbols( a );
// [ Symbol(my own symbol) ]
Хотя символы на самом деле не являются приватными (Object.getOwnPropertySymbols(..)
отражает объект и раскрывает символы довольно публично), их использование для приватных или специальных свойств, вероятно, является их основным вариантом использования. Для большинства разработчиков они могут заменить имена свойств с префиксами подчеркивания _
, которые почти всегда по соглашению означают: «Эй, это частное/специальное/внутреннее свойство, так что не трогайте его!»
Примечание: "Символы" не "объекты", это простые скалярные примитивы.
Каждый из встроенных нативных конструкторов имеет свой собственный объект .prototype
-- Array.prototype
, String.prototype
и т.д.
Эти объекты содержат поведение, уникальное для их конкретного подтипа объекта.
Например, все строковые объекты, а также расширенные (через упаковку) примитивы string
, имеют доступ к поведению по умолчанию как методы, определенные в объекте String.prototype
.
Примечание: По соглашению, принятому в документации, String.prototype.XYZ сокращается до String#XYZ, и аналогично для всех остальных .prototype
.
String#indexOf(..)
: найти позицию в строке другой подстрокиString#charAt(..)
: получить доступ к символу в позиции в строкеString#substr(..)
,String#substring(..)
, andString#slice(..)
: извлечь часть строки как новую строкуString#toUpperCase()
andString#toLowerCase()
: создать новую строку, которая преобразуется в верхний или нижний регистрString#trim()
: создать новую строку, лишенную всех конечных или начальных пробелов
Ни один из методов не изменяет строку на месте. Модификации (такие как преобразование регистра или обрезка) создают новое значение из существующего значения.
Благодаря делегированию прототипам (см. заголовок this & Object Prototypes в этой серии) любое строковое значение может получить доступ к этим методам:
var a = " abc ";
a.indexOf( "c" ); // 3
a.toUpperCase(); // " ABC "
a.trim(); // "abc"
Другие прототипы конструкторов содержат поведение, соответствующее их типам, например Number#toFixed(..)
(строка числа с фиксированным количеством десятичных цифр) и Array#concat(..)
(объединение массивов). Все функции имеют доступ к apply(..)
, call(..)
и bind(..)
, поскольку они определены в Function.prototype
.
Но некоторые нативные прототипы не являются просто простыми объектами:
typeof Function.prototype; // "function"
Function.prototype(); // Пустая функция!
RegExp.prototype.toString(); // "/(?:)/" -- пустая регулярка
"abc".match( RegExp.prototype ); // [""]
Особенно плохая идея, вы даже можете изменить эти собственные прототипы (а не просто добавить свойства, с которыми вы, вероятно, знакомы):
Array.isArray( Array.prototype ); // true
Array.prototype.push( 1, 2, 3 ); // 3
Array.prototype; // [1,2,3]
// не оставляйте так, иначе ждите странностей!
// сбрасываем `Array.prototype` на пустой
Array.prototype.length = 0;
Как видите, Function.prototype — это функция, RegExp.prototype — регулярное выражение, а Array.prototype — массив. Интересно и круто, да?
Function.prototype
– пустая функция, RegExp.prototype
– "пустое" (например, несоответствующее) регулярное выражение, а Array.prototype
– пустой массив, сделайте для них все приятные значения "по умолчанию" для присвоения к переменным, если эти переменные еще не имели значения надлежащего типа.
Например:
function isThisCool(vals,fn,rx) {
vals = vals || Array.prototype;
fn = fn || Function.prototype;
rx = rx || RegExp.prototype;
return rx.test(
vals.map( fn ).join( "" )
);
}
isThisCool(); // true
isThisCool(
["a","b","c"],
function(v){ return v.toUpperCase(); },
/D/
); // false
Примечание: Начиная с ES6, нам не нужно использовать vals = vals || ..
трюк с синтаксисом значения по умолчанию (см. главу 4) больше не используется, потому что значения по умолчанию могут быть установлены для параметров с помощью собственного синтаксиса в объявлении функции (см. главу 5).
Одним из незначительных побочных преимуществ этого подхода является то, что .prototype
уже созданы и встроены, поэтому создаются только один раз. Напротив, использование самих значений []
, function(){}
и /(?:)/
для этих значений по умолчанию (вероятно, в зависимости от реализации движка) будет воссоздавать эти значения (и, возможно, выполнять сборку мусора). их позже) для каждого вызова isThisCool(..)
. Это может быть расточительно по памяти/ЦП.
Кроме того, будьте очень осторожны, чтобы не использовать Array.prototype
в качестве значения по умолчанию, которое впоследствии будет изменено. В этом примере vals используется только для чтения, но если бы вы вместо этого вносили изменения в vals на месте, вы фактически модифицировали бы сам Array.prototype, что привело бы к подводным камням, упомянутым ранее!
Примечание. Хотя мы указываем на эти нативные прототипы и на некоторую их полезность, будьте осторожны, полагаясь на них, и еще более осторожны, не изменяя их каким-либо образом. См. Приложение A «Родные прототипы» для более подробного обсуждения.
JavaScript предоставляет объектные оболочки вокруг примитивных значений, известных как стандартные объекты (String, Number, Boolean и т. д.). Эти оболочки объектов предоставляют значениям доступ к поведению, подходящему для каждого подтипа объекта (String#trim()
и Array#concat(..)
).
Если у вас есть простое скалярное примитивное значение, такое как "abc"
, и вы обращаетесь к его свойству length
или какому-то методу String.prototype
, JS автоматически "упаковывает" значение (оборачивает его в соответствующую оболочку объекта), чтобы доступы к свойствам/методам могли быть выполнены.