refactor types
This commit is contained in:
parent
0712ddc698
commit
c108f03596
10 changed files with 69 additions and 438 deletions
|
@ -1,4 +1,4 @@
|
|||
# Шесть типов данных
|
||||
# Шесть типов данных, typeof
|
||||
|
||||
В JavaScript существует несколько основных типов данных.
|
||||
|
||||
|
@ -104,12 +104,65 @@ alert( x ); // "undefined"
|
|||
|
||||
Первые 5 типов называют *"примитивными"*.
|
||||
|
||||
Особняком стоит шестой тип: *"объекты"*. К нему относятся, например, даты, функции, он используется для коллекций данных и для объявления более сложных сущностей.
|
||||
Особняком стоит шестой тип: *"объекты"*.
|
||||
|
||||
Позже, в главе [про объекты](/object) мы вернёмся к этому типу и рассмотрим его принципиальные отличия от примитивов.
|
||||
Он используется для коллекций данных и для объявления более сложных сущностей.
|
||||
|
||||
Объявляются объекты при помощи фигурных скобок `{...}`, например:
|
||||
|
||||
```js
|
||||
var user = { name: "Вася" };
|
||||
```
|
||||
|
||||
Мы подробно разберём способы объявления объектов и, вообще, работу с объектами, позже, в главе [](/object).
|
||||
|
||||
## Оператор typeof [#type-typeof]
|
||||
|
||||
Оператор `typeof` возвращает тип аргумента.
|
||||
|
||||
У него есть два синтаксиса: со скобками и без:
|
||||
<ol>
|
||||
<li>Синтаксис оператора: `typeof x`.</li>
|
||||
<li>Синтаксис функции: `typeof(x)`.</li>
|
||||
</ol>
|
||||
|
||||
Работают они одинаково, но первый синтаксис короче.
|
||||
|
||||
**Результатом `typeof` является строка, содержащая тип:**
|
||||
|
||||
```js
|
||||
typeof undefined // "undefined"
|
||||
|
||||
typeof 0 // "number"
|
||||
|
||||
typeof true // "boolean"
|
||||
|
||||
typeof "foo" // "string"
|
||||
|
||||
typeof {} // "object"
|
||||
|
||||
*!*
|
||||
typeof null // "object" (1)
|
||||
*/!*
|
||||
|
||||
*!*
|
||||
typeof function(){} // "function" (2)
|
||||
*/!*
|
||||
```
|
||||
|
||||
Последние две строки помечены, потому что `typeof` ведет себя в них по-особому.
|
||||
|
||||
<ol>
|
||||
<li>Результат `typeof null == "object"` -- это официально признанная ошибка в языке, которая сохраняется для совместимости. На самом деле `null` -- это не объект, а отдельный тип данных.</li>
|
||||
<li>Функции мы пройдём чуть позже. Пока лишь заметим, что функции не являются отдельным базовым типом в JavaScript, а подвидом объектов. Но `typeof` выделяет функции отдельно, возвращая для них `"function"`. На практике это весьма удобно, так как позволяет легко определить функцию.</li>
|
||||
</ol>
|
||||
|
||||
К работе с типами мы также вернёмся более подробно в будущем, после изучения основных структур данных.
|
||||
|
||||
## Итого
|
||||
|
||||
Есть 5 "примитивных" типов: `number`, `string`, `boolean`, `null`, `undefined` и 6-й тип -- объекты `object`.
|
||||
|
||||
Очень скоро мы изучим их во всех деталях.
|
||||
|
||||
Оператор `typeof x` позволяет выяснить, какой тип находится в `x`, возвращая его в виде строки.
|
|
@ -1,28 +0,0 @@
|
|||
function formatDate(date) {
|
||||
if (typeof date == 'number') {
|
||||
// перевести секунды в миллисекунды и преобразовать к Date
|
||||
date = new Date(date * 1000);
|
||||
} else if (typeof date == 'string') {
|
||||
// разобрать строку и преобразовать к Date
|
||||
date = date.split('-');
|
||||
date = new Date(date[0], date[1] - 1, date[2]);
|
||||
} else if (date.length) { // есть длина, но не строка - значит массив
|
||||
date = new Date(date[0], date[1], date[2]);
|
||||
}
|
||||
// преобразования для поддержки полиморфизма завершены,
|
||||
// теперь мы работаем с датой (форматируем её)
|
||||
|
||||
var day = date.getDate();
|
||||
if (day < 10) day = '0' + day;
|
||||
|
||||
var month = date.getMonth() + 1;
|
||||
if (month < 10) month = '0' + month;
|
||||
|
||||
// взять 2 последние цифры года
|
||||
var year = date.getFullYear() % 100;
|
||||
if (year < 10) year = '0' + year;
|
||||
|
||||
var formattedDate = day + '.' + month + '.' + year;
|
||||
|
||||
return formattedDate;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
describe("formatDate", function() {
|
||||
it("читает дату вида гггг-мм-дд из строки", function() {
|
||||
assert.equal(formatDate('2011-10-02'), "02.10.11");
|
||||
});
|
||||
|
||||
it("читает дату из числа 1234567890 (миллисекунды)", function() {
|
||||
assert.equal(formatDate(1234567890), "14.02.09");
|
||||
});
|
||||
|
||||
it("читает дату из массива вида [гггг, м, д]", function() {
|
||||
assert.equal(formatDate([2014, 0, 1]), "01.01.14");
|
||||
});
|
||||
|
||||
it("читает дату из объекта Date", function() {
|
||||
assert.equal(formatDate(new Date(2014, 0, 1)), "01.01.14");
|
||||
});
|
||||
|
||||
});
|
|
@ -1,55 +0,0 @@
|
|||
Для определения примитивного типа строка/число подойдет оператор [typeof](#type-typeof).
|
||||
|
||||
Примеры его работы:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( typeof 123 ); // "number"
|
||||
alert( typeof "строка" ); // "string"
|
||||
alert( typeof new Date() ); // "object"
|
||||
alert( typeof [] ); // "object"
|
||||
```
|
||||
|
||||
Оператор `typeof` не умеет различать разные типы объектов, они для него все на одно лицо: `"object"`. Поэтому он не сможет отличить `Date` от `Array`.
|
||||
|
||||
Используем для них утиную типизацию:
|
||||
|
||||
Функция:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function formatDate(date) {
|
||||
if (typeof date == 'number') {
|
||||
// перевести секунды в миллисекунды и преобразовать к Date
|
||||
date = new Date(date * 1000);
|
||||
} else if (typeof date == 'string') {
|
||||
// разобрать строку и преобразовать к Date
|
||||
date = date.split('-');
|
||||
date = new Date(date[0], date[1] - 1, date[2]);
|
||||
} else if (date.length) { // есть длина, но не строка - значит массив
|
||||
date = new Date(date[0], date[1], date[2]);
|
||||
}
|
||||
// преобразования для поддержки полиморфизма завершены,
|
||||
// теперь мы работаем с датой (форматируем её)
|
||||
|
||||
var day = date.getDate();
|
||||
if (day < 10) day = '0' + day;
|
||||
|
||||
var month = date.getMonth() + 1;
|
||||
if (month < 10) month = '0' + month;
|
||||
|
||||
// взять 2 последние цифры года
|
||||
var year = date.getFullYear() % 100;
|
||||
if (year < 10) year = '0' + year;
|
||||
|
||||
var formattedDate = day + '.' + month + '.' + year;
|
||||
|
||||
return formattedDate;
|
||||
}
|
||||
|
||||
alert( formatDate('2011-10-02') ); // 02.10.11
|
||||
alert( formatDate(1234567890) ); // 14.02.09
|
||||
alert( formatDate([2014, 0, 1]) ); // 01.01.14
|
||||
alert( formatDate(new Date(2014, 0, 1)) ); // 01.01.14
|
||||
```
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# Полиморфная функция formatDate
|
||||
|
||||
[importance 5]
|
||||
|
||||
Напишите функцию `formatDate(date)`, которая возвращает дату в формате `dd.mm.yy`.
|
||||
|
||||
Ее первый аргумент должен содержать дату в одном из видов:
|
||||
<ol>
|
||||
<li>Как объект `Date`.</li>
|
||||
<li>Как строку в формате `yyyy-mm-dd`.</li>
|
||||
<li>Как число *секунд* с `01.01.1970`.</li>
|
||||
<li>Как массив `[гггг, мм, дд]`, месяц начинается с нуля</li>
|
||||
</ol>
|
||||
Для этого вам понадобится определить тип данных аргумента и, при необходимости, преобразовать входные данные в нужный формат.
|
||||
|
||||
Пример работы:
|
||||
|
||||
```js
|
||||
function formatDate(date) { /* ваш код */ }
|
||||
|
||||
alert( formatDate('2011-10-02') ); // 02.10.11
|
||||
alert( formatDate(1234567890) ); // 14.02.09
|
||||
alert( formatDate([2014, 0, 1]) ); // 01.01.14
|
||||
alert( formatDate(new Date(2014, 0, 1)) ); // 01.01.14
|
||||
```
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
# Полиморфизм, typeof и утиная типизация
|
||||
|
||||
В этой главе мы рассмотрим, как создавать *полиморфные* функции, то есть такие, которые по-разному обрабатывают аргументы, в зависимости от их типа. Например, функция вывода может по-разному форматировать числа и даты.
|
||||
|
||||
Для реализации такой возможности нужен способ определить тип переменной.
|
||||
|
||||
[cut]
|
||||
Как мы знаем, существует несколько *примитивных типов*:
|
||||
<dl>
|
||||
<dt>`null`</dt>
|
||||
<dd>Специальный тип, содержит только значение `null`.</dd>
|
||||
<dt>`undefined`</dt>
|
||||
<dd>Специальный тип, содержит только значение `undefined`.</dd>
|
||||
<dt>`number`</dt>
|
||||
<dd>Числа: `0`, `3.14`, а также значения `NaN` и `Infinity`</dd>
|
||||
<dt>`boolean`</dt>
|
||||
<dd>`true`, `false`.</dd>
|
||||
<dt>`string`</dt>
|
||||
<dd>Строки, такие как `"Мяу"` или пустая строка `""`.</dd>
|
||||
</dl>
|
||||
|
||||
Все остальные значения, включая даты и массивы, являются объектами.
|
||||
|
||||
## Оператор typeof [#type-typeof]
|
||||
|
||||
Оператор `typeof` возвращает тип аргумента. У него есть два синтаксиса: со скобками и без:
|
||||
<ol>
|
||||
<li>Синтаксис оператора: `typeof x`.</li>
|
||||
<li>Синтаксис функции: `typeof(x)`.</li>
|
||||
</ol>
|
||||
|
||||
Работают они одинаково, но первый синтаксис короче.
|
||||
|
||||
**Результатом `typeof` является строка, содержащая тип:**
|
||||
|
||||
```js
|
||||
typeof undefined // "undefined"
|
||||
|
||||
typeof 0 // "number"
|
||||
|
||||
typeof true // "boolean"
|
||||
|
||||
typeof "foo" // "string"
|
||||
|
||||
typeof {} // "object"
|
||||
|
||||
*!*
|
||||
typeof null // "object"
|
||||
*/!*
|
||||
|
||||
function f() { /* ... */ }
|
||||
typeof f // "function"
|
||||
*/!*
|
||||
```
|
||||
|
||||
Последние две строки помечены, потому что `typeof` ведет себя в них по-особому.
|
||||
|
||||
<ol>
|
||||
<li>Результат `typeof null == "object"` -- это официально признанная ошибка в языке, которая сохраняется для совместимости.
|
||||
|
||||
На самом деле `null` -- это не объект, а примитив. Это сразу видно, если попытаться присвоить ему свойство:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var x = null;
|
||||
x.prop = 1; // ошибка, т.к. нельзя присвоить свойство примитиву
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Для функции `f` значением `typeof f` является `"function"`. На самом деле функция не является отдельным базовым типом в JavaScript, все функции являются объектами, но такое выделение функций на практике удобно, так как позволяет легко определить функцию.</li>
|
||||
</ol>
|
||||
|
||||
**Оператор `typeof` надежно работает с примитивными типами, кроме `null`, а также с функциями. Но обычные объекты, массивы и даты для `typeof` все на одно лицо, они имеют тип `'object'`:**
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( typeof {} ); // 'object'
|
||||
alert( typeof [] ); // 'object'
|
||||
alert( typeof new Date ); // 'object'
|
||||
```
|
||||
|
||||
Поэтому различить их при помощи `typeof` нельзя.
|
||||
|
||||
## Утиная типизация
|
||||
|
||||
Основная проблема `typeof` -- неумение различать объекты, кроме функций. Но есть и другой способ проверки типа.
|
||||
|
||||
Так называемая "утиная типизация" основана на одной известной пословице: *"If it looks like a duck, swims like a duck and quacks like a duck, then it probably is a duck (who cares what it really is)"*.
|
||||
|
||||
В переводе: *"Если это выглядит как утка, плавает как утка и крякает как утка, то, вероятно, это утка (какая разница, что это на самом деле)"*.
|
||||
|
||||
Смысл утиной типизации -- в проверке необходимых методов и свойств.
|
||||
|
||||
Например, у нас функция работает с массивами. Мы можем проверить, что объект -- массив, уточнив наличие метода `splice`, который, как известно, есть у всех массивов:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var something = [1, 2, 3];
|
||||
|
||||
if (something.splice) {
|
||||
alert( 'Это утка! То есть, массив!' );
|
||||
}
|
||||
```
|
||||
|
||||
Обратите внимание -- в `if` мы не вызываем метод `something.splice()`, а пробуем получить само свойство `something.splice`. Для массивов оно всегда есть и является функцией, т.е. даст в логическом контексте `true`.
|
||||
|
||||
Проверить на дату можно, определив наличие метода `getTime`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var x = new Date();
|
||||
|
||||
if (x.getTime) {
|
||||
alert( 'Дата!' );
|
||||
}
|
||||
```
|
||||
|
||||
С виду такая проверка хрупка, ее можно "сломать", передав похожий объект с тем же методом.
|
||||
|
||||
Но как раз в этом и есть смысл утиной типизации: если объект похож на массив, у него есть методы массива, то будем работать с ним как с массивом (какая разница, что это на самом деле).
|
||||
|
||||
[smart header="Метод `Array.isArray()`"]
|
||||
Для массивов есть специальный метод проверки: `Array.isArray(arr)`, который возвращает `true` только если `arr` -- массив:
|
||||
```js
|
||||
//+ run
|
||||
alert( Array.isArray([1,2,3]) ); // true
|
||||
alert( Array.isArray("not array")); // false
|
||||
```
|
||||
|
||||
Этот метод уникален в своём роде, других аналогичных (типа `Object.isObject`, `Date.isDate`) -- нет.
|
||||
|
||||
Если нужно удостовериться, что мы получили именно массив, а не нечто похожее на него -- можно использовать `Array.isArray`. Но при этом нужно отдавать себе отчёт, что этим мы одновременно ограничиваем применимость кода: "похожие на массив" данные теперь обрабатываться не будут. Решение зависит от конкретной ситуации.
|
||||
[/smart]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Полиморфизм
|
||||
|
||||
Пример полиморфной функции -- `sayHi(who)`, которая будет говорить "Привет" своему аргументу, причём если передан массив -- то "Привет" каждому:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function sayHi(who) {
|
||||
|
||||
if (Array.isArray(who)) {
|
||||
who.forEach(sayHi);
|
||||
} else {
|
||||
alert( 'Привет, ' + who );
|
||||
}
|
||||
}
|
||||
|
||||
// Вызов с примитивным аргументом
|
||||
sayHi("Вася"); // Привет, Вася
|
||||
|
||||
// Вызов с массивом
|
||||
sayHi(["Саша", "Петя"]); // Привет, Саша... Петя
|
||||
|
||||
// Вызов с вложенными массивами - тоже работает!
|
||||
sayHi(["Саша", "Петя", ["Маша", "Юля"]]); // Привет Саша..Петя..Маша..Юля
|
||||
```
|
||||
|
||||
Здесь используется не "duck typing", а "жёсткая" проверка на массив. Можно было бы и поступить мягче -- проверить только наличие метода `forEach`:
|
||||
```js
|
||||
if (who.forEach) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Итого
|
||||
|
||||
Для написания полиморфных (это удобно!) функций нам нужна проверка типов.
|
||||
|
||||
Для примитивов с ней отлично справляется оператор `typeof`.
|
||||
|
||||
У него две особенности:
|
||||
<ol>
|
||||
<li>Он считает `null` объектом, это внутренняя ошибка в языке.</li>
|
||||
<li>Для функций он возвращает `function`, по стандарту функция не считается базовым типом, но на практике это удобно и полезно.</li>
|
||||
</ol>
|
||||
|
||||
Там, где нужно различать объекты, обычно используется утиная типизация, то есть мы смотрим, есть ли в объекте нужный метод, желательно -- тот, который мы собираемся использовать.
|
||||
|
|
@ -198,7 +198,7 @@ function work() {
|
|||
|
||||
Здесь нам не важно, какие, нас интересует именно как описана эта библиотека, как в ней применяется приём "модуль".
|
||||
|
||||
Вот выдержка из исходного файла:
|
||||
Вот примерная выдержка из исходного файла:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
|
@ -221,8 +221,7 @@ function work() {
|
|||
// код функции size, пока что доступен только внутри
|
||||
*/!*
|
||||
function size(collection) {
|
||||
var length = collection ? collection.length : 0;
|
||||
return typeof length == 'number' ? length : Object.keys(collection).length;
|
||||
return Object.keys(collection).length;
|
||||
}
|
||||
|
||||
*!*
|
||||
|
|
|
@ -243,9 +243,7 @@ alert( pete.age ); // 25
|
|||
|
||||
С обычными свойствами в коде меньше букв, они удобны, причины использовать функции пока нет.
|
||||
|
||||
...Но рано или поздно может произойти что-то, что потребует более сложной логики.
|
||||
|
||||
Например, формат данных изменился и теперь вместо возраста `age` хранится дата рождения `birthday`:
|
||||
...Но рано или поздно могут произойти изменения. Например, в `User` может стать более целесообразно вместо возраста `age` хранить дату рождения `birthday`:
|
||||
|
||||
```js
|
||||
function User(name, birthday) {
|
||||
|
@ -263,12 +261,13 @@ var pete = new User("Петя", new Date(1987, 6, 1));
|
|||
Добавление `get`-функции `age` позволяет обойти проблему легко и непринуждённо:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
//+ run no-beautify
|
||||
function User(name, birthday) {
|
||||
this.name = name;
|
||||
this.birthday = birthday;
|
||||
|
||||
*!*
|
||||
// age будет высчитывать возраст по birthday
|
||||
Object.defineProperty(this, "age", {
|
||||
get: function() {
|
||||
var todayYear = new Date().getFullYear();
|
||||
|
@ -280,10 +279,11 @@ function User(name, birthday) {
|
|||
|
||||
var pete = new User("Петя", new Date(1987, 6, 1));
|
||||
|
||||
alert( pete.age ); // получает возраст из даты рождения
|
||||
alert( pete.birthday ); // и дата рождения доступна
|
||||
alert( pete.age ); // и возраст
|
||||
```
|
||||
|
||||
Таким образом, `defineProperty` позволяет нам использовать обычные свойства и, при необходимости, в любой момент заменить их на функции, сохраняя полную совместимость.
|
||||
Таким образом, `defineProperty` позволяет нам начать с обычных свойств, а в будущем, при необходимости, можно в любой момент заменить их на функции, реализующие более сложную логику.
|
||||
|
||||
## Другие методы работы со свойствами
|
||||
|
||||
|
|
|
@ -415,15 +415,9 @@ function mul(a, b) {
|
|||
return a * b;
|
||||
};
|
||||
|
||||
function ask(question, correctAnswer, ok, fail) {
|
||||
var result;
|
||||
if (typeof correctAnswer == 'boolean') {
|
||||
result = confirm(question);
|
||||
} else {
|
||||
result = prompt(question, '');
|
||||
}
|
||||
|
||||
if (result == correctAnswer) ok()
|
||||
function ask(question, answer, ok, fail) {
|
||||
var result = prompt(question, '');
|
||||
if (result.toLowerCase() == answer.toLowerCase()) ok();
|
||||
else fail();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
# Секретное свойство [[Class]]
|
||||
|
||||
Для встроенных объектов есть одна "секретная" возможность узнать их тип, которая связана с методом `toString`.
|
||||
|
||||
Во всех встроенных объектах есть специальное свойство `[[Class]]`, в котором хранится информация о его типе или конструкторе.
|
||||
|
||||
Оно взято в квадратные скобки, так как это свойство -- внутреннее. Явно получить его нельзя, но можно прочитать его "в обход", воспользовавшись методом `toString` из `Object`.
|
||||
|
||||
[cut]
|
||||
|
||||
## Получение [[Class]]
|
||||
|
||||
Вернёмся к примеру, который видели раньше:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var obj = {};
|
||||
alert( obj ); // [object Object]
|
||||
```
|
||||
|
||||
**В выводе стандартного `toString` для объектов внутри `[object ...]` указано как раз значение `[[Class]]`.**
|
||||
|
||||
Для обычного объекта это как раз и есть `"Object"`, но если бы такой `toString` запустить для даты, то будет `[object Date]`, для массивов -- `[object Array]` и т.п.
|
||||
|
||||
К сожалению или к счастью, но большинство встроенных объектов в JavaScript имеют свой собственный метод `toString`: для массивов он выводит элементы через запятую, для дат -- строчное представление и так далее.
|
||||
|
||||
То есть, просто вызов `[1,2,3].toString()` вернёт нам `1,2,3` и никакой информации про `[[Class]]`.
|
||||
|
||||
Поэтому для получения `[[Class]]` мы одолжим функцию `toString` у стандартного объекта и запустим её в контексте тех значений, для которых нужно получить тип. В этом нам поможет метод `call`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var toClass = {}.toString; // (1)
|
||||
|
||||
var arr = [1, 2];
|
||||
alert( toClass.call(arr) ); // (2) [object Array]
|
||||
|
||||
var date = new Date;
|
||||
alert( toClass.call(date) ); // [object Date]
|
||||
|
||||
var type = toClass.call(date).slice(8, -1); // (3)
|
||||
alert( type ); // Date
|
||||
```
|
||||
|
||||
Разберем происходящее более подробно.
|
||||
|
||||
<ol>
|
||||
<li>Можно переписать эту строку в две:
|
||||
|
||||
```js
|
||||
var obj = {};
|
||||
var toClass = obj.toString;
|
||||
```
|
||||
|
||||
Иначе говоря, мы создаём пустой объект `{}` и копируем ссылку на его метод `toString` в переменную `toClass`.
|
||||
|
||||
**Для получения `[[Class]]` нужна именно внутренняя реализация `toString` стандартного объекта `Object`, другая не подойдёт.**</li>
|
||||
<li>Вызываем скопированный метод в контексте нужного объекта `obj`.
|
||||
|
||||
Мы могли бы поступить проще -- одолжить метод под другим названием:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var arr = [1, 2];
|
||||
arr.toClass = {}.toString;
|
||||
|
||||
alert( arr.toClass() ); // [object Array]
|
||||
```
|
||||
|
||||
...Но зачем копировать лишнее свойство в объект? Синтаксис `toClass.call(arr)` делает то же самое, поэтому используем его.
|
||||
</li>
|
||||
<li>Всё, класс получен. При желании можно убрать обёртку `[object ...]`, взяв подстроку вызовом `slice(8,-1)`.</li>
|
||||
</ol>
|
||||
|
||||
Метод также можно использовать с примитивами:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( {}.toString.call(123) ); // [object Number]
|
||||
alert( {}.toString.call("строка") ); // [object String]
|
||||
```
|
||||
|
||||
[warn header="Вызов `{}.toString` в консоли может выдать ошибку"]
|
||||
При тестировании кода в консоли вы можете обнаружить, что если ввести в командную строку `{}.toString.call(...)` -- будет ошибка. С другой стороны, вызов `alert( {}.toString... )` -- работает.
|
||||
|
||||
Эта ошибка возникает потому, что фигурные скобки `{ }` в основном потоке кода интерпретируются как блок. Интерпретатор читает `{}.toString.call(...)` так:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
{ } // пустой блок кода
|
||||
.toString.call(...) // а что это за точка в начале? не понимаю, ошибка!
|
||||
```
|
||||
|
||||
Фигурные скобки считаются объектом, только если они находятся в контексте выражения. В частности, оборачивание в скобки `( {}.toString... )` тоже сработает нормально.
|
||||
[/warn]
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>Свойство `[[Class]]` позволяет получить тип для встроенных объектов. Далее мы будем рассматривать создание своих объектов через функцию-конструктор, с ними `[[Class]]` не работает.</li>
|
||||
<li>Для доступа к `[[Class]]` используется `{}.toString.call(obj).slice(8, -1)`.</li>
|
||||
</ul>
|
||||
|
||||
Обычно в JavaScript используется "утиная" типизация. Свойство `[[Class]]` -- самое надёжное средство проверки типа встроенных объектов, но обычно утиной типизации вполне хватает.
|
Loading…
Add table
Add a link
Reference in a new issue