[JS Advanced] Вы действительно освоили переменные и типы?

внешний интерфейс JavaScript
[JS Advanced] Вы действительно освоили переменные и типы?

Управляемое чтение

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

  • JavaScriptКакова конкретная форма хранения переменных в памяти?
  • 0.1+0.2почему не равно0.3?Какова конкретная причина ошибки десятичного вычисления?
  • Symbolхарактеристики и каковы реальные сценарии применения?
  • [] == ![],[undefined] == falseпочему равноtrue• Когда в коде происходят неявные преобразования типов? Каковы правила конвертации?
  • Как точно определить тип переменной?

Если вы не можете хорошо ответить на вышеперечисленные вопросы, значит, вы не до конца усвоили эту часть знаний, то, пожалуйста, внимательно прочитайте следующие статьи.

В этой статье подробно рассказывается от основного принципа до практического применения.JavaScriptЗнание переменных и типов в .

1. Типы данных JavaScript

Стандарт ECMAScriptпредусмотренный7тип данных, который обрабатывает это7Существует два типа типов данных: примитивные типы и объектные типы.

примитивный тип

  • Null: содержит только одно значение:null
  • Undefined: содержит только одно значение:undefined
  • Boolean: содержит два значения:trueиfalse
  • Number: целое число или число с плавающей запятой, а также некоторые специальные значения (-Infinity,+Infinity,NaN)
  • String: последовательность символов, представляющая текстовое значение
  • Symbol: Экземпляр — это уникальный и неизменяемый тип данных.

(существуетes10Добавлен седьмой примитивный тип.BigInt, сейчас обновленоChromeслужба поддержки)

тип объекта

  • Object: Не так уж сложно отнести себя к одной категории, за исключением часто используемых.Object,Array,Functionспециальные объекты

Во-вторых, зачем различать примитивные типы и типы объектов?

2.1 Неизменность

Примитивные типы, упомянутые выше, вECMAScriptстандарт, они определяются какprimitive values, то есть исходное значение, а это значит, что само значение изменить нельзя.

Возьмем строку в качестве примера, когда мы вызываем метод, который манипулирует строкой, нет метода, который может напрямую изменить строку:

var str = 'ConardLi';
str.slice(1);
str.substr(1);
str.trim(1);
str.toLowerCase(1);
str[0] = 1;
console.log(str);  // ConardLi

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

Итак, когда мы переходим к вызову следующего кода:

str += '6'
console.log(str);  // ConardLi6

ты найдешь,strЗначение строки было изменено, не нарушает ли это неизменность строки? На самом деле это не так, мы это по памяти понимаем:

существуетJavaScript, каждой переменной требуется место в памяти для ее хранения.

Пространство памяти делится на два типа: память стека и память кучи.

стек памяти:

  • Сохраняемые значения имеют фиксированный размер
  • меньше места
  • Сохраненными переменными можно управлять напрямую, а эффективность работы высока.
  • Место для хранения выделяется системой автоматически

JavaScriptЗначение примитивного типа in хранится непосредственно в стеке, и стек выделяет для него место в памяти при определении переменной.

Поскольку размер пространства памяти в стеке фиксирован, переменные, хранящиеся в стеке, должны быть неизменяемыми.

В приведенном выше коде мы выполняемstr += '6''ConardLi6', то переменнаяstrуказывает на это пространство, так что это не нарушает不可变性的Функции.

2.2 Ссылочные типы

куча памяти:

  • Сохраненное значение является неопределенным и может быть динамически изменено
  • Большое пространство, низкая эффективность работы
  • С его внутренней памятью нельзя напрямую манипулировать, читать по ссылочному адресу.
  • Выделение места через код

По сравнению с приведенными выше неизменяемыми примитивными типами, я привык называть объекты ссылочными типами.Значение ссылочного типа фактически хранится в куче памяти.В стеке хранится только адрес фиксированной длины, который указывает на адрес в память кучи.значение.

var obj1 = {name:"ConardLi"}
var obj2 = {age:18}
var obj3 = function(){...}
var obj4 = [1,2,3,4,5,6,7,8,9]

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

Конечно, ссылочные типы больше не имеют不可变性, мы можем легко изменить их:

obj1.name = "ConardLi6";
obj2.age = 19;
obj4.length = 0;
console.log(obj1); //{name:"ConardLi6"}
console.log(obj2); // {age:19}
console.log(obj4); // []

Возьмите массив в качестве примера, многие из его методов могут видоизменяться.

  • pop()Удалить последний элемент массива, если массив пуст, массив не будет изменен, вернуть undefined, изменить исходный массив и вернуть удаленный элемент
  • push()Добавьте один или несколько элементов в конец массива, измените исходный массив и верните длину нового массива.
  • shift()Удалить первый элемент массива, если массив пуст, ничего не делать, вернуть undefined, изменить исходный массив и вернуть значение первого элемента
  • unshift()Добавляет один или несколько элементов в начало массива, изменяет исходный массив и возвращает длину нового массива.
  • reverse()Обратный порядок элементов в массиве, изменение исходного массива и возврат массива
  • sort()Отсортируйте элементы массива, измените исходный массив и верните массив
  • splice()Добавить/удалить элементы из массива, изменить исходный массив, вернуть удаленный элемент

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

2.3 Копировать

Когда мы копируем значение одной переменной в другую переменную, примитивные типы и ссылочные типы ведут себя по-разному.Давайте сначала рассмотрим примитивные типы:

var name = 'ConardLi';
var name2 = name;
name2 = 'code秘密花园';
console.log(name); // ConardLi;

В памяти есть переменнаяname, значениеConardLi. Начнем с переменнойnameскопировать переменнуюname2, в памяти создается новый блок пространства для храненияConardLi, хотя значения двух одинаковы, пространство памяти, на которое они указывают, совершенно различно, и две переменные участвуют в любой операции, не влияя друг на друга.

Скопируйте ссылочный тип:

var obj = {name:'ConardLi'};
var obj2 = obj;
obj2.name = 'code秘密花园';
console.log(obj.name); // code秘密花园

Когда мы копируем переменную ссылочного типа, мы фактически копируем адрес, хранящийся в стеке, поэтому скопированныйobj2на самом деле иobjуказывает на тот же объект в куче. Итак, мы меняем значение любой из переменных, другая переменная будет затронута, поэтому есть глубокие и мелкие копии.

2.4 Сравнение

Когда мы сравниваем две переменные, разные типы переменных ведут себя по-разному:

var name = 'ConardLi';
var name2 = 'ConardLi';
console.log(name === name2); // true
var obj = {name:'ConardLi'};
var obj2 = {name:'ConardLi'};
console.log(obj === obj2); // false

Для примитивных типов сравнивать их значения напрямую, и если значения равны, возвращатьtrue.

Для ссылочных типов при сравнении сравниваются их ссылочные адреса.Хотя объекты, хранящиеся в куче двух переменных, имеют одинаковые значения атрибутов, они хранятся в разных областях памяти, поэтому значение сравненияfalse.

2.5 Передача по значению и передача по ссылке

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

let name = 'ConardLi';
function changeValue(name){
  name = 'code秘密花园';
}
changeValue(name);
console.log(name);

Выполните приведенный выше код, если окончательный выводnameда'ConardLi', изменений нет, что указывает на то, что параметр функции передает значение переменной, то есть значение передается. Если окончательный отпечаток'code秘密花园', операция внутри функции может изменять входящую переменную, то это означает, что параметр функции передается по ссылке, то есть передается по ссылке.

Очевидно, что результатом вышеописанного выполнения является'ConardLi', то есть параметр функции — это только локальная переменная, копируемая входящей переменной, и изменение этой локальной переменной не повлияет на внешнюю переменную.

let obj = {name:'ConardLi'};
function changeValue(obj){
  obj.name = 'code秘密花园';
}
changeValue(obj);
console.log(obj.name); // code秘密花园

Приведенный выше код может заставить вас задуматься, является ли параметр ссылочным типом или передается по ссылке?

Прежде всего, давайте проясним,ECMAScriptВсе аргументы функции передаются по значению.

Аналогично, когда параметр функции является ссылочным типом, мы также копируем копию параметра в локальную переменную, но скопированная копия указывает только на адрес в куче памяти.Мы оперируем свойствами объекта внутри функции, на самом деле то же значение, что и внешняя переменная, указывает на память в куче, но это не значит, что ссылка передана, возьмем другой пример:

let obj = {};
function changeValue(obj){
  obj.name = 'ConardLi';
  obj = {name:'code秘密花园'};
}
changeValue(obj);
console.log(obj.name); // ConardLi

Видно, что параметры функции не являются переданными переменными.引用, а копия копии переменной, которая является самим значением, если переменная является примитивным типом, и адресом в куче памяти, если переменная является ссылочным типом. Итак, вспомните еще раз:

ECMAScriptВсе аргументы функции передаются по значению.

3. Неразличимый null и undefined

В примитивных типах есть два типаNullиUndefined, все они имеют одно и только одно значение,nullиundefined, и оба они ничего и ничего не представляют, я их вообще различаю так:

null

Представляет назначенный объект, преднамеренно назначая объект какnull, что намеренно пустое и не должно иметь ценности.

Таким образом, свойство объектаnullнормально,nullПри преобразовании в число значение равно0.

undefined

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

Если значение свойства объектаundefined, что ненормально, напримерobj.name=undefined, мы не должны это писать, мы должны прямоdelete obj.name.

undefinedПри преобразовании в числовое значениеNaN(специальное значение для нечислового значения)

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

Четыре, менее знакомые типы символов

SymbolтипES6Добавлен новый примитивный тип.

Каждое значение символа, возвращаемое функцией Symbol(), уникально. Значение символа может быть использовано в качестве идентификатора свойства объекта, это единственная цель этого типа данных.

Давайте взглянемSymbolКакими свойствами обладает тип.

4.1 Особенности символа

1. Уникальный

Использовать напрямуюSymbol()создать новоеsymbolПеременная, опционально строка для описания. Когда параметр является объектом, объектtoString()метод.

var sym1 = Symbol();  // Symbol() 
var sym2 = Symbol('ConardLi');  // Symbol(ConardLi)
var sym3 = Symbol('ConardLi');  // Symbol(ConardLi)
var sym4 = Symbol({name:'ConardLi'}); // Symbol([object Object])
console.log(sym2 === sym3);  // false

Создаем два с двумя одинаковыми строкамиSymbolпеременные, они не равны, видно, что каждаяSymbolПеременные уникальны.

Если мы хотим создать два равныхSymbolпеременная, вы можете использоватьSymbol.for(key).

Ищет существующий символ с заданным ключом и возвращает символ, если он найден. В противном случае в глобальном реестре символов будет создан новый символ с заданным ключом.

var sym1 = Symbol.for('ConardLi');
var sym2 = Symbol.for('ConardLi');
console.log(sym1 === sym2); // true

2. Примитивные типы

Обратите внимание, что использованиеSymbol()создание функцииsymbolпеременные, вместо использования конструктора используйтеnewОператор сообщит об ошибке напрямую.

new Symbol(); // Uncaught TypeError: Symbol is not a constructor

мы можем использоватьtypeofоператор для определенияSymbolтип:

typeof Symbol() === 'symbol'
typeof Symbol('ConardLi') === 'symbol'

3. Не перечислить

когда используешьSymbolПри использовании в качестве атрибута объекта можно гарантировать, что объект не будет иметь тот же атрибут имени, вызовитеfor...inЕго нельзя перечислить, и другой вызовObject.getOwnPropertyNames、Object.keys()тоже не могу получитьSymbolАтрибуты.

Object.getOwnPropertySymbols() можно вызывать для получения конкретных свойств Symbol.

var obj = {
  name:'ConardLi',
  [Symbol('name2')]:'code秘密花园'
}
Object.getOwnPropertyNames(obj); // ["name"]
Object.keys(obj); // ["name"]
for (var i in obj) {
   console.log(i); // name
}
Object.getOwnPropertySymbols(obj) // [Symbol(name)]

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

Вот несколькоSymbolСценарий применения в программе.

Приложение 1: предотвращение XSS

существуетReactизReactElementобъект, есть?typeofсобственность, этоSymbolПеременные типа:

var REACT_ELEMENT_TYPE =
  (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
  0xeac7;

ReactElement.isValidElementФункция используется для определения допустимости компонента React Ниже приведена его конкретная реализация.

ReactElement.isValidElement = function (object) {
  return typeof object === 'object' && object !== null && object.?typeof === REACT_ELEMENT_TYPE;
};

видимыйReactбудет отображаться без?typeofИдентификация и компоненты, не прошедшие проверку правил, отфильтровываются.

Если на вашем сервере есть уязвимость, которая позволяет пользователям хранить произвольныеJSONобъект, а клиентский код ожидает строку, что может стать проблемой:

// JSON
let expectedTextButGotJSON = {
  type: 'div',
  props: {
    dangerouslySetInnerHTML: {
      __html: '/* put your exploit here */'
    },
  },
};
let message = { text: expectedTextButGotJSON };
<p>
  {message.text}
</p>

иJSONнельзя хранить вSymbolпеременная типа, вот что мешаетXSSзапястье.

Приложение 2: Частная собственность

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

const privateField = Symbol();
class myClass {
  constructor(){
    this[privateField] = 'ConardLi';
  }
  getField(){
    return this[privateField];
  }
  setField(val){
    this[privateField] = val;
  }
}

Применение 3: Предотвратить загрязнение имущества

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

Например, в следующем сценарии мы моделируем и реализуемcallметод:

    Function.prototype.myCall = function (context) {
      if (typeof this !== 'function') {
        return undefined; // 用于防止 Function.prototype.myCall() 直接调用
      }
      context = context || window;
      const fn = Symbol();
      context[fn] = this;
      const args = [...arguments].slice(1);
      const result = context[fn](...args);
      delete context[fn];
      return result;
    }

Нам нужно временно вызвать метод для объекта, не вызывая загрязнения свойств,Symbolхороший выбор.

Пять, нечестный числовой тип

зачем говоритьNumberТипа не честный.Полагаю, что каждый сталкивался с проблемой неточного десятичного исчисления в разработке, типа0.1+0.2!==0.3, давайте вернемся к первоисточнику, чтобы понять, почему происходит это явление и как его избежать.

Ниже приведена простая функция, которую я реализовал для определения точности сложения двух десятичных знаков:

    function judgeFloat(n, m) {
      const binaryN = n.toString(2);
      const binaryM = m.toString(2);
      console.log(`${n}的二进制是    ${binaryN}`);
      console.log(`${m}的二进制是    ${binaryM}`);
      const MN = m + n;
      const accuracyMN = (m * 100 + n * 100) / 100;
      const binaryMN = MN.toString(2);
      const accuracyBinaryMN = accuracyMN.toString(2);
      console.log(`${n}+${m}的二进制是${binaryMN}`);
      console.log(`${accuracyMN}的二进制是    ${accuracyBinaryMN}`);
      console.log(`${n}+${m}的二进制再转成十进制是${to10(binaryMN)}`);
      console.log(`${accuracyMN}的二进制是再转成十进制是${to10(accuracyBinaryMN)}`);
      console.log(`${n}+${m}在js中计算是${(to10(binaryMN) === to10(accuracyBinaryMN)) ? '' : '不'}准确的`);
    }
    function to10(n) {
      const pre = (n.split('.')[0] - 0).toString(2);
      const arr = n.split('.')[1].split('');
      let i = 0;
      let result = 0;
      while (i < arr.length) {
        result += arr[i] * Math.pow(2, -(i + 1));
        i++;
      }
      return result;
    }
    judgeFloat(0.1, 0.2);
    judgeFloat(0.6, 0.7);

image

5.1 Потеря точности

Все данные в компьютере二进制хранятся, поэтому при вычислениях компьютер должен сначала преобразовать данные в二进制выполнить расчет, а затем преобразовать результат расчета в十进制.

Нетрудно увидеть из вышеприведенного кода, что при вычислении0.1+0.2час,二进制При расчете произошла потеря точности, что привело к обратному преобразованию в十进制Результаты не соответствовали ожидаемым результатам.

5.2 Анализ результатов — дополнительные вопросы

0.1и0.2Двоичное число 1100 — это бесконечный цикл десятичных знаков. Давайте посмотрим на результаты, вычисленные JS, чтобы помочь нам один за другим:

0,1 в двоичном формате:

0.0001100110011001100110011001100110011001100110011001101

0,2 двоичный:

0.001100110011001100110011001100110011001100110011001101

Теоретически добавление приведенных выше результатов должно::

0.0100110011001100110011001100110011001100110011001100111

Двоичный код 0,1+0,2, рассчитанный фактическим JS

0.0100110011001100110011001100110011001100110011001101

Смотрите здесь, у вас могут возникнуть дополнительные вопросы:

Почему двоичный код 0,1, рассчитанный js, имеет столько бит, а не больше? ? ?

Почему двоичный результат (0,1 + 0,2), рассчитанный js, отличается от двоичного результата (0,1 + 0,2), рассчитанного нами? ? ?

Почему 0,1 в двоичном формате + 0,2 в двоичном формате! = 0,3 в двоичном формате? ? ?

5.3 Как js хранит двоичные десятичные числа

десятичный二进制Большинство из них представляют собой бесконечные циклы,JavaScriptКак их хранить?

существуетСпецификация языка ECMAScript®можно увидеть в,ECMAScriptсерединаNumberТип следуетIEEE 754стандарт. Используйте 64-битную фиксированную длину для представления.

На самом деле существует множество языков с числовыми типами, которые следуют этому стандарту, напримерJAVA, поэтому многие языки также имеют ту же проблему, что и выше.

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

Если вы заинтересованы, вы можете проверить этот сайт0.30000000000000004.com/, да, вы правильно прочитали, то есть0.30000000000000004.com/! ! !

5.4 IEEE 754

IEEE754Стандарт содержит двоичное представление набора действительных чисел. Он состоит из трех частей:

  • знаковый бит

  • бит экспоненты

  • мантисса

Цифры каждой части числа с плавающей запятой трех прецизионностей следующие:

image

JavaScriptиспользует 64-битное кодирование с плавающей запятой двойной точности, поэтому его符号位Занимать1биты, биты экспоненты занимают11биты, биты мантиссы занимают52немного.

Ниже мы разберемся, что такое符号位,指数位,尾数位,от0.1Например:

Его двоичный файл:0.0001100110011001100...

В целях экономии места для хранения он выражается в экспоненциальном представлении в компьютере, то есть

1.100110011001100... X 2-4

Если это нелегко понять, подумайте о десятичных числах:

1100Научная запись11 X 102

так:

image

符号位является знаком положительного и отрицательного,1выражать,0выражать;

指数位Сохраните экспоненту в научном представлении;

尾数位Сохраняйте значащие цифры после экспоненциальной записи;

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

5.5 toString(2) в js

Поскольку биты мантиссы могут храниться только52число, которое объясняетtoString(2)Результат выполнения:

Если на компьютере нет ограничений по объему памяти, то0.1из二进制должно быть:

0.00011001100110011001100110011001100110011001100110011001...

научная запись мантисса

1.1001100110011001100110011001100110011001100110011001...

Однако из-за ограничений значительное количество53цифры и далее не сохраняются, из этого следует, что если1просто иди вперед1,если0принцип отказа.

53-й бит в научной двоичной записи для 0,1 равен 1, поэтому мы имеем следующий результат:

0.0001100110011001100110011001100110011001100110011001101

0.2У него такая же проблема, собственно именно из-за такого хранения здесь и происходит потеря точности, в результате чего0.1+0.2!=0.3.

На самом деле расчетов с одинаковой проблемой точности много, и мы не можем их все записать, поэтому, когда в программе есть численные расчеты, нам лучше использовать библиотеки инструментов, которые помогут нам решить их. рекомендуемые библиотеки с открытым исходным кодом:

5.6 Наибольшее число, которое может представить JavaScript

по иIEEE 754Ограничения 64-битной спецификации двойной точности:

指数位Самое большое число, которое можно представить:1023(десятичный)

尾数位Наибольшее число, которое можно выразить, — это мантисса.1Случай

Таким образом, наибольшее число, которое может представлять JavaScript, — это бит

1.111...X 21023Этот результат, преобразованный в десятичный вид, равен1.7976931348623157e+308, результатNumber.MAX_VALUE.

5.7 Максимальный номер сейфа

JavaScriptNumber.MAX_SAFE_INTEGERПредставляет наибольшее безопасное число, а результат вычисления9007199254740991, то есть потери точности (кроме десятичных знаков) в этом диапазоне чисел не будет, это число фактически1.111...X 252.

Мы также можем использовать некоторые библиотеки с открытым исходным кодом для работы с большими целыми числами:

Собственно, этот вопрос чиновник тоже рассматривал.bigIntвведитеes10вырос в и сейчасChromeуже есть, используйтеbigIntМожет работать с числами, которые превышают максимальное безопасное число.

6. Какие еще типы ссылок существуют?

существуетECMAScript, ссылочный тип — это структура данных, используемая для совместной организации данных и функций.

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

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

  • Arrayмножество
  • DateДата
  • RegExpОбычный
  • Functionфункция

6.1 Тип упаковки

Для облегчения манипулирования примитивными значениями,ECMAScriptТакже предоставляется несколько специальных ссылочных типов, которые являются типами-оболочками для примитивных типов:

  • Boolean
  • Number
  • String

Обратите внимание на разницу между типами-оболочками и примитивными типами:

true === new Boolean(true); // false
123 === new Number(123); // false
'ConardLi' === new String('ConardLi'); // false
console.log(typeof new String('ConardLi')); // object
console.log(typeof 'ConardLi'); // string

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

var name = 'ConardLi'
name.color = 'red';
console.log(name.color); // undefined

6.2 Упаковка и распаковка

  • Преобразование упаковки: преобразование базового типа в соответствующий тип упаковки

  • Распаковка: Преобразование ссылочного типа в примитивный тип

Поскольку примитивные типы не могут расширять свойства и методы, как нам вызывать методы, использующие примитивные типы?

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

var name = "ConardLi";
var name2 = name.substring(2);

Фактически происходят следующие процессы:

  • СоздаватьStringэкземпляр типа оболочки
  • вызов по инстанцииsubstringметод
  • уничтожить экземпляр

То есть мы используем базовый тип для вызова метода, а операции упаковки и распаковки будут выполняться автоматически.NumberиBooleanЭтот процесс также происходит при типе.

Последует преобразование ссылочного типа в примитивный, то есть процесс распаковки.ECMAScript规范УказанныйtoPrimitiveВ принципе, ссылочный тип обычно называетсяvalueOfиtoStringметод, вы также можете напрямую переопределитьtoPeimitiveметод. Вообще принципы преобразования в разные типы значений разные, например:

  • преобразование ссылочного типа вNumberнапиши сначала позвониvalueOf, затем позвонитеtoString
  • преобразование ссылочного типа вStringнапиши сначала позвониtoString, затем позвонитеvalueOf

какvalueOfиtoStringне существует или не возвращает примитивный тип, выбросьтеTypeErrorаномальный.

const obj = {
  valueOf: () => { console.log('valueOf'); return 123; },
  toString: () => { console.log('toString'); return 'ConardLi'; },
};
console.log(obj - 1);   // valueOf   122
console.log(`${obj}ConardLi`); // toString  ConardLiConardLi

const obj2 = {
  [Symbol.toPrimitive]: () => { console.log('toPrimitive'); return 123; },
};
console.log(obj2 - 1);   // valueOf   122

const obj3 = {
  valueOf: () => { console.log('valueOf'); return {}; },
  toString: () => { console.log('toString'); return {}; },
};
console.log(obj3 - 1);  
// valueOf  
// toString
// TypeError

Помимо автоматической распаковки и автоматической упаковки в программе, мы также можем выполнять операции распаковки и упаковки вручную. Мы можем напрямую вызвать тип оболочкиvalueOfилиtoString, чтобы реализовать операцию распаковки:

var num =new Number("123");  
console.log( typeof num.valueOf() ); //number
console.log( typeof num.toString() ); //string

Семь, преобразование типов

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

Существует два типа преобразования типов: неявное преобразование — это преобразование типа, автоматически выполняемое программой, а обязательное преобразование — это преобразование типа, которое мы выполняем вручную.

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

7.1 Правила преобразования типов

Если происходит неявное преобразование, то различные типы преобразуются друг в друга в соответствии со следующими правилами:

7.2 Операторы if и логические операторы

существуетifВ операторах и логических операторах, если есть только одна переменная, переменная сначала преобразуется вBooleanзначение, только следующие случаи будут преобразованы вfalse, остальные превращаются вtrue:

null
undefined
''
NaN
0
false

7.3 Различные арифметические операторы

Мы имеем дело с различными неNumberТипы используют математические операторы (- * /), Затем на-Numberпреобразование типа вNumberтип;

1 - true // 0
1 - null //  1
1 * undefined //  NaN
2 * ['5'] //  10

Уведомление+является исключением, выполнить+оператор:

  • 1. Когда одна сторонаStringтип, распознается как конкатенация строк и предпочтительно преобразует другую сторону в строковый тип.
  • 2. Когда одна сторонаNumberтип, другая сторона является примитивным типом, затем примитивный тип преобразуется вNumberтип.
  • 3. Когда одна сторонаNumberтип, другая сторона - ссылочный тип, ссылочный тип иNumberКонкатенация после преобразования типа в строку.
123 + '123' // 123123   (规则1)
123 + null  // 123    (规则2)
123 + true // 124    (规则2)
123 + {}  // 123[object Object]    (规则3)

7.4 ==

использовать==, если обе стороны одного типа, результат сравнения и===то же самое, иначе произойдет неявное преобразование, используйте==Происходящее преобразование можно разделить на несколько разных случаев (только две стороны считаются разными типами):

  • 1.NaN

NaNСравнение с любым другим типом всегда возвращаетfalse(включая и себя).

NaN == NaN // false
  • 2.Boolean

Booleanпо сравнению с любым другим типом,Booleanсначала преобразуется вNumberтип.

true == 1  // true 
true == '2'  // false
true == ['1']  // true
true == ['2']  // false

Обратите внимание на момент, который может сбить с толку:undefined、nullиBooleanсравни, однакоundefined、nullиfalseлегко представить себе как ложные значения, но результат их сравненияfalse, Причина в том, чтоfalseсначала преобразуется в0:

undefined == false // false
null == false // false
  • 3. Строка и число

StringиNumberсравните сначалаStringпреобразовать вNumberтип.

123 == '123' // true
'' == 0 // true
  • 4.нулевой и неопределенный

null == undefinedРезультат сравненияtrue,Кроме,null、undefinedСравнивается с любым другим результатом, какfalse.

null == undefined // true
null == '' // false
null == 0 // false
null == false // false
undefined == '' // false
undefined == 0 // false
undefined == false // false
  • 5. Примитивные типы и ссылочные типы

При сравнении примитивных типов и ссылочных типов тип объектаToPrimitiveПравила преобразования в примитивные типы:

  '[object Object]' == {} // true
  '1,2,3' == [1, 2, 3] // true

Взгляните на сравнение ниже:

[] == ![] // true

!приоритет выше, чем==,![]сначала будет преобразовано вfalse, а затем по второму пункту выше,falseпреобразовать вNumberтип0, левый[]преобразовать в0, обе стороны равны.

[null] == false // true
[undefined] == false // true

по массивуToPrimitiveправило, элементы массиваnullилиundefined, элемент рассматривается как пустая строка, поэтому[null]、[undefined]будет преобразован в0.

Итак, сказав так много, рекомендуется использовать===Чтобы проверить, равны ли два значения...

7.5 Интересный вопрос интервью

Классический вопрос на собеседовании, как сделать:a == 1 && a == 2 && a == 3.

В соответствии с приведенным выше преобразованием распаковки и==Неявное преобразование , мы можем легко написать ответ:

const a = {
   value:[3,2,1],
   valueOf: function () {return this.value.pop(); },
} 

Восемь, способ определить тип данных JavaScript

8.1 typeof

Применимая сцена

typeofОператор может точно определить, относится ли переменная к следующим примитивным типам:

typeof 'ConardLi'  // string
typeof 123  // number
typeof true  // boolean
typeof Symbol()  // symbol
typeof undefined  // undefined

Вы также можете использовать его для определения типов функций:

typeof function(){}  // function

Неприменимые сценарии

когда вы используетеtypeofКажется, немного устал судить об эталонном типе:

typeof [] // object
typeof {} // object
typeof new Date() // object
typeof /^\d*$/; // object

Все ссылочные типы, кроме функций, оцениваются какobject.

Кроме тогоtypeof null === 'object'Это также может вызвать головную боль, которая находится вJavaScriptПервое издание переданоbug, поскольку модификация вызовет большое количество проблем с совместимостью, она не исправлена...

8.2 instanceof

instanceofОператоры могут помочь нам определить, к какому типу объекта относится ссылочный тип:

[] instanceof Array // true
new Date() instanceof Date // true
new RegExp() instanceof RegExp // true

Давайте сначала рассмотрим несколько правил цепочки прототипов:

  • 1. Все ссылочные типы имеют объектные характеристики, то есть атрибуты можно свободно расширять
  • 2. Все ссылочные типы имеют__proto__(Неявный прототип) свойство, которое является простым объектом
  • 3. Все функции естьprototypeСвойство (явный прототип), также обычный объект
  • 4. Все типы ссылок__proto__value указывает на его конструкторprototype
  • 5. При попытке получить свойства объекта, если сама переменная не имеет этого свойства, она перейдет к его__proto__найти в

[] instanceof Arrayна самом деле судитьArray.prototypeВы в[]в цепочке прототипов.

Итак, используйтеinstanceofдля определения типа данных это будет не очень точно, это не является первоначальным замыслом его дизайна:

[] instanceof Object // true
function(){}  instanceof Object // true

Кроме того, используйтеinstanceofтакже не может обнаруживать примитивные типы данных, поэтомуinstanceofНе очень хороший выбор.

8.3 toString

Выше мы упоминали об операции распаковкиtoStringфункцию, которую мы можем вызывать для выполнения преобразований из ссылочных типов.

Каждый ссылочный тип имеетtoStringметод по умолчанию,toString()метод каждогоObjectНаследование объекта. Если этот метод не переопределен в пользовательском объекте,toString()возвращение"[object type]"typeявляется типом объекта.

const obj = {};
obj.toString() // [object Object]

Обратите внимание, что упомянутое выше如果此方法在自定义对象中未被覆盖,toStringпозволит достичь желаемого эффекта.На самом деле, большинство ссылочных типов, таких какArray、Date、RegExpЭто все переписаноtoStringметод.

Мы можем напрямую позвонитьObjectРаскрыто на прототипеtoString()метод, используяcallизменитьthisТочка для достижения эффекта, который мы хотим.

8.4 jquery

ПосмотримjqueryКак судить о типе в исходном коде:

var class2type = {};
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
function( i, name ) {
	class2type[ "[object " + name + "]" ] = name.toLowerCase();
} );

