Поговорим о принципе реализации typeof instanceof

JavaScript
Поговорим о принципе реализации typeof instanceof

предисловие

Недавно я оглядывался назад на основы js, и я просто что-то не понял в суждении о типах данных, или я не понял принципов typeof и instanceof, поэтому я был готов это изучить🤭

Если дело касается прототипов, вы можете прочитать эту статью 🤭

Говоря о прототипах JavaScript

тип данных

Последний стандарт ECMAScript определяет 8 типов данных:

Может быть, все относительно незнакомы с примитивным типом данных BigInt, его предложение решает некоторые проблемы, такие как больше, чем253 - 1целое число. Первоначально это было доступно в JavascriptNumberМаксимальное число для представления.BigIntМожет представлять произвольно крупные целые числа.

Разобравшись с типом данных, давайте посмотрим, как определить тип данных.


Определить тип данных

typeof

typeofОператор возвращает строку, представляющую тип неоцененного операнда.

Подводя итог возможным возвращаемым значениям:

  • "undefined"
  • "object"
  • "boolean"
  • "number"
  • "bigint"
  • "string"
  • "symbol"
  • "function"

Дополнительная информация:

typeof null === 'object';

Можно сказать, что это ошибка в дизайне JavaScript. Спецификация MDN объясняет это так:

В исходной реализации JavaScript значение в JavaScript было представлено тегом, представляющим тип и фактическое значение данных. Метка типа объекта равна 0. из-заnullПредставитель — это пустой указатель (большинство платформ — 0x00), поэтому тег типа NULL равен 0,typeof nullтакже вернуться"object".

Когда typeof оценивает данные типа объекта, он не может точно сказать нам, какой это тип Object, а при оценке null он также предоставляет указанную выше дополнительную информацию. Чтобы определить, какой это объект, нам нужно использовать оператор instanceof, о котором мы поговорим позже.

Давайте поговорим о принципе typeof, говоря о котором, мы должны рассмотреть, как JavaScript хранит данные, или, для переменной, каков стандарт взвешивания ее типов данных?

После ознакомления с соответствующей информацией, на самом деле, это ошибка, оставшаяся от истории.В начальной версии javascript использовалась 32-битная система, ради производительности информация о типе переменной хранится в младших битах:

  • 000: Объект
  • 010: число с плавающей запятой
  • 100: Строка
  • 110: логическое значение
  • 1: целое число

но дляundefinedа такжеnullДругими словами, хранение информации этих двух значений немного особенное.

null: Указатель NULL, соответствующий машинному коду, обычно все нули.

undefined: представлено целыми числами −2^30!

так,typeofв сужденииnullПроблема возникает, когдаnullВсе машинные коды равны 0, поэтому они напрямую рассматриваются как объекты.

Так что, кажется, немного разобрался с мехом (●'◡'●)


instanceof

instanceof операторДля обнаружения конструктораprototypeПоявляется ли свойство в цепочке прототипов экземпляра объекта.

грамматика

object instanceof constructor
object 某个实例对象
construtor 某个构造函数
// 定义构造函数
function C(){} 
function D(){} 

var o = new C();


o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype


o instanceof D; // false,因为 D.prototype 不在 o 的原型链上

o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object // true,同上

C.prototype = {};
var o2 = new C();

o2 instanceof C; // true

o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上.

D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上

Обратите внимание, что если выражениеobj instanceof Fooвернутьtrue, это не означает, что выражение всегда будет возвращатьtrue,потому чтоFoo.prototypeЗначение атрибута может измениться, и измененное значение может не существовать вobjна цепочке прототипов , то значение исходного выражения становитсяfalse. В другом случае изменится и значение исходного выражения, то есть изменение объектаobjВ случае с цепочкой прототипов, хотя в текущей спецификации ES мы можем только прочитать прототип объекта и не можем его изменить, с помощью нестандартных__proto__Достижимы псевдосвойства. такие как выполнениеobj.__proto__ = {}Позже,obj instanceof Fooвернусьfalse.

Принципиальный анализ

Чтобы понять принцип instanceof, нам нужно понять его с двух сторон:

  • как операторы определены в спецификации языка
  • Механизм прототипного наследования JavaScript

Здесь я напрямую перевожу определение спецификации в код JavaScript следующим образом:

function my_instance_of(leftVaule, rightVaule) {
    if(typeof leftVaule !== 'object' || leftVaule === null) return false;
    let rightProto = rightVaule.prototype,
        leftProto = leftVaule.__proto__;
    while (true) {
        if (leftProto === null) {
            return false;
        }
        if (leftProto === rightProto) {
            return true;
        }
        leftProto = leftProto.__proto__
    }
}

Как видно из приведенного выше кода, основной принцип instanceof таков:

Пока прототип справа и слева может прототип цепь, то есть вернуть истину. Следовательно, InstanceOF во время запроса будет проходить переменную цепи прототипов слева, пока вы не найдете правильные прототипы переменных, если это не удается, он возвращает false, сообщите нам переменную слева, не является переменным переменными.

Далее рассмотрим интересные примеры:

function Foo() {}
        console.log(Object instanceof Object)
        console.log(Function instanceof Function)
        console.log(Function instanceof Object)
        console.log(Foo instanceof Object)
        console.log(Foo instanceof Function)
        console.log(Foo instanceof Foo)

Принцип прототипного наследования в JavaScript

Что касается принципа прототипного наследования, я просто использую картинку, чтобы представить

Эта диаграмма очень важна.Для тех, кто не понимает цепочку прототипов, вы можете прочитать эту статью:

Говоря о прототипах JavaScript

Возьмите пример для анализа интересного экземпляра примера

Object instanceof Object

由图可知,Object 的 prototype 属性是 Object.prototype, 而由于 Object 本身是一个函数,由 Function 所创建,所以 Object.__proto__ 的值是 Function.prototype,而 Function.prototype 的 __proto__ 属性是 Object.prototype,所以我们可以判断出,Object instanceof Object 的结果是 true 。用代码简单的表示一下
leftValue = Object.__proto__ = Function.prototype;
rightValue = Object.prototype;
// 第一次判断
leftValue != rightValue
leftValue = Function.prototype.__proto__ = Object.prototype
// 第二次判断
leftValue === rightValue
// 返回 true

Остальные интересные примеры, такие как Function instanceof Object, можно реализовать вручную 🤭


Object.prototype.toString

Описание в спецификации ES5

Известно, что Object.prototype.toString в конечном итоге вернет строку в форме [объект, класс], а класс относится к обнаруженному типу данных, который является ключом к нашему суждению о типе данных.

var toString=Object.prototype.toString;

console.log(toString.call(und));  // [object Undefined]
console.log(toString.call(nul));  // [object Null]
console.log(toString.call(boo));  // [object Boolean]
console.log(toString.call(num));  // [object Number]
console.log(toString.call(str));  // [object String]
console.log(toString.call(obj));  // [object Object]
console.log(toString.call(arr));  // [object Array]
console.log(toString.call(fun));  // [object Function]
console.log(toString.call(date));  // [object Date]
console.log(toString.call(reg));  // [object RegExp]
console.log(toString.call(err));  // [object Error]
console.log(toString.call(arg));  // [object Arguments]

Окончательный метод определения типа данных

/**
 * @desc 数据类型检测
 * @param obj 待检测的数据
 * @return {String} 类型字符串
 */
 let type = (obj) => typeof obj !== 'object' ? typeof obj : Object.prototype.toString.call(obj).slice(8,-1).toLowerCase();

Индивидуальное определение типов данных

/**
 * @desc 是否是 Undefined 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isUndefined = obj => obj === void 0
/**
 * @desc 是否是 Null 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isNull = obj => obj === Null
/**
 * @desc 是否是 Boolean 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isBoolean = obj => typeof(obj) === 'boolean'
/**
 * @desc 是否是 Number 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isNumber = obj => typeof(obj) === 'number'
/**
 * @desc 是否是 String 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isString = obj => typeof(obj) === 'string'
/**
 * @desc 是否是 Object 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isObject = obj => Object.prototype.toString.call(obj) === '[object Object]'
/**
 * @desc 是否是 Array 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isArray = obj => Object.prototype.toString.call(obj) === '[object Array]'
/**
 * @desc 是否是 Function 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isFunction = obj => typeof obj === 'function'
/**
 * @desc 是否是 Date 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isDate = obj => Object.prototype.toString.call(obj) === '[object Date]'
/**
 * @desc 是否是 RegExp 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isRegExp = obj => Object.prototype.toString.call(obj) === '[object RegExp]'
/**
 * @desc 是否是 Error 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isError = obj => Object.prototype.toString.call(obj) === '[object Error]'
/**
 * @desc 是否是 Arguments 类型检测
 * @param obj 待检测的数据
 * @return {Boolean} 布尔值
 */
let isArguments = obj => Object.prototype.toString.call(obj) === '[object Arguments]'

В заключение

  • Можно использовать typeof для оценки базового типа данных.Следует отметить, что проблема, когда typeof оценивает нулевой тип
  • При оценке объекта рассмотрите возможность использования instanceof, но когда instanceof оценивает массив, он может быть оценен как объект с помощью instanceof
  • Чтобы точно определить тип экземпляра объекта, используйте метод Object.prototype.toString.call().

Ссылаться на

Как движок v8 узнает тип данных js?

Принцип typeof?

Подробное объяснение typeof в JavaScript

Идеальное решение для определения типа данных JavaScript