[Интервью] Разговор о типах данных в JavaScript

внешний интерфейс JavaScript
[Интервью] Разговор о типах данных в JavaScript

предисловие

пожалуйста, скажите мнеJavaScriptтип данных в ?

Предполагается, что в предварительном интервью всем задавали этот вопрос.

отвечать:JavascriptТипы данных включают примитивные типы и ссылочные типы. где примитивные типы включаютnull,undefined,boolean,string,symbol,bigInt,number. ссылочный тип относится кObject.

Да, я ответил так же, но это обычно первый вопрос, и из этого вопроса может быть поднято много-много вопросов, таких как

  • Nullа такжеUndefinedКакая разница? На что следует обратить внимание в пустых суждениях о фронтенде?
  • typeof nullЗачемobject?
  • ЗачемES6предложитьSymbol?
  • BigIntВ чем проблема?
  • Зачем0.1 + 0.2 !== 0.3?Как решить эту проблему?
  • Как определить значение - это массив?
  • ...

слабо типизированный язык

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

var foo = 42;  // foo is a Number now
foo = "bar";  // foo is a String now
foo = true;   // foo is a Boolean now

Хотя эта функция приносит нам удобство, она также приносит нам много ошибок типов. Представьте, еслиJSГоворят, что это строго типизированный язык, тогда нет преобразования между различными типами, и есть слой разделения или уровень защиты.Будет ли легче поддерживать? - это может бытьTypeScriptПричина рождения.

правильноJavaScriptОсвоение типов данных является самым базовым знанием внешнего интерфейса.

нулевой или неопределенный

определение

undefinedПредставляет неопределенную переменную.nullЗначение представляет нулевой указатель объекта.

Возвращаясь к первоисточнику: В началеJavaScriptдизайнерBrendan EichНа самом деле он просто определяетnull,nullкак будтоJavaрассматривается как объект. Но потому чтоJavaScriptСуществует два типа данных: примитивные типы данных и ссылочные типы данных.Brendan EichЯ думаю, что значения, которые представляют «нет», лучше не являются объектами.

такJavascriptДизайнnull — это объект, представляющий «ничего», который равен 0 при преобразовании в числовое значение; undefined — это примитивное значение, представляющее «ничего», которое равно NaN при преобразовании в числовое значение.

Number(null)
// 0

5 + null
// 5

Number(undefined)
// NaN

5 + undefined
// NaN

Разница и применение Null и Undefined

null означает "нет объекта", т.е. там не должно быть никакого значения., типичное использование выглядит следующим образом

  1. В качестве аргумента функции это означает, что аргумент функции не является объектом.
  2. как конец цепочки прототипов объектов.
Object.getPrototypeOf(Object.prototype)
// null

undefined означает "отсутствующее значение", т.е. здесь должно быть значение, но оно не определено. Типичное использование:

  1. Когда переменная объявлена, но ей не присвоено значение, она равнаundefined.
  2. При вызове функции не был указан параметр, который должен быть указан, и параметр равенundefined.
  3. Объект не имеет назначенного свойства, значение свойства равноundefined.
  4. Когда функция не имеет возвращаемого значения, она возвращается по умолчанию.undefined.
var i;
i // undefined

function f(x){console.log(x)}
f() // undefined

var  o = new Object();
o.p // undefined

var x = f();
x // undefined

На что следует обратить внимание при оценке пустого?

javaScriptПять видов нулевых и ложных значений, соответственно undefined, null, false, "", 0, NAN

Иногда это может легко привести к некоторым проблемам, таким как

let a = 0;
console.log(a || '/'); // 本意是只要 a 为 null 或者 Undefined 的时候,输出 '/',但实际上只要是我们以上的五种之一就输出 '/'

Конечно, мы можем написать

let a = 0;
if (a === null || a === undefined) {
  console.log('/');
} else {
  console.log(a);
}

всегда не очень элегантен, поэтому спецификация ES предлагает оператор объединения null (??)

Оператор объединения null (??) — это логический оператор, который возвращает правый операнд, когда левый операнд имеет значение null или не определен, в противном случае возвращает левый операнд.

Приведенный выше пример можно записать так:

let a = 0;
console.log(a??'/'); // 0

typeof null — ошибки JS

typeof null // "object"

JavaScriptЗначение в представлено меткой, представляющей тип и фактическое значение данных. первое изданиеJavaScriptЗначение хранится в 32 битах, а тип идентифицируется младшими 1 или 3 битами значения, а тег типа объекта равен 000. следующим образом

  • 1: Целое (целое)
  • 000: Тип ссылки (объект)
  • 010: тип с плавающей запятой двойной точности (двойной)
  • 100: строка (строка)
  • 110: логическое значение (логическое значение)