type: function( obj ) {
	if ( obj == null ) {
		return obj + "";
	}
	return typeof obj === "object" || typeof obj === "function" ?
		class2type[Object.prototype.toString.call(obj) ] || "object" :
		typeof obj;
}

isFunction: function( obj ) {
		return jQuery.type(obj) === "function";
}

Примитивные типы используются напрямуюtypeof, ссылочный тип используетObject.prototype.toString.callполучить тип, сclass2typeОбъект отфильтровывает избыточный код строки, такой как[object function]получитеarray, а затем в суждении последнего типа, напримерisFunctionможно использовать напрямуюjQuery.type(obj) === "function"такое суждение.

Ссылаться на

резюме

Я надеюсь, что вы сможете достичь следующих результатов после прочтения этой статьи:

  • учитьсяJavaScriptКонкретная форма хранения переменных в памяти может соответствовать реальной сцене
  • Понять основные причины неточных десятичных вычислений
  • Понимать сценарии, в которых могут происходить неявные преобразования типов, и принципы преобразования.
  • общее суждениеJavaScriptСпособ и основные принципы типов данных

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

Если вы хотите читать больше качественных статей, вы можете подписаться на меняgithubблог, твоя звезда✨, лайки и внимание - движущая сила моего постоянного творчества!

Рекомендую обратить внимание на мой паблик WeChat [code secret garden], каждый день выкладывать качественные статьи, будем общаться и расти вместе.

Мы являемся научно-исследовательской командой ByteDance Interactive Entertainment, включая Douyin Short Video, Douyin Volcano, TikTok, Faceu, Qingyan, Jianying и т. д. По состоянию на январь 2020 года Douyin Daily Active (DAU) превысил 400 миллионов человек и продолжает поддерживать быстрый рост. . Вы будете поддерживать разработку продуктов и связанные с ними архитектурные работы, где каждая строка кода может повлиять на сотни миллионов пользователей.

Код набора в школу 2021:DRZUM5Z

Официальный сайт доставки:job.toutiao.com/s/JR8SthH