Размышляя о методе toString(), запускаемом Object.prototype.toString.call()

внешний интерфейс JavaScript регулярное выражение опрос

введение

Есть такой классический вопрос на фронтенд-интервью, как определить, является ли объект массивом?

ES5 предоставляет функцию для определения, является ли объект массивом.

Array.isArray(object);

Среди них требуется объект, указывающий объект, подлежащий тестированию.

Array.isArray([]); //true
Array.isArray({}); //false 
Array.isArray(''); //false

Однако, когда мы рассматриваем проблемы совместимости браузеров, нам нужен более безопасный способ оценки

Object.prototype.toString.call/apply(object);

Результаты сравнения следующие

Object.prototype.toString.call([]);
<!--"[object Array]"-->

Object.prototype.toString.call({});
<!--"[object Object]"-->

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

Здесь я в основном хочу рассказать о некоторых мыслях о методе toString().


думать

Во-первых, давайте поговорим о методе toString(), преобразовав его в строковую форму.

В ECMAScript,ObjectУ каждого экземпляра типа есть метод toString(), который возвращает строковое представление объекта, поэтому каждый созданный объект может вызывать метод toString().

Результат вызова следующий

var obj = {a: 1};
obj.toString(); //"[object Object]"

Итак, откуда взялся метод toString() объекта obj?

Мы следуем цепочке прототипов, obj => obj.proto=> Object.prototype, можно обнаружить, что метод toString() определен в объекте-прототипе Object Object.prototype, так что каждый экземпляр объекта Object может совместно использовать метод Object.prototype.toString().

Как я могу напрямую вызвать метод Object.prototype.toString() без поиска в цепочке прототипов?

Object.prototype.toString();
<!--"[object Object]"-->

Это правильно? Вызов toString() в приведенном выше коде не имеет ничего общего с объектом obj.Почему я получаю тот же результат? Это связано с тем, что Object.prototype также является объектом, поэтому возвращается строковое представление объекта!

Правильный способ вызова метода Object.prototype.toString() через объект obj выглядит следующим образом:

Object.prototype.toString.call/apply(obj);
<!--"[object Object]"-->

Далее давайте проанализируем, как разные типы "объектов" вызывают метод toString(). В чем разница между возвращаемыми значениями?

Давайте сначала уточним типы данных ECMAScript, 7 типов

  • Undefined
  • Null
  • String
  • Number
  • Boolean
  • Object
  • Символ (введен в ES6)

Среди них Object — ссылочный тип, представляющий собой структуру данных и часто называемый классом Object (но это название не подходит, в JS нет класса, все — просто синтаксический сахар).

Кроме того, на основе типа объекта JS также реализует другие часто используемые подтипы объектов (то есть объекты разных типов).

  • Object
  • Array
  • Function
  • String
  • Boolean
  • Number
  • Date
  • RegExp
  • Error
  • ...

Мы можем сказать,Класс объекта является родительским классом всех подклассов

Object instanceof Object; //true
Function instanceof Object; //true
Array instanceof Object; //true
String instanceof Object; //true
Boolean instanceof Object; //true
Number instanceof Object; //true

Таким образом, метод toString(), определенный для Object.prototype, упомянутый выше, можно назвать самым примитивным методом toString(), а другие типы более или менееПереопределить метод toString(), заставляя разные типы объектов вызывать метод toString() для получения разных возвращаемых значений.

Что нам также нужно знать, так это то, что существует две формы создания экземпляра объекта, форма конструктора и литеральная форма, конкретная разница пока не будет обсуждаться.

Затем проанализируйте возвращаемые результаты различных подтипов объектов после перезаписи метода toString().

  1. Объект объекта (класс объекта)

TOSTRING(): возвращает строку объектов

var obj = {a: 1};
obj.toString();//"[object Object]"
Object.prototype.toString.call(obj);//"[object Object]"

Здесь мы размышляем над вопросом, может ли какой-либо объект-объект вызывать метод Object.prototype.toString() через эту привязку? Ответ положительный, результаты следующие

Object.prototype.toString.call({});
<!--"[object Object]"-->
Object.prototype.toString.call([]);
<!--"[object Array]"-->
Object.prototype.toString.call(function(){});
<!--"[object Function]"-->
Object.prototype.toString.call('');
<!--"[object String]"-->
Object.prototype.toString.call(1);
<!--"[object Number]"-->
Object.prototype.toString.call(true);
<!--"[object Boolean]"-->
Object.prototype.toString.call(null);
<!--"[object Null]"-->
Object.prototype.toString.call(undefined);
<!--"[object Undefined]"-->
Object.prototype.toString.call();
<!--"[object Undefined]"-->
Object.prototype.toString.call(new Date());
<!--"[object Date]"-->
Object.prototype.toString.call(/at/);
<!--"[object RegExp]"-->

Как видно из приведенного выше кода, поскольку Object является родительским классом для всех подклассов, объект любого типа может вызвать метод Object.prototype.toString() через эту привязку, чтобы вернуть строковое представление объекта!

  1. Массив массив (класс массива)

toString(): возвращает строку, разделенную запятыми, объединенную строковой формой каждого значения в массиве.

var array = [1, 's', true, {a: 2}];
array.toString();//"1,s,true,[object Object]"
Array.prototype.toString.call(array);//"1,s,true,[object Object]"

Здесь мы также думаем над вышеуказанными вопросами, могут ли объекты, не являющиеся массивами, также вызывать метод Array.prototype.toString() через эту привязку? Ответ положительный, результаты следующие

Array.prototype.toString.call({});
<!--"[object Object]"-->
Array.prototype.toString.call(function(){})
<!--"[object Function]"-->
Array.prototype.toString.call(1)
<!--"[object Number]"-->
Array.prototype.toString.call('')
<!--"[object String]"-->
Array.prototype.toString.call(true)
<!--"[object Boolean]"-->
Array.prototype.toString.call(/s/)
<!--"[object RegExp]"-->

Array.prototype.toString.call();
<!--Cannot convert undefined or null to object at toString-->
Array.prototype.toString.call(undefined);
Array.prototype.toString.call(null);

Из приведенного выше кода мы можем обнаружить, что объект массива вызывает метод Array.prototype.toString() через эту привязку, чтобы вернуть конкатенацию строк значения массива, но объект, не являющийся массивом, вызывает метод Array.prototype.toString() метод через эту привязку, возвращается строковое представление объекта, а метод Array.prototype.toString() не может быть вызван через привязку для null и undefined.

  1. Функциональная функция (класс функции)

toString(): возвращает код функции

function foo(){
    console.log('function');
};
foo.toString();
<!--"function foo(){-->
<!--    console.log('function');-->
<!--}"-->
Function.prototype.toString.call(foo);
<!--"function foo(){-->
<!--    console.log('function');-->
<!--}"-->

Здесь также необходимо обратить внимание на одну проблему: все упомянутые выше «классы» по сути являются конструкторами, поэтому вызов метода toString() возвращает коды функций.

Object.toString();
//"function Object() { [native code] }"
Function.toString();
//"function Function() { [native code] }"
Array.toString();
//"function Array() { [native code] }"
....

Кроме того, давайте еще раз обсудим вышеупомянутые вопросы, могут ли нефункциональные объекты также вызывать метод Array.prototype.toString() через эту привязку? Ответ отрицательный, результаты следующие

Function.prototype.toString.call({});
<!--Function.prototype.toString requires that 'this' be a Function-->

Кроме того, посредством тестов других подклассов Object, за исключением двух случаев Object и Array, упомянутых выше, другие типы не поддерживают экземпляры, отличные от self, для вызова метода toString() на объекте-прототипе подкласса Object через эту привязку. Это означает, что когда они переписывают метод toString(), они четко определяют тип объекта, который вызывает этот метод, и экземпляры объекта, отличные от self, не могут быть вызваны. Итак, обычно мы используем только метод Object.prototype.toString.call/apply().

  1. Дата (класс даты)

TOSTRING(): возвращает дату и время с информацией о часовом поясе.

Тип Date имеет только построенную форму, а не буквальную форму.

var date = new Date();
date.toString();
//"Fri May 11 2018 14:55:43 GMT+0800 (中国标准时间)"
Date.prototype.toString.call(date);
//"Fri May 11 2018 14:55:43 GMT+0800 (中国标准时间)"
  1. Регулярные выражения (класс RegExp)

toString(): возвращает литерал регулярного выражения

var re = /cat/g;
re.toString();// "/cat/g"
RegExp.prototype.toString.call(re);// "/cat/g"
  1. Базовый тип оболочки (класс Boolean/Number/String)

ECMAScript предоставляет три специальных ссылочных типа: Boolean, Number и String, которые имеют особое поведение, соответствующее их соответствующим базовым типам.

Возьмите тип String в качестве примера, чтобы кратко сказать

var str = 'wangpf';
str.toString();//"wangpf"

Есть сомнения по поводу приведенного выше кода.Во-первых, я определяю базовый тип строковой переменной str, которая не является объектом, но почему я могу вызывать метод toString() и откуда берется метод toString()?

Давайте сначала посмотрим на разницу между str и strObject:

var str = 'I am a string';
typeof str; //"string"
str instanceof String; //false

var strObject = new String('I am a string');
typeof strObject; //"object"
strObject instanceof String; //true
strObject instanceof Object; //true

Оказывается, из-за существования базового типа упаковки String механизм JS при необходимости преобразует строковый литерал в объект String, чтобы можно было выполнить операцию доступа к свойствам и методам Конкретный процесс выглядит следующим образом. :

(1)创建一个String类型的实例;
(2)在实例上调用指定的方法;
(3)销毁这个实例。

Итак, процесс вызова метода toString() выглядит так:

var strObject = new String('wangpf');
strObject.toString(); //'wangpf'
strObject = null;

Обратите внимание, что приведенный выше код автоматически выполняется движком JS, вы не можете получить доступ к объекту strObject, он существует только в момент выполнения кода, а затем сразу уничтожается, поэтому мы больше не можем добавлять свойства и методы к базовому типу во время выполнения, если он не вызывается напрямую через новое отображение. Основные типы оболочки создают объекты, но мы не рекомендуем это делать! ! !

  1. Строковая строка (класс String)

toString(): возвращает копию строки

var str = "a";
str.toString(); //"a"
String.prototype.toString.call(str); //"a"
  1. Числовой номер (класс чисел)

toString(): возвращает числовое значение в виде строки.

var num = 520;
num.toString(); //"520"
Number.prototype.toString.call(num); //"520"
  1. boolean boolean (логический класс)

toString(): возвращает строку «true» или «false».

var boo = true;
boo.toString(); //"true"
Boolean.prototype.toString.call(boo); //"true"
  1. нулевой и неопределенный

null и undefined не имеют соответствующих конструкторов, поэтому они не имеют и не могут вызывать метод toString(), а значит, не могут получить доступ ни к каким свойствам и методам, только к базовым типам.

  1. Окно глобального объекта (класс Window)

Глобальный объект Global можно сказать, что является наиболее специальным объектом в ECMAScript, который не существует сама, а в качестве окончательного «нагрузочного», все свойства и методы, которые не являются другими объектами, в конечном итоге его свойства и методы.

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

toString(): возвращает строковое представление объекта.

window.toString();
<!--"[object Window]"-->
Window.prototype.toString.call(window);//这里其实有问题
<!--"[object Window]"-->

После проверки класс Winodw не переопределяет метод toString() объекта-прототипа Window.prototype, он будет искать и вызывать Object.prototype.toString() в цепочке прототипов.

Следовательно, любой объектный объект может вызвать метод Window.prototype.toString() через эту привязку, то есть вызвать метод Object.prototype.toString(), и результат будет таким же, как у класса Object.

Таким образом, приведенный выше код по существу

Object.prototype.toString.call(window);
<!--"[object Window]"-->

Наконец, поговорим о прямом выполнении метода toString().

Выполните toString() напрямую, вывод будет следующим

toString();
<!--"[object Undefined]"-->

(function(){
    console.log(toString());
})();
<!--[object Undefined]-->

То есть прямой вызов метода toString() эквивалентен

Object.prototype.toString.call();
<!--"[object Undefined]"-->
Object.prototype.toString.call(undefined);
<!--"[object Undefined]"-->

Таким образом, прямой вызов toString() должен быть замаскированным методом undefined.toString().

Обратите внимание, что прямой вызов метода toString() нельзя понимать как вызов метода toString() в глобальной области видимости, а именно window.toString();

Кроме того, поговорим о методе toString.call/apply(this)

toString.call({});
<!--"[object Object]"-->
toString.call([]);
<!--"[object Array]"-->

Люди часто используют toString.call/apply(this) вместо Object.prototype.toString.call/apply(this) Я думаю, что это неточно и может легко привести к некоторым проблемам, как показано ниже.

function toString(){
    console.log("wangpf")
}
toString();//"wangpf"
toString.call({});//"wangpf"
toString.call([]);//"wangpf"

Мы можем обнаружить, что когда мы настраиваем метод toString() и вызываем метод toString() напрямую, метод toString() класса Object больше не будет вызываться по умолчанию, но будет использоваться наш пользовательский метод, который может не результат, который мы хотим, поэтому мы должны попытаться использовать Object.prototype.toString.call/apply(this).


расширять

Подобно методу toString(), различные подтипы Object также переписывают методы toLocaleString(), valueOf() и др. Здесь я хочу сказать, что независимо от того, как подтип объекта переопределяет метод, если мы понимаем, откуда берутся эти методы, как вызывать, можно хорошо понять результаты вызовов этих методов!

В конце концов, понимание объектов и прототипов в JS очень и очень важно!


Ссылаться на

  • Расширенное программирование на JavaScript (третье издание)
  • JavaScript, которого вы не знаете (Увеличение громкости)