Но есть два особых значения:

  • undefined, используйте целое число -2^30 (отрицательное 2 в 30-й степени, не в диапазоне целых чисел)
  • null, нулевой указатель машинного кода (определение макроса C/C++), младшие три цифры также равны 000

из-заnullПредставляет нулевой указатель (младшие три бита также000 ),因此,nullТег типа000,typeof nullСледовательно, возвращается «объект».

ЭтоJavaScriptОшибка дизайна, но ее нельзя изменить, ведь если вы ее модифицируете, это повлияет на существующий код.

Номер - 0,1 + 0,2! == 0,3

Феномен

существуетJavaScriptБудет явление похожее на следующее

0.1 + 0.2 
0.30000000000000004

причина

В процессе работы с числами с плавающей запятой нам необходимо преобразовать десятичное число в двоичное. Правила преобразования десятичного числа в двоичное следующие:

Умножьте число после запятой на 2, возьмите целую часть результата (либо 1, либо 0), затем умножьте дробную часть на 2, затем возьмите целую часть результата... и так далее до дробной части часть 0 или бит Число достаточно, и все в порядке. Затем расположите взятые целые части по порядку

В соответствии с приведенными выше правилами окончательное представление 0,1 выглядит следующим образом:

0.000110011001100110011(0011无限循环)……

Таким образом, потеря точности — это не языковая проблема, а неотъемлемый недостаток хранения чисел с плавающей запятой.

JavaScriptда64Число двойной точности с плавающей запятой хранит всеNumberзначение типа, согласноIEEE754Спецификация, двоичное число 0,1 сохраняет только 52 значащие цифры, то есть

1.100110011001100110011001100110011001100110011001101 * 2^(-4)

Точно так же двоичное число 0,2 равно

1.100110011001100110011001100110011001100110011001101 * 2^(-3)

Таким образом, была потеряна точность при преобразовании между основаниями. Операция выглядит следующим образом

0.00011001100110011001100110011001100110011001100110011010
+0.00110011001100110011001100110011001100110011001100110100
------------------------------------------------------------
=0.01001100110011001100110011001100110011001100110011001110

Таким образом, это приводит к 0,1 + 0,2 !== 0,3 в конечном результате расчета.

Как решить

  • Преобразование чисел в целые числа
function add(num1, num2) {
 const num1Digits = (num1.toString().split('.')[1] || '').length;
 const num2Digits = (num2.toString().split('.')[1] || '').length;
 const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
 return (num1 * baseNum + num2 * baseNum) / baseNum;
}
  • библиотека классов

NPMСуществует много поддержки наJavaScriptа такжеNode.jsматематические библиотеки, такие какmath.js,decimal.js,D.jsтак далее

  • ES6

ES6существуетNumberК объекту добавлена ​​очень маленькая константа -Number.EPSILON

Number.EPSILON
// 2.220446049250313e-16
Number.EPSILON.toFixed(20)
// "0.00000000000000022204"

Цель введения такой малой суммы — установить диапазон ошибок для вычислений с плавающей запятой, если ошибка может быть меньшеNumber.EPSILON, можно считать результаты достоверными.

function withinErrorMargin (left, right) {
    return Math.abs(left - right) < Number.EPSILON
}
withinErrorMargin(0.1+0.2, 0.3)

Будущие решения -TC39 Decimal proposal

в настоящее время вStage 1предложение. упоминается позжеBigIntрасширенныйJSПоложительная граница , превышает задачу 2^53 безопасных целых чисел.DecimalЭто решить десятичную проблему JS -2 ^ 53. Это предложение вводит новые примитивные типы в JS:decimal(суффикс m), объявляет, что число является десятичной операцией.

let zero_point_three = 0.1m + 0.2m;
assert(zero_point_three === 0.3m);
// 提案中的例子
function calculateBill(items, tax) {
  let total = 0m;
  for (let {price, count} of items) {
    total += price * BigDecimal(count);
  }
  return BigDecimal.round(total * (1m + tax), {maximumFractionDigits: 2, round: "up"});
}

let items = [{price: 1.25m, count: 5}, {price: 5m, count: 1}];
let tax = .0735m;
console.log(calculateBill(items, tax));

Расширение - хранение чисел с плавающей запятой в памяти

Так как же выглядит итоговое число с плавающей запятой в памяти?EEE754Дано определение представления чисел с плавающей запятой

(-1)^S * M * 2^E

Значение каждого символа следующее: S — бит знака, который определяет, является ли он положительным или отрицательным, когда он равен 0, это положительное число, а когда он равен 1, это отрицательное число. M относится к эффективному количеству цифр, больше 1 и меньше 2. E, является битом экспоненты.

Javascript — это 64-битное число двойной точности с плавающей запятой, старший 1 бит — это бит знака S, следующие 11 бит — показатель степени E, а оставшиеся 52 бита — мантиссы M.

доступны с помощьюэтот инструмент визуализацииСм. двоичное представление чисел с плавающей запятой в памяти)

BigInt — преодолевая самые большие ограничения

JavaScriptизNumberТипДвойная точность IEEE 754 64-битная с плавающей запятойТипы. Максимальное значение в JavaScript равно2^53.

BigIntЧисловые типы произвольной точности вошли в спецификацию stage3.BigIntМожет представлять произвольно большие целые числа. создатьBigInt, нам просто нужно добавить суффикс n к любому целочисленному литералу. Например, напишите 123 как 123n. Этот глобальный BigInt(число) можно использовать для преобразования числа в BigInt, что означает, что BigInt(123) === 123n. Теперь позвольте мне использовать эти два пункта для решения проблемы, о которой мы упоминали ранее:

Символ - я самый красивый мальчик

определение

ES6 представляет новый примитивный тип данныхSymbol, представляющий уникальное значение

let s = Symbol();

typeof s
// "symbol"

Сценарии применения

  • Определите набор констант, чтобы убедиться, что наборы констант не равны. Избавьтесь от волшебных строк

  • Объекты гарантированно имеют разные имена свойств

let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
  • Vueсерединаprovideа такжеinject.provideа такжеinjectКомпоненту-предку может быть разрешено внедрять зависимость во всех своих потомков, независимо от того, насколько глубока иерархия компонентов, и это всегда будет действовать до тех пор, пока устанавливаются отношения восходящего и нисходящего потоков. Но эта инвазивность также очень сильна, используяSymbolsтак какkeyЭто может избежать вмешательства в код компонента, и не будет проблем, таких как одинаковые имена.

Массив - особое существование в объекте

Подскажите, пожалуйста, как судить о Array?

Зачем задавать этот вопрос?

Поскольку массив — это особое существование, это одна из структур данных, с которыми мы обычно чаще всего соприкасаемся, это особый объект, а его индекс — «обычный объект».keyстоимость. Но у него также есть некоторые методы, которых нет у «обычных объектов», напримерmapЖдать

typeofдаjavascriptВстроенный оператор для определения типа данных, который возвращает строку, представляющую тип данных параметра. но мы не можем пройтиtypeofОпределить, является ли массив. потому чтоtypeofмассивы и простые объекты иnull, оба возвращают "объект"

const a = null;
const b = {};
const c= [];
console.log(typeof(a)); //Object
console.log(typeof(b)); //Object
console.log(typeof(c)); //Object

Как оценить массив

  • Object.prototype.toString.call().

каждое наследствоObjectобъекты имеютtoStringметод, еслиtoStringЕсли метод не переопределен, он вернет[Object type]typeтип объекта

const a = ['Hello','Howard'];
const b = {0:'Hello',1:'Howard'};
const c = 'Hello Howard';
Object.prototype.toString.call(a);//"[object Array]"
Object.prototype.toString.call(b);//"[object Object]"
Object.prototype.toString.call(c);//"[object String]"
  • Array.isArray()
const a = [];
const b = {};
Array.isArray(a);//true
Array.isArray(b);//false

Array.isArray()даES5Добавлен метод, когда он не существуетArray.isArray(),Можно использоватьObject.prototype.toString.call()выполнить

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}
  • instanceof.instanceofОператоры могут использоваться для определения значения конструктора.prototypeСуществует ли объект, на который указывает свойство, в цепочке прототипов другого объекта, подлежащего тестированию. Поскольку конструктор массиваArray, поэтому можно судить по следующему.Примечание. Поскольку массивы также являются объектами, поэтомуa instanceof Objectтакже дляtrue
const a = [];
const b = {};
console.log(a instanceof Array);//true
console.log(a instanceof Object);//true,在数组的原型链上也能找到Object构造函数
console.log(b instanceof Array);//false
  • constructor. Экземпляр, созданный с помощью конструктора, имеетconstructorАтрибуты.
function B() {};
let b = new B();
console.log(b.constructor === B) // true

И массив состоит из вызываемогоArrayреализована функция. так что вы можете

let c = [];
console.log(c.constructor === Array) // true

Примечание: конструктор может быть изменен. Поэтому не рекомендуется судить

let c = [];
c.constructor = Object;
console.log(c.constructor === Array); // false

В заключение

Согласно приведенному выше описанию, метод суждения о личной рекомендации имеет следующие приоритеты.

isArray > Object.prototype.toString.call() > instanceof > constructor

Суммировать

Эта статья направлена ​​наJavaScriptОбсуждаются и анализируются некоторые распространенные проблемы с типами данных. Я надеюсь, что это может быть полезно для всех на собеседованиях или работе в обычное время. Кроме того, вещи, которые могут быть не упомянуты, например, преобразование типов, имеют возможность обсудить еще раз.

Наконец, приглашаю всех обратить внимание на мою официальную учетную запись - интерфейсный продуктовый магазин, обсудить технические вопросы~

Ссылаться